diff --git a/src/init.cpp b/src/init.cpp index 3ec84089ab01..a9161183c8ed 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -2106,6 +2106,8 @@ bool AppInitMain() strLoadError = _("Corrupted block database detected"); break; } + + ResetBlockFailureFlags(nullptr); } } catch (const std::exception& e) { LogPrintf("%s\n", e.what()); diff --git a/src/validation.cpp b/src/validation.cpp index 7485d73f9a76..65eaa5c96d50 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -3377,6 +3377,16 @@ bool MarkConflictingBlock(CValidationState& state, const CChainParams& chainpara bool CChainState::ResetBlockFailureFlags(CBlockIndex *pindex) { AssertLockHeld(cs_main); + if (!pindex) { + if (pindexBestInvalid && pindexBestInvalid->GetAncestor(chainActive.Height()) == chainActive.Tip()) { + LogPrintf("%s: the best known invalid block (%s) is ahead of our tip, reconsidering\n", + __func__, pindexBestInvalid->GetBlockHash().ToString()); + pindex = pindexBestInvalid; + } else { + return true; + } + } + int nHeight = pindex->nHeight; // Remove the invalidity flag from this block and all its descendants. diff --git a/test/functional/feature_llmq_chainlocks.py b/test/functional/feature_llmq_chainlocks.py index d1b197720046..27c3488c1f85 100755 --- a/test/functional/feature_llmq_chainlocks.py +++ b/test/functional/feature_llmq_chainlocks.py @@ -91,21 +91,52 @@ def run_test(self): self.stop_node(0) self.start_node(0) connect_nodes(self.nodes[0], 1) - self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash()) + assert(self.nodes[0].getbestblockhash() == good_tip) + self.nodes[0].invalidateblock(good_tip) self.log.info("Now try to reorg the chain") self.nodes[0].generate(2) time.sleep(6) assert(self.nodes[1].getbestblockhash() == good_tip) - self.nodes[0].generate(2) + bad_tip = self.nodes[0].generate(2)[-1] time.sleep(6) + assert(self.nodes[0].getbestblockhash() == bad_tip) assert(self.nodes[1].getbestblockhash() == good_tip) self.log.info("Now let the node which is on the wrong chain reorg back to the locked chain") self.nodes[0].reconsiderblock(good_tip) assert(self.nodes[0].getbestblockhash() != good_tip) - self.nodes[1].generatetoaddress(1, node0_mining_addr) - self.wait_for_chainlocked_block(self.nodes[0], self.nodes[1].getbestblockhash()) - assert(self.nodes[0].getbestblockhash() == self.nodes[1].getbestblockhash()) + good_tip = self.nodes[1].generatetoaddress(1, node0_mining_addr)[-1] + self.wait_for_chainlocked_block(self.nodes[0], good_tip) + assert(self.nodes[0].getbestblockhash() == good_tip) + + self.log.info("Should not switch to the most work chain on restart despite having no idea about chainlocks messages anymore") + assert(int(self.nodes[0].getblock(bad_tip)["chainwork"], 16) > int(self.nodes[1].getblock(good_tip)["chainwork"], 16)) + self.stop_node(0) + self.start_node(0) + time.sleep(1) + assert(self.nodes[0].getbestblockhash() == good_tip) + bad_tip = self.nodes[0].generate(3)[-1] + good_fork = self.nodes[1].generatetoaddress(1, node0_mining_addr)[-1] + self.wait_for_chainlocked_block(self.nodes[1], good_fork) + connect_nodes(self.nodes[0], 1) + good_tip = self.nodes[1].generatetoaddress(1, node0_mining_addr)[-1] # this should mark bad_tip as invalid + self.wait_for_chainlocked_block(self.nodes[0], good_tip) + assert(int(self.nodes[0].getblock(bad_tip)["chainwork"], 16) > int(self.nodes[1].getblock(good_tip)["chainwork"], 16)) + + # NOTE: this is an undesired behaviour + self.log.info("Should switch to the most work chain on second restart now because bad tip was marked invalid and not conflicting") + self.stop_node(0) + self.start_node(0) + self.nodes[0].invalidateblock(good_fork) + self.stop_node(0) + self.start_node(0) + time.sleep(1) + assert(self.nodes[0].getbestblockhash() == bad_tip) + self.log.info("Bring it back to the right chain") + self.nodes[0].reconsiderblock(good_tip) + connect_nodes(self.nodes[0], 1) + good_tip = self.nodes[1].generatetoaddress(1, node0_mining_addr)[-1] + self.wait_for_chainlocked_block(self.nodes[0], good_tip) self.log.info("Isolate a node and let it create some transactions which won't get IS locked") isolate_node(self.nodes[0]) @@ -114,12 +145,14 @@ def run_test(self): txs.append(self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 1)) txs += self.create_chained_txs(self.nodes[0], 1) self.log.info("Assert that after block generation these TXs are NOT included (as they are \"unsafe\")") - self.nodes[0].generate(1) + node0_tip = self.nodes[0].generate(1)[-1] for txid in txs: tx = self.nodes[0].getrawtransaction(txid, 1) assert("confirmations" not in tx) time.sleep(1) - assert(not self.nodes[0].getblock(self.nodes[0].getbestblockhash())["chainlock"]) + node0_tip_block = self.nodes[0].getblock(node0_tip) + assert(not node0_tip_block["chainlock"]) + assert(node0_tip_block["previousblockhash"] == good_tip) self.log.info("Disable LLMQ based InstantSend for a very short time (this never gets propagated to other nodes)") self.nodes[0].spork("SPORK_2_INSTANTSEND_ENABLED", 4070908800) self.log.info("Now the TXs should be included")