diff --git a/doc/release-notes-6189.md b/doc/release-notes-6189.md new file mode 100644 index 0000000000000..b88a030d8258d --- /dev/null +++ b/doc/release-notes-6189.md @@ -0,0 +1,8 @@ +Tests +----- + +- For the `regtest` network the activation heights of several softforks were + changed. (dash#6189) + * BIP 34 (blockheight in coinbase) from 500 to 2 + * BIP 66 (DERSIG) from 1251 to 102 + * BIP 65 (CLTV) from 1351 to 111 diff --git a/src/chainparams.cpp b/src/chainparams.cpp index d946b2d84bd69..c67d349e542b8 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -794,10 +794,10 @@ class CRegTestParams : public CChainParams { consensus.nGovernanceMinQuorum = 1; consensus.nGovernanceFilterElements = 100; consensus.nMasternodeMinimumConfirmations = 1; - consensus.BIP34Height = 500; // BIP34 activated on regtest (Used in functional tests) + consensus.BIP34Height = 2; // BIP34 activated on regtest (Block at height 1 not enforced for testing purposes) consensus.BIP34Hash = uint256(); - consensus.BIP65Height = 1351; // BIP65 activated on regtest (Used in functional tests) - consensus.BIP66Height = 1251; // BIP66 activated on regtest (Used in functional tests) + consensus.BIP65Height = 111; // BIP65 activated on regtest (Block at height 110 and earlier not enforced for testing purposes) + consensus.BIP66Height = 102; // BIP66 activated on regtest (Block at height 101 and earlier not enforced for testing purposes) consensus.BIP147Height = 432; // BIP147 activated on regtest (Used in functional tests) consensus.CSVHeight = 432; // CSV activated on regtest (Used in rpc activation tests) consensus.DIP0001Height = 2000; diff --git a/src/consensus/params.h b/src/consensus/params.h index f025d8a54034c..e5e5d6ff9574f 100644 --- a/src/consensus/params.h +++ b/src/consensus/params.h @@ -14,8 +14,12 @@ namespace Consensus { -enum BuriedDeployment : int16_t -{ +/** + * A buried deployment is one where the height of the activation has been hardcoded into + * the client implementation long after the consensus change has activated. See BIP 90. + */ +enum BuriedDeployment : int16_t { + // buried deployments get negative values to avoid overlap with DeploymentPos DEPLOYMENT_HEIGHTINCB = std::numeric_limits::min(), DEPLOYMENT_DERSIG, DEPLOYMENT_CLTV, @@ -29,17 +33,16 @@ enum BuriedDeployment : int16_t DEPLOYMENT_BRR, DEPLOYMENT_V19, }; -constexpr bool ValidDeployment(BuriedDeployment dep) { return DEPLOYMENT_HEIGHTINCB <= dep && dep <= DEPLOYMENT_V19; } +constexpr bool ValidDeployment(BuriedDeployment dep) { return dep <= DEPLOYMENT_V19; } -enum DeploymentPos : uint16_t -{ +enum DeploymentPos : uint16_t { DEPLOYMENT_TESTDUMMY, DEPLOYMENT_V20, // Deployment of EHF, LLMQ Randomness Beacon DEPLOYMENT_MN_RR, // Deployment of Masternode Reward Location Reallocation // NOTE: Also add new deployments to VersionBitsDeploymentInfo in deploymentinfo.cpp MAX_VERSION_BITS_DEPLOYMENTS }; -constexpr bool ValidDeployment(DeploymentPos dep) { return DEPLOYMENT_TESTDUMMY <= dep && dep <= DEPLOYMENT_MN_RR; } +constexpr bool ValidDeployment(DeploymentPos dep) { return dep < MAX_VERSION_BITS_DEPLOYMENTS; } /** * Struct for each individual consensus rule change using BIP9. diff --git a/src/deploymentstatus.cpp b/src/deploymentstatus.cpp index 9007800421870..bba86639a3570 100644 --- a/src/deploymentstatus.cpp +++ b/src/deploymentstatus.cpp @@ -7,6 +7,8 @@ #include #include +#include + VersionBitsCache g_versionbitscache; /* Basic sanity checking for BuriedDeployment/DeploymentPos enums and @@ -15,3 +17,18 @@ VersionBitsCache g_versionbitscache; static_assert(ValidDeployment(Consensus::DEPLOYMENT_TESTDUMMY), "sanity check of DeploymentPos failed (TESTDUMMY not valid)"); static_assert(!ValidDeployment(Consensus::MAX_VERSION_BITS_DEPLOYMENTS), "sanity check of DeploymentPos failed (MAX value considered valid)"); static_assert(!ValidDeployment(static_cast(Consensus::DEPLOYMENT_TESTDUMMY)), "sanity check of BuriedDeployment failed (overlaps with DeploymentPos)"); + +/* ValidDeployment only checks upper bounds for ensuring validity. + * This checks that the lowest possible value or the type is also a + * (specific) valid deployment so that lower bounds don't need to be checked. + */ + +template +static constexpr bool is_minimum() +{ + using U = typename std::underlying_type::type; + return x == std::numeric_limits::min(); +} + +static_assert(is_minimum(), "heightincb is not minimum value for BuriedDeployment"); +static_assert(is_minimum(), "testdummy is not minimum value for DeploymentPos"); diff --git a/src/deploymentstatus.h b/src/deploymentstatus.h index 84ce46375ebfa..608e4bda06a31 100644 --- a/src/deploymentstatus.h +++ b/src/deploymentstatus.h @@ -49,7 +49,7 @@ inline bool DeploymentEnabled(const Consensus::Params& params, Consensus::Buried inline bool DeploymentEnabled(const Consensus::Params& params, Consensus::DeploymentPos dep) { assert(Consensus::ValidDeployment(dep)); - return params.vDeployments[dep].nTimeout != 0; + return params.vDeployments[dep].nStartTime != Consensus::BIP9Deployment::NEVER_ACTIVE; } /** this function is convenient helper for DIP0003 because 'active' and 'enforced' are different statuses for DIP0003 */ diff --git a/src/miner.cpp b/src/miner.cpp index c9d47ab9382f5..5e5269dda2e5c 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -64,9 +64,9 @@ BlockAssembler::Options::Options() } BlockAssembler::BlockAssembler(CChainState& chainstate, const NodeContext& node, const CTxMemPool& mempool, const CChainParams& params, const Options& options) : - m_blockman(Assert(node.chainman)->m_blockman), + m_blockman(chainstate.m_blockman), m_cpoolman(*Assert(node.cpoolman)), - m_chain_helper(*Assert(node.chain_helper)), + m_chain_helper(chainstate.ChainHelper()), m_chainstate(chainstate), m_dmnman(*Assert(node.dmnman)), m_evoDb(*Assert(node.evodb)), diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index e6b7a4949261a..657068d70b9cb 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1615,10 +1615,7 @@ static RPCHelpMan verifychain() static void SoftForkDescPushBack(const CBlockIndex* active_chain_tip, UniValue& softforks, const Consensus::Params& params, Consensus::BuriedDeployment dep) { // For buried deployments. - // A buried deployment is one where the height of the activation has been hardcoded into - // the client implementation long after the consensus change has activated. See BIP 90. - // Buried deployments with activation height value of - // std::numeric_limits::max() are disabled and thus hidden. + if (!DeploymentEnabled(params, dep)) return; UniValue rv(UniValue::VOBJ); @@ -1633,8 +1630,8 @@ static void SoftForkDescPushBack(const CBlockIndex* active_chain_tip, UniValue& static void SoftForkDescPushBack(const CBlockIndex* active_chain_tip, const std::unordered_map& signals, UniValue& softforks, const Consensus::Params& consensusParams, Consensus::DeploymentPos id) { // For BIP9 deployments. - // Deployments that are never active are hidden. - if (consensusParams.vDeployments[id].nStartTime == Consensus::BIP9Deployment::NEVER_ACTIVE) return; + + if (!DeploymentEnabled(consensusParams, id)) return; UniValue bip9(UniValue::VOBJ); const ThresholdState thresholdState = g_versionbitscache.State(active_chain_tip, consensusParams, id); diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp index 7ab9ed9eb1b87..72b723645e0af 100644 --- a/src/test/util/setup_common.cpp +++ b/src/test/util/setup_common.cpp @@ -428,10 +428,10 @@ CBlock TestChainSetup::CreateBlock( auto cbTx = GetTxPayload(*block.vtx[0]); BOOST_ASSERT(cbTx.has_value()); BlockValidationState state; - if (!CalcCbTxMerkleRootMNList(block, m_node.chainman->ActiveChain().Tip(), cbTx->merkleRootMNList, *m_node.dmnman, state, m_node.chainman->ActiveChainstate().CoinsTip())) { + if (!CalcCbTxMerkleRootMNList(block, chainstate.m_chain.Tip(), cbTx->merkleRootMNList, *m_node.dmnman, state, chainstate.CoinsTip())) { BOOST_ASSERT(false); } - if (!CalcCbTxMerkleRootQuorums(block, m_node.chainman->ActiveChain().Tip(), *m_node.llmq_ctx->quorum_block_processor, cbTx->merkleRootQuorums, state)) { + if (!CalcCbTxMerkleRootQuorums(block, chainstate.m_chain.Tip(), *m_node.llmq_ctx->quorum_block_processor, cbTx->merkleRootQuorums, state)) { BOOST_ASSERT(false); } CMutableTransaction tmpTx{*block.vtx[0]}; @@ -443,7 +443,7 @@ CBlock TestChainSetup::CreateBlock( { LOCK(cs_main); unsigned int extraNonce = 0; - IncrementExtraNonce(&block, m_node.chainman->ActiveChain().Tip(), extraNonce); + IncrementExtraNonce(&block, chainstate.m_chain.Tip(), extraNonce); } while (!CheckProofOfWork(block.GetHash(), block.nBits, chainparams.GetConsensus())) ++block.nNonce; diff --git a/src/test/validation_block_tests.cpp b/src/test/validation_block_tests.cpp index 20ac5ab130f8a..352421a815bac 100644 --- a/src/test/validation_block_tests.cpp +++ b/src/test/validation_block_tests.cpp @@ -79,6 +79,8 @@ std::shared_ptr MinerTestingSetup::Block(const uint256& prev_hash) txCoinbase.vout[1].scriptPubKey = P2SH_OP_TRUE; txCoinbase.vout[1].nValue = txCoinbase.vout[0].nValue; txCoinbase.vout[0].nValue = 0; + // Always pad with OP_0 at the end to avoid bad-cb-length error + txCoinbase.vin[0].scriptSig = CScript{} << WITH_LOCK(::cs_main, return m_node.chainman->m_blockman.LookupBlockIndex(prev_hash)->nHeight + 1) << OP_0; pblock->vtx[0] = MakeTransactionRef(std::move(txCoinbase)); return pblock; @@ -92,6 +94,11 @@ std::shared_ptr MinerTestingSetup::FinalizeBlock(std::shared_ptr ++(pblock->nNonce); } + // submit block header, so that miner can get the block height from the + // global state and the node has the topology of the chain + BlockValidationState ignored; + BOOST_CHECK(Assert(m_node.chainman)->ProcessNewBlockHeaders({pblock->GetBlockHeader()}, ignored, Params())); + return pblock; } @@ -146,13 +153,6 @@ BOOST_AUTO_TEST_CASE(processnewblock_signals_ordering) } bool ignored; - BlockValidationState state; - std::vector headers; - std::transform(blocks.begin(), blocks.end(), std::back_inserter(headers), [](std::shared_ptr b) { return b->GetBlockHeader(); }); - - // Process all the headers so we understand the toplogy of the chain - BOOST_CHECK(Assert(m_node.chainman)->ProcessNewBlockHeaders(headers, state, Params())); - // Connect the genesis block and drain any outstanding events BOOST_CHECK(Assert(m_node.chainman)->ProcessNewBlock(Params(), std::make_shared(Params().GenesisBlock()), true, &ignored)); SyncWithValidationInterfaceQueue(); diff --git a/test/functional/feature_block.py b/test/functional/feature_block.py index f5e9bd5c0411d..e4bc12a46a705 100755 --- a/test/functional/feature_block.py +++ b/test/functional/feature_block.py @@ -378,7 +378,9 @@ def run_test(self): # b30 has a max-sized coinbase scriptSig. self.move_tip(23) b30 = self.next_block(30) - b30.vtx[0].vin[0].scriptSig = b'\x00' * 100 + b30.vtx[0].vin[0].scriptSig = bytes(b30.vtx[0].vin[0].scriptSig) # Convert CScript to raw bytes + b30.vtx[0].vin[0].scriptSig += b'\x00' * (100 - len(b30.vtx[0].vin[0].scriptSig)) # Fill with 0s + assert_equal(len(b30.vtx[0].vin[0].scriptSig), 100) b30.vtx[0].rehash() b30 = self.update_block(30, []) self.send_blocks([b30], True) @@ -839,6 +841,7 @@ def run_test(self): b61.vtx[0].rehash() b61 = self.update_block(61, []) assert_equal(duplicate_tx.serialize(), b61.vtx[0].serialize()) + # BIP30 is always checked on regtest, regardless of the BIP34 activation height self.send_blocks([b61], success=False, reject_reason='bad-txns-BIP30', reconnect=True) # Test BIP30 (allow duplicate if spent) diff --git a/test/functional/feature_cltv.py b/test/functional/feature_cltv.py index 64f7e44a35677..60a0ec4538144 100755 --- a/test/functional/feature_cltv.py +++ b/test/functional/feature_cltv.py @@ -4,11 +4,11 @@ # file COPYING or http://www.opensource.org/licenses/mit-license.php. """Test BIP65 (CHECKLOCKTIMEVERIFY). -Test that the CHECKLOCKTIMEVERIFY soft-fork activates at (regtest) block height -1351. +Test that the CHECKLOCKTIMEVERIFY soft-fork activates. """ from test_framework.blocktools import ( + CLTV_HEIGHT, create_block, create_coinbase, ) @@ -34,8 +34,6 @@ MiniWalletMode, ) -CLTV_HEIGHT = 1351 - # Helper function to modify a transaction by # 1) prepending a given script to the scriptSig of vin 0 and @@ -66,9 +64,9 @@ def cltv_invalidate(tx, failure_reason): # +-------------------------------------------------+------------+--------------+ [[OP_CHECKLOCKTIMEVERIFY], None, None], [[OP_1NEGATE, OP_CHECKLOCKTIMEVERIFY, OP_DROP], None, None], - [[CScriptNum(1000), OP_CHECKLOCKTIMEVERIFY, OP_DROP], 0, 1296688602], # timestamp of genesis block - [[CScriptNum(1000), OP_CHECKLOCKTIMEVERIFY, OP_DROP], 0, 500], - [[CScriptNum(500), OP_CHECKLOCKTIMEVERIFY, OP_DROP], 0xffffffff, 500], + [[CScriptNum(100), OP_CHECKLOCKTIMEVERIFY, OP_DROP], 0, 1296688602], # timestamp of genesis block + [[CScriptNum(100), OP_CHECKLOCKTIMEVERIFY, OP_DROP], 0, 50], + [[CScriptNum(50), OP_CHECKLOCKTIMEVERIFY, OP_DROP], 0xffffffff, 50], ][failure_reason] cltv_modify_tx(tx, prepend_scriptsig=scheme[0], nsequence=scheme[1], nlocktime=scheme[2]) @@ -111,6 +109,7 @@ def run_test(self): self.log.info("Mining %d blocks", CLTV_HEIGHT - 2) wallet.generate(10) self.nodes[0].generate(CLTV_HEIGHT - 2 - 10) + assert_equal(self.nodes[0].getblockcount(), CLTV_HEIGHT - 2) self.log.info("Test that invalid-according-to-CLTV transactions can still appear in a block") diff --git a/test/functional/feature_csv_activation.py b/test/functional/feature_csv_activation.py index c5bf7ddeaafd3..5db5311c6ba42 100755 --- a/test/functional/feature_csv_activation.py +++ b/test/functional/feature_csv_activation.py @@ -40,6 +40,7 @@ from itertools import product from test_framework.blocktools import ( + CSV_ACTIVATION_HEIGHT, create_block, create_coinbase, TIME_GENESIS_BLOCK, @@ -63,12 +64,12 @@ TESTING_TX_COUNT = 83 # Number of testing transactions: 1 BIP113 tx, 16 BIP68 txs, 66 BIP112 txs (see comments above) COINBASE_BLOCK_COUNT = TESTING_TX_COUNT # Number of coinbase blocks we need to generate as inputs for our txs BASE_RELATIVE_LOCKTIME = 10 -CSV_ACTIVATION_HEIGHT = 432 SEQ_DISABLE_FLAG = 1 << 31 SEQ_RANDOM_HIGH_BIT = 1 << 25 SEQ_TYPE_FLAG = 1 << 22 SEQ_RANDOM_LOW_BIT = 1 << 18 + def relative_locktime(sdf, srhb, stf, srlb): """Returns a locktime with certain bits set.""" @@ -83,6 +84,7 @@ def relative_locktime(sdf, srhb, stf, srlb): locktime |= SEQ_RANDOM_LOW_BIT return locktime + def all_rlt_txs(txs): return [tx['tx'] for tx in txs] @@ -94,6 +96,7 @@ def set_test_params(self): # Must also set '-maxtipage=600100' to allow syncing from very old blocks # and '-dip3params=2000:2000' to create pre-dip3 blocks only self.extra_args = [[ + '-peertimeout=999999', # bump because mocktime might cause a disconnect otherwise '-whitelist=noban@127.0.0.1', '-maxtipage=600100', '-dip3params=2000:2000', '-par=1', # Use only one script thread to get the exact reject reason for testing @@ -149,13 +152,13 @@ def create_bip112txs(self, bip112inputs, varyOP_CSV, txversion, locktime_delta=0 for i, (sdf, srhb, stf, srlb) in enumerate(product(*[[True, False]] * 4)): locktime = relative_locktime(sdf, srhb, stf, srlb) tx = self.create_self_transfer_from_utxo(bip112inputs[i]) - if (varyOP_CSV): # if varying OP_CSV, nSequence is fixed + if varyOP_CSV: # if varying OP_CSV, nSequence is fixed tx.vin[0].nSequence = BASE_RELATIVE_LOCKTIME + locktime_delta else: # vary nSequence instead, OP_CSV is fixed tx.vin[0].nSequence = locktime + locktime_delta tx.nVersion = txversion self.miniwallet.sign_tx(tx) - if (varyOP_CSV): + if varyOP_CSV: tx.vin[0].scriptSig = CScript([locktime, OP_CHECKSEQUENCEVERIFY, OP_DROP] + list(CScript(tx.vin[0].scriptSig))) else: tx.vin[0].scriptSig = CScript([BASE_RELATIVE_LOCKTIME, OP_CHECKSEQUENCEVERIFY, OP_DROP] + list(CScript(tx.vin[0].scriptSig))) @@ -202,7 +205,7 @@ def run_test(self): self.tip = int(self.nodes[0].getbestblockhash(), 16) # Activation height is hardcoded - test_blocks = self.generate_blocks(CSV_ACTIVATION_HEIGHT-5 - COINBASE_BLOCK_COUNT) + test_blocks = self.generate_blocks(CSV_ACTIVATION_HEIGHT - 5 - COINBASE_BLOCK_COUNT) #test_blocks = self.generate_blocks(345) self.send_blocks(test_blocks) assert not softfork_active(self.nodes[0], 'csv') @@ -488,5 +491,6 @@ def run_test(self): self.send_blocks([self.create_test_block(time_txs)]) self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash()) + if __name__ == '__main__': BIP68_112_113Test().main() diff --git a/test/functional/feature_dersig.py b/test/functional/feature_dersig.py index 6e3a49df3e307..597bce5a7ef56 100755 --- a/test/functional/feature_dersig.py +++ b/test/functional/feature_dersig.py @@ -4,10 +4,11 @@ # file COPYING or http://www.opensource.org/licenses/mit-license.php. """Test BIP66 (DER SIG). -Test that the DERSIG soft-fork activates at (regtest) height 1251. +Test the DERSIG soft-fork activation on regtest. """ from test_framework.blocktools import ( + DERSIG_HEIGHT, create_block, create_coinbase, ) @@ -24,8 +25,6 @@ MiniWalletMode, ) -DERSIG_HEIGHT = 1251 - # A canonical signature consists of: # <30> <02> <02> @@ -88,8 +87,10 @@ def run_test(self): block.rehash() block.solve() + assert_equal(self.nodes[0].getblockcount(), DERSIG_HEIGHT - 2) self.test_dersig_info(is_active=False) # Not active as of current tip and next block does not need to obey rules peer.send_and_ping(msg_block(block)) + assert_equal(self.nodes[0].getblockcount(), DERSIG_HEIGHT - 1) self.test_dersig_info(is_active=True) # Not active as of current tip, but next block must obey rules assert_equal(self.nodes[0].getbestblockhash(), block.hash) diff --git a/test/functional/rpc_blockchain.py b/test/functional/rpc_blockchain.py index 9c4eab3a37de8..10c8d961364e4 100755 --- a/test/functional/rpc_blockchain.py +++ b/test/functional/rpc_blockchain.py @@ -27,6 +27,8 @@ from test_framework.address import ADDRESS_BCRT1_P2SH_OP_TRUE from test_framework.blocktools import ( + CLTV_HEIGHT, + DERSIG_HEIGHT, create_block, create_coinbase, TIME_GENESIS_BLOCK, @@ -142,9 +144,9 @@ def _test_getblockchaininfo(self): assert_equal(res['prune_target_size'], 576716800) assert_greater_than(res['size_on_disk'], 0) assert_equal(res['softforks'], { - 'bip34': {'type': 'buried', 'active': False, 'height': 500}, - 'bip66': {'type': 'buried', 'active': False, 'height': 1251}, - 'bip65': {'type': 'buried', 'active': False, 'height': 1351}, + 'bip34': {'type': 'buried', 'active': True, 'height': 2}, + 'bip66': {'type': 'buried', 'active': True, 'height': DERSIG_HEIGHT}, + 'bip65': {'type': 'buried', 'active': True, 'height': CLTV_HEIGHT}, 'bip147': { 'type': 'buried', 'active': False, 'height': 432}, 'csv': {'type': 'buried', 'active': False, 'height': 432}, 'dip0001': { 'type': 'buried', 'active': False, 'height': 2000}, diff --git a/test/functional/test_framework/blocktools.py b/test/functional/test_framework/blocktools.py index b12a4749ef852..a77a11669c4f0 100644 --- a/test/functional/test_framework/blocktools.py +++ b/test/functional/test_framework/blocktools.py @@ -35,14 +35,21 @@ # Coinbase transaction outputs can only be spent after this number of new blocks (network rule) COINBASE_MATURITY = 100 +# Soft-fork activation heights +DERSIG_HEIGHT = 102 # BIP 66 +CLTV_HEIGHT = 111 # BIP 65 +CSV_ACTIVATION_HEIGHT = 432 + NORMAL_GBT_REQUEST_PARAMS = {"rules": []} # type: ignore[var-annotated] +VERSIONBITS_LAST_OLD_BLOCK_VERSION = 4 + def create_block(hashprev=None, coinbase=None, ntime=None, *, version=None, tmpl=None, txlist=None, dip4_activated=False, v20_activated=False): """Create a block (with regtest difficulty).""" block = CBlock() if tmpl is None: tmpl = {} - block.nVersion = version or tmpl.get('version') or 1 + block.nVersion = version or tmpl.get('version') or VERSIONBITS_LAST_OLD_BLOCK_VERSION block.nTime = ntime or tmpl.get('curtime') or int(time.time() + 600) block.hashPrevBlock = hashprev or int(tmpl['previousblockhash'], 0x10) if tmpl and not tmpl.get('bits') is None: diff --git a/test/functional/test_framework/messages.py b/test/functional/test_framework/messages.py index b98616df86aca..b634231253c82 100755 --- a/test/functional/test_framework/messages.py +++ b/test/functional/test_framework/messages.py @@ -564,7 +564,7 @@ def __init__(self, header=None): self.calc_sha256() def set_null(self): - self.nVersion = 1 + self.nVersion = 4 self.hashPrevBlock = 0 self.hashMerkleRoot = 0 self.nTime = 0 diff --git a/test/functional/test_framework/util.py b/test/functional/test_framework/util.py index ec3c6dcde17d7..9f0930e40930d 100644 --- a/test/functional/test_framework/util.py +++ b/test/functional/test_framework/util.py @@ -601,6 +601,17 @@ def mine_large_block(node, utxos=None): node.generate(1) +def generate_to_height(node, target_height): + """Generates blocks until a given target block height has been reached. + To prevent timeouts, only up to 200 blocks are generated per RPC call. + Can be used to activate certain soft-forks (e.g. CSV, CLTV).""" + current_height = node.getblockcount() + while current_height < target_height: + nblocks = min(200, target_height - current_height) + current_height += len(node.generate(nblocks)) + assert_equal(node.getblockcount(), target_height) + + def find_vout_for_address(node, txid, addr): """ Locate the vout index of the given transaction sending to the