Page MenuHomePhabricator

D2004.diff
No OneTemporary

D2004.diff

diff --git a/src/validation.cpp b/src/validation.cpp
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -2397,12 +2397,58 @@
while (hasValidAncestor && pindexTest && pindexTest != pindexFork) {
assert(pindexTest->nChainTx || pindexTest->nHeight == 0);
+ // If this is a parked chain, but it has enough PoW, clear the park
+ // state.
+ bool fParkedChain = pindexTest->nStatus.isOnParkedChain();
+ if (fParkedChain && gArgs.GetBoolArg("-parkdeepreorg", true)) {
+ const CBlockIndex *pindexTip = chainActive.Tip();
+
+ // During initialization, pindexTip and/or pindexFork may be
+ // null. In this case, we just ignore the fact that the chain is
+ // parked.
+ if (!pindexTip || !pindexFork) {
+ UnparkBlock(pindexTest);
+ continue;
+ }
+
+ // A parked chain can be unparked if it has twice as much PoW
+ // accumulated as the main chain has since the fork block.
+ CBlockIndex const *pindexExtraPow = pindexTip;
+ arith_uint256 requiredWork = pindexTip->nChainWork;
+ switch (pindexTip->nHeight - pindexFork->nHeight) {
+ // Limit the penality for depth 1, 2 and 3 to half a block
+ // worth of work to ensure we don't fork accidentaly.
+ case 3:
+ case 2:
+ pindexExtraPow = pindexExtraPow->pprev;
+ // FALLTHROUGH
+ case 1: {
+ const arith_uint256 deltaWork =
+ pindexExtraPow->nChainWork - pindexFork->nChainWork;
+ requiredWork += (deltaWork >> 1);
+ break;
+ }
+ default:
+ requiredWork +=
+ pindexExtraPow->nChainWork - pindexFork->nChainWork;
+ break;
+ }
+
+ if (pindexNew->nChainWork > requiredWork) {
+ // We have enough, clear the parked state.
+ LogPrintf("Unpark block %s as its chain has accumulated "
+ "enough PoW.\n",
+ pindexTest->GetBlockHash().ToString());
+ fParkedChain = false;
+ UnparkBlock(pindexTest);
+ }
+ }
+
// Pruned nodes may have entries in setBlockIndexCandidates for
// which block files have been deleted. Remove those as candidates
// for the most work chain if we come across them; we can't switch
// to a chain unless we have all the non-active-chain parent blocks.
bool fInvalidChain = pindexTest->nStatus.isInvalid();
- bool fParkedChain = pindexTest->nStatus.isOnParkedChain();
bool fMissingData = !pindexTest->nStatus.hasData();
if (!(fInvalidChain || fParkedChain || fMissingData)) {
// The current block is acceptable, move to the parent, up to
@@ -2721,6 +2767,10 @@
nBlockReverseSequenceId--;
}
+ // In case this was parked, unpark it.
+ UnparkBlock(pindex);
+
+ // Make sure it is added to the candidate list if apropriate.
if (pindex->IsValid(BlockValidity::TRANSACTIONS) && pindex->nChainTx) {
setBlockIndexCandidates.insert(pindex);
PruneBlockIndexCandidates();
@@ -3621,6 +3671,20 @@
block.GetHash().ToString());
}
+ // If this is a deep reorg (a regorg of more than one block), preemptively
+ // mark the chain as parked. If it has enough work, it'll unpark
+ // automatically. We mark the block as parked at the very last minute so we
+ // can make sure everything is ready to be reorged if needed.
+ if (gArgs.GetBoolArg("-parkdeepreorg", true)) {
+ const CBlockIndex *pindexFork = chainActive.FindFork(pindex);
+ if (pindexFork && pindexFork->nHeight + 1 < pindex->nHeight) {
+ LogPrintf("Park block %s as it would cause a deep reorg.\n",
+ pindex->GetBlockHash().ToString());
+ pindex->nStatus = pindex->nStatus.withParked();
+ setDirtyBlockIndex.insert(pindex);
+ }
+ }
+
// Header is valid/has work and the merkle tree is good.
// Relay now, but if it does not build on our best tip, let the
// SendMessages loop relay it.
diff --git a/test/functional/abc-parkedchain.py b/test/functional/abc-parkedchain.py
--- a/test/functional/abc-parkedchain.py
+++ b/test/functional/abc-parkedchain.py
@@ -6,12 +6,13 @@
import os
from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import assert_equal
+from test_framework.util import assert_equal, connect_nodes_bi, sync_blocks, wait_until
class ParkedChainTest(BitcoinTestFramework):
def set_test_params(self):
- self.num_nodes = 1
+ self.num_nodes = 2
+ self.extra_args = [["-noparkdeepreorg"], []]
# There should only be one chaintip, which is expected_tip
def only_valid_tip(self, expected_tip, other_tip_status=None):
@@ -36,6 +37,8 @@
# Let's park the chain.
assert(parked_tip != tip)
+ assert(block_to_park != tip)
+ assert(block_to_park != parked_tip)
node.parkblock(block_to_park)
assert_equal(node.getbestblockhash(), tip)
@@ -122,6 +125,49 @@
node.reconsiderblock(bad_tip)
self.only_valid_tip(good_tip)
+ # First, make sure both nodes are in sync.
+ parking_node = self.nodes[1]
+ connect_nodes_bi(self.nodes, 0, 1)
+ sync_blocks(self.nodes[0:2])
+
+ assert_equal(node.getbestblockhash(), parking_node.getbestblockhash())
+
+ # Wait for node 1 to park the chain.
+ def wait_for_parked_block(block):
+ def check_block():
+ for tip in parking_node.getchaintips():
+ if tip["hash"] == block:
+ assert(tip["status"] != "active")
+ return tip["status"] == "parked"
+ return False
+ wait_until(check_block)
+
+ def check_reorg_protection(depth, extra_blocks):
+ self.log.info("Test deep reorg parking, %d block deep" % depth)
+
+ # Invalidate the tip on node 0, so it doesn't follow node 1.
+ node.invalidateblock(node.getbestblockhash())
+ # Mine block to create a fork of proper depth
+ parking_node.generate(depth - 1)
+ node.generate(depth)
+ # extra block should now find themselves parked
+ for i in range(extra_blocks):
+ node.generate(1)
+ wait_for_parked_block(node.getbestblockhash())
+
+ # If we mine one more block, the node reorgs.
+ node.generate(1)
+ wait_until(lambda: parking_node.getbestblockhash()
+ == node.getbestblockhash())
+
+ check_reorg_protection(1, 0)
+ check_reorg_protection(2, 0)
+ check_reorg_protection(3, 1)
+ check_reorg_protection(4, 4)
+ check_reorg_protection(5, 5)
+ check_reorg_protection(6, 6)
+ check_reorg_protection(100, 100)
+
if __name__ == '__main__':
ParkedChainTest().main()
diff --git a/test/functional/bip68-sequence.py b/test/functional/bip68-sequence.py
--- a/test/functional/bip68-sequence.py
+++ b/test/functional/bip68-sequence.py
@@ -26,7 +26,7 @@
class BIP68Test(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 2
- self.extra_args = [["-blockprioritypercentage=0"],
+ self.extra_args = [["-blockprioritypercentage=0", "-noparkdeepreorg"],
["-blockprioritypercentage=0", "-acceptnonstdtxn=0"]]
def run_test(self):
diff --git a/test/functional/dbcrash.py b/test/functional/dbcrash.py
--- a/test/functional/dbcrash.py
+++ b/test/functional/dbcrash.py
@@ -53,7 +53,8 @@
# Set -rpcservertimeout=900 to reduce socket disconnects in this
# long-running test
self.base_args = ["-limitdescendantsize=0", "-maxmempool=0",
- "-rpcservertimeout=900", "-dbbatchsize=200000"]
+ "-rpcservertimeout=900", "-dbbatchsize=200000",
+ "-noparkdeepreorg"]
# Set different crash ratios and cache sizes. Note that not all of
# -dbcache goes to pcoinsTip.
diff --git a/test/functional/getchaintips.py b/test/functional/getchaintips.py
--- a/test/functional/getchaintips.py
+++ b/test/functional/getchaintips.py
@@ -14,6 +14,7 @@
class GetChainTipsTest (BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 4
+ self.extra_args = [["-noparkdeepreorg"], ["-noparkdeepreorg"], [], []]
def run_test(self):
tips = self.nodes[0].getchaintips()
diff --git a/test/functional/invalidateblock.py b/test/functional/invalidateblock.py
--- a/test/functional/invalidateblock.py
+++ b/test/functional/invalidateblock.py
@@ -15,6 +15,7 @@
def set_test_params(self):
self.setup_clean_chain = True
self.num_nodes = 3
+ self.extra_args = [["-noparkdeepreorg"], [], []]
def setup_network(self):
self.setup_nodes()
diff --git a/test/functional/listsinceblock.py b/test/functional/listsinceblock.py
--- a/test/functional/listsinceblock.py
+++ b/test/functional/listsinceblock.py
@@ -11,6 +11,7 @@
def set_test_params(self):
self.num_nodes = 4
self.setup_clean_chain = True
+ self.extra_args = [["-noparkdeepreorg"], ["-noparkdeepreorg"], [], []]
def run_test(self):
'''
diff --git a/test/functional/p2p-acceptblock.py b/test/functional/p2p-acceptblock.py
--- a/test/functional/p2p-acceptblock.py
+++ b/test/functional/p2p-acceptblock.py
@@ -76,7 +76,8 @@
def set_test_params(self):
self.setup_clean_chain = True
self.num_nodes = 3
- self.extra_args = [[], ["-whitelist=127.0.0.1"],
+ self.extra_args = [["-noparkdeepreorg"],
+ ["-noparkdeepreorg", "-whitelist=127.0.0.1"],
["-minimumchainwork=0x10"]]
def setup_network(self):
diff --git a/test/functional/p2p-fullblocktest.py b/test/functional/p2p-fullblocktest.py
--- a/test/functional/p2p-fullblocktest.py
+++ b/test/functional/p2p-fullblocktest.py
@@ -61,7 +61,7 @@
# Change the "outcome" variable from each TestInstance object to only do the comparison.
def set_test_params(self):
self.num_nodes = 1
- self.extra_args = [['-whitelist=127.0.0.1']]
+ self.extra_args = [['-whitelist=127.0.0.1', '-noparkdeepreorg']]
self.setup_clean_chain = True
self.block_heights = {}
self.coinbase_key = CECKey()
diff --git a/test/functional/preciousblock.py b/test/functional/preciousblock.py
--- a/test/functional/preciousblock.py
+++ b/test/functional/preciousblock.py
@@ -45,6 +45,8 @@
def set_test_params(self):
self.setup_clean_chain = True
self.num_nodes = 3
+ self.extra_args = [["-noparkdeepreorg"],
+ ["-noparkdeepreorg"], ["-noparkdeepreorg"]]
def setup_network(self):
self.setup_nodes()
diff --git a/test/functional/pruning.py b/test/functional/pruning.py
--- a/test/functional/pruning.py
+++ b/test/functional/pruning.py
@@ -37,16 +37,21 @@
# Create nodes 0 and 1 to mine.
# Create node 2 to test pruning.
- self.full_node_default_args = ["-maxreceivebuffer=20000", "-blockmaxsize=999000", "-checkblocks=5",
- "-limitdescendantcount=100", "-limitdescendantsize=5000", "-limitancestorcount=100", "-limitancestorsize=5000"]
+ self.full_node_default_args = ["-maxreceivebuffer=20000", "-blockmaxsize=999000",
+ "-checkblocks=5", "-noparkdeepreorg",
+ "-limitdescendantcount=100", "-limitdescendantsize=5000",
+ "-limitancestorcount=100", "-limitancestorsize=5000"]
# Create nodes 3 and 4 to test manual pruning (they will be re-started with manual pruning later)
# Create nodes 5 to test wallet in prune mode, but do not connect
self.extra_args = [self.full_node_default_args,
self.full_node_default_args,
- ["-maxreceivebuffer=20000", "-prune=550"],
- ["-maxreceivebuffer=20000", "-blockmaxsize=999000"],
- ["-maxreceivebuffer=20000", "-blockmaxsize=999000"],
- ["-prune=550"]]
+ ["-maxreceivebuffer=20000",
+ "-prune=550", "-noparkdeepreorg"],
+ ["-maxreceivebuffer=20000",
+ "-blockmaxsize=999000", "-noparkdeepreorg"],
+ ["-maxreceivebuffer=20000",
+ "-blockmaxsize=999000", "-noparkdeepreorg"],
+ ["-prune=550", "-noparkdeepreorg"]]
def setup_network(self):
self.setup_nodes()
@@ -146,7 +151,8 @@
# transactions (from disconnected blocks)
self.stop_node(1)
self.start_node(1, extra_args=[
- "-maxreceivebuffer=20000", "-blockmaxsize=5000", "-checkblocks=5", "-disablesafemode"])
+ "-maxreceivebuffer=20000", "-blockmaxsize=5000", "-checkblocks=5",
+ "-disablesafemode", "-noparkdeepreorg"])
height = self.nodes[1].getblockcount()
self.log.info("Current block height: %d" % height)
@@ -172,7 +178,8 @@
# Reboot node1 to clear those giant tx's from mempool
self.stop_node(1)
self.start_node(1, extra_args=[
- "-maxreceivebuffer=20000", "-blockmaxsize=5000", "-checkblocks=5", "-disablesafemode"])
+ "-maxreceivebuffer=20000", "-blockmaxsize=5000", "-checkblocks=5",
+ "-disablesafemode", "-noparkdeepreorg"])
self.log.info("Generating new longer chain of 300 more blocks")
self.nodes[1].generate(300)
@@ -364,7 +371,7 @@
# check that the pruning node's wallet is still in good shape
self.log.info("Stop and start pruning node to trigger wallet rescan")
self.stop_node(2)
- self.start_node(2, extra_args=["-prune=550"])
+ self.start_node(2, extra_args=["-prune=550", "-noparkdeepreorg"])
self.log.info("Success")
# check that wallet loads loads successfully when restarting a pruned node after IBD.
@@ -374,7 +381,7 @@
nds = [self.nodes[0], self.nodes[5]]
sync_blocks(nds, wait=5, timeout=300)
self.stop_node(5) # stop and start to trigger rescan
- self.start_node(5, extra_args=["-prune=550"])
+ self.start_node(5, extra_args=["-prune=550", "-noparkdeepreorg"])
self.log.info("Success")
def run_test(self):
diff --git a/test/functional/sendheaders.py b/test/functional/sendheaders.py
--- a/test/functional/sendheaders.py
+++ b/test/functional/sendheaders.py
@@ -182,6 +182,7 @@
def set_test_params(self):
self.setup_clean_chain = True
self.num_nodes = 2
+ self.extra_args = [["-noparkdeepreorg"], ["-noparkdeepreorg"]]
# mine count blocks and return the new tip
def mine_blocks(self, count):
diff --git a/test/functional/txn_clone.py b/test/functional/txn_clone.py
--- a/test/functional/txn_clone.py
+++ b/test/functional/txn_clone.py
@@ -11,6 +11,7 @@
class TxnMallTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 4
+ self.extra_args = [["-noparkdeepreorg"], ["-noparkdeepreorg"], [], []]
def add_options(self, parser):
parser.add_argument("--mineblock", dest="mine_block", default=False, action="store_true",
diff --git a/test/functional/txn_doublespend.py b/test/functional/txn_doublespend.py
--- a/test/functional/txn_doublespend.py
+++ b/test/functional/txn_doublespend.py
@@ -11,6 +11,7 @@
class TxnMallTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 4
+ self.extra_args = [["-noparkdeepreorg"], ["-noparkdeepreorg"], [], []]
def add_options(self, parser):
parser.add_argument("--mineblock", dest="mine_block", default=False, action="store_true",

File Metadata

Mime Type
text/plain
Expires
Sat, Mar 1, 11:44 (3 h, 39 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
5187684
Default Alt Text
D2004.diff (16 KB)

Event Timeline