Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2106,6 +2106,8 @@ bool AppInitMain()
strLoadError = _("Corrupted block database detected");
break;
}

ResetBlockFailureFlags(nullptr);
}
} catch (const std::exception& e) {
LogPrintf("%s\n", e.what());
Expand Down
10 changes: 10 additions & 0 deletions src/validation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
47 changes: 40 additions & 7 deletions test/functional/feature_llmq_chainlocks.py
Original file line number Diff line number Diff line change
Expand Up @@ -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])
Expand All @@ -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")
Expand Down