diff options
Diffstat (limited to 'test')
21 files changed, 424 insertions, 141 deletions
diff --git a/test/functional/feature_assumevalid.py b/test/functional/feature_assumevalid.py index 67cacaa9ce..482c29c994 100755 --- a/test/functional/feature_assumevalid.py +++ b/test/functional/feature_assumevalid.py @@ -83,8 +83,6 @@ class AssumeValidTest(BitcoinTestFramework): break def run_test(self): - p2p0 = self.nodes[0].add_p2p_connection(BaseNode()) - # Build the blockchain self.tip = int(self.nodes[0].getbestblockhash(), 16) self.block_time = self.nodes[0].getblock(self.nodes[0].getbestblockhash())['time'] + 1 @@ -139,28 +137,23 @@ class AssumeValidTest(BitcoinTestFramework): self.block_time += 1 height += 1 - self.nodes[0].disconnect_p2ps() - # Start node1 and node2 with assumevalid so they accept a block with a bad signature. self.start_node(1, extra_args=["-assumevalid=" + hex(block102.sha256)]) self.start_node(2, extra_args=["-assumevalid=" + hex(block102.sha256)]) p2p0 = self.nodes[0].add_p2p_connection(BaseNode()) - p2p1 = self.nodes[1].add_p2p_connection(BaseNode()) - p2p2 = self.nodes[2].add_p2p_connection(BaseNode()) - - # send header lists to all three nodes p2p0.send_header_for_blocks(self.blocks[0:2000]) p2p0.send_header_for_blocks(self.blocks[2000:]) - p2p1.send_header_for_blocks(self.blocks[0:2000]) - p2p1.send_header_for_blocks(self.blocks[2000:]) - p2p2.send_header_for_blocks(self.blocks[0:200]) # Send blocks to node0. Block 102 will be rejected. self.send_blocks_until_disconnected(p2p0) self.wait_until(lambda: self.nodes[0].getblockcount() >= COINBASE_MATURITY + 1) assert_equal(self.nodes[0].getblockcount(), COINBASE_MATURITY + 1) + p2p1 = self.nodes[1].add_p2p_connection(BaseNode()) + p2p1.send_header_for_blocks(self.blocks[0:2000]) + p2p1.send_header_for_blocks(self.blocks[2000:]) + # Send all blocks to node1. All blocks will be accepted. for i in range(2202): p2p1.send_message(msg_block(self.blocks[i])) @@ -168,6 +161,9 @@ class AssumeValidTest(BitcoinTestFramework): p2p1.sync_with_ping(960) assert_equal(self.nodes[1].getblock(self.nodes[1].getbestblockhash())['height'], 2202) + p2p2 = self.nodes[2].add_p2p_connection(BaseNode()) + p2p2.send_header_for_blocks(self.blocks[0:200]) + # Send blocks to node2. Block 102 will be rejected. self.send_blocks_until_disconnected(p2p2) self.wait_until(lambda: self.nodes[2].getblockcount() >= COINBASE_MATURITY + 1) diff --git a/test/functional/feature_fee_estimation.py b/test/functional/feature_fee_estimation.py index 0357a6f281..619f3c08ea 100755 --- a/test/functional/feature_fee_estimation.py +++ b/test/functional/feature_fee_estimation.py @@ -23,7 +23,7 @@ from test_framework.wallet import MiniWallet def small_txpuzzle_randfee( - wallet, from_node, conflist, unconflist, amount, min_fee, fee_increment + wallet, from_node, conflist, unconflist, amount, min_fee, fee_increment, batch_reqs ): """Create and send a transaction with a random fee using MiniWallet. @@ -57,8 +57,11 @@ def small_txpuzzle_randfee( tx.vout[0].nValue = int((total_in - amount - fee) * COIN) tx.vout.append(deepcopy(tx.vout[0])) tx.vout[1].nValue = int(amount * COIN) + tx.rehash() + txid = tx.hash + tx_hex = tx.serialize().hex() - txid = from_node.sendrawtransaction(hexstring=tx.serialize().hex(), maxfeerate=0) + batch_reqs.append(from_node.sendrawtransaction.get_request(hexstring=tx_hex, maxfeerate=0)) unconflist.append({"txid": txid, "vout": 0, "value": total_in - amount - fee}) unconflist.append({"txid": txid, "vout": 1, "value": amount}) @@ -115,13 +118,12 @@ def check_estimates(node, fees_seen): check_smart_estimates(node, fees_seen) -def send_tx(wallet, node, utxo, feerate): - """Broadcast a 1in-1out transaction with a specific input and feerate (sat/vb).""" - return wallet.send_self_transfer( - from_node=node, +def make_tx(wallet, utxo, feerate): + """Create a 1in-1out transaction with a specific input and feerate (sat/vb).""" + return wallet.create_self_transfer( utxo_to_spend=utxo, fee_rate=Decimal(feerate * 1000) / COIN, - )['txid'] + ) class EstimateFeeTest(BitcoinTestFramework): @@ -156,6 +158,7 @@ class EstimateFeeTest(BitcoinTestFramework): # resorting to tx's that depend on the mempool when those run out for _ in range(numblocks): random.shuffle(self.confutxo) + batch_sendtx_reqs = [] for _ in range(random.randrange(100 - 50, 100 + 50)): from_index = random.randint(1, 2) (tx_bytes, fee) = small_txpuzzle_randfee( @@ -166,9 +169,12 @@ class EstimateFeeTest(BitcoinTestFramework): Decimal("0.005"), min_fee, min_fee, + batch_sendtx_reqs, ) tx_kbytes = tx_bytes / 1000.0 self.fees_per_kb.append(float(fee) / tx_kbytes) + for node in self.nodes: + node.batch(batch_sendtx_reqs) self.sync_mempools(wait=0.1) mined = mining_node.getblock(self.generate(mining_node, 1)[0], True)["tx"] # update which txouts are confirmed @@ -245,14 +251,20 @@ class EstimateFeeTest(BitcoinTestFramework): assert_greater_than_or_equal(len(utxos), 250) for _ in range(5): # Broadcast 45 low fee transactions that will need to be RBF'd + txs = [] for _ in range(45): u = utxos.pop(0) - txid = send_tx(self.wallet, node, u, low_feerate) + tx = make_tx(self.wallet, u, low_feerate) utxos_to_respend.append(u) - txids_to_replace.append(txid) + txids_to_replace.append(tx["txid"]) + txs.append(tx) # Broadcast 5 low fee transaction which don't need to for _ in range(5): - send_tx(self.wallet, node, utxos.pop(0), low_feerate) + tx = make_tx(self.wallet, utxos.pop(0), low_feerate) + txs.append(tx) + batch_send_tx = [node.sendrawtransaction.get_request(tx["hex"]) for tx in txs] + for n in self.nodes: + n.batch(batch_send_tx) # Mine the transactions on another node self.sync_mempools(wait=0.1, nodes=[node, miner]) for txid in txids_to_replace: @@ -261,7 +273,12 @@ class EstimateFeeTest(BitcoinTestFramework): # RBF the low-fee transactions while len(utxos_to_respend) > 0: u = utxos_to_respend.pop(0) - send_tx(self.wallet, node, u, high_feerate) + tx = make_tx(self.wallet, u, high_feerate) + node.sendrawtransaction(tx["hex"]) + txs.append(tx) + dec_txs = [res["result"] for res in node.batch([node.decoderawtransaction.get_request(tx["hex"]) for tx in txs])] + self.wallet.scan_txs(dec_txs) + # Mine the last replacement txs self.sync_mempools(wait=0.1, nodes=[node, miner]) diff --git a/test/functional/feature_maxtipage.py b/test/functional/feature_maxtipage.py index ddc2102542..51f37ef1e0 100755 --- a/test/functional/feature_maxtipage.py +++ b/test/functional/feature_maxtipage.py @@ -22,23 +22,24 @@ class MaxTipAgeTest(BitcoinTestFramework): self.setup_clean_chain = True self.num_nodes = 2 - def test_maxtipage(self, maxtipage, set_parameter=True): + def test_maxtipage(self, maxtipage, set_parameter=True, test_deltas=True): node_miner = self.nodes[0] node_ibd = self.nodes[1] self.restart_node(1, [f'-maxtipage={maxtipage}'] if set_parameter else None) self.connect_nodes(0, 1) - - # tips older than maximum age -> stay in IBD cur_time = int(time.time()) - node_ibd.setmocktime(cur_time) - for delta in [5, 4, 3, 2, 1]: - node_miner.setmocktime(cur_time - maxtipage - delta) - self.generate(node_miner, 1) - assert_equal(node_ibd.getblockchaininfo()['initialblockdownload'], True) + + if test_deltas: + # tips older than maximum age -> stay in IBD + node_ibd.setmocktime(cur_time) + for delta in [5, 4, 3, 2, 1]: + node_miner.setmocktime(cur_time - maxtipage - delta) + self.generate(node_miner, 1) + assert_equal(node_ibd.getblockchaininfo()['initialblockdownload'], True) # tip within maximum age -> leave IBD - node_miner.setmocktime(cur_time - maxtipage) + node_miner.setmocktime(max(cur_time - maxtipage, 0)) self.generate(node_miner, 1) assert_equal(node_ibd.getblockchaininfo()['initialblockdownload'], False) @@ -51,6 +52,10 @@ class MaxTipAgeTest(BitcoinTestFramework): self.log.info(f"Test IBD with maximum tip age of {hours} hours (-maxtipage={maxtipage}).") self.test_maxtipage(maxtipage) + max_long_val = 9223372036854775807 + self.log.info(f"Test IBD with highest allowable maximum tip age ({max_long_val}).") + self.test_maxtipage(max_long_val, test_deltas=False) + if __name__ == '__main__': MaxTipAgeTest().main() diff --git a/test/functional/feature_pruning.py b/test/functional/feature_pruning.py index 58bc6ca67c..de1ea8a3a6 100755 --- a/test/functional/feature_pruning.py +++ b/test/functional/feature_pruning.py @@ -10,8 +10,11 @@ This test takes 30 mins or more (up to 2 hours) """ import os -from test_framework.blocktools import create_coinbase -from test_framework.messages import CBlock +from test_framework.blocktools import ( + MIN_BLOCKS_TO_KEEP, + create_block, + create_coinbase, +) from test_framework.script import ( CScript, OP_NOP, @@ -48,21 +51,7 @@ def mine_large_blocks(node, n): previousblockhash = int(best_block["hash"], 16) for _ in range(n): - # Build the coinbase transaction (with large scriptPubKey) - coinbase_tx = create_coinbase(height) - coinbase_tx.vin[0].nSequence = 2 ** 32 - 1 - coinbase_tx.vout[0].scriptPubKey = big_script - coinbase_tx.rehash() - - # Build the block - block = CBlock() - block.nVersion = best_block["version"] - block.hashPrevBlock = previousblockhash - block.nTime = mine_large_blocks.nTime - block.nBits = int('207fffff', 16) - block.nNonce = 0 - block.vtx = [coinbase_tx] - block.hashMerkleRoot = block.calc_merkle_root() + block = create_block(hashprev=previousblockhash, ntime=mine_large_blocks.nTime, coinbase=create_coinbase(height, script_pubkey=big_script)) block.solve() # Submit to the node @@ -345,7 +334,7 @@ class PruneTest(BitcoinTestFramework): assert has_block(2), "blk00002.dat is still there, should be pruned by now" # advance the tip so blk00002.dat and blk00003.dat can be pruned (the last 288 blocks should now be in blk00004.dat) - self.generate(node, 288, sync_fun=self.no_op) + self.generate(node, MIN_BLOCKS_TO_KEEP, sync_fun=self.no_op) prune(1000) assert not has_block(2), "blk00002.dat is still there, should be pruned by now" assert not has_block(3), "blk00003.dat is still there, should be pruned by now" diff --git a/test/functional/feature_taproot.py b/test/functional/feature_taproot.py index dbe51f8f54..f6f6f3f78b 100755 --- a/test/functional/feature_taproot.py +++ b/test/functional/feature_taproot.py @@ -1292,7 +1292,7 @@ class TaprootTest(BitcoinTestFramework): # It is not impossible to fit enough tapscript sigops to hit the old 80k limit without # busting txin-level limits. We simply have to account for the p2pk outputs in all # transactions. - extra_output_script = CScript([OP_CHECKSIG]*((MAX_BLOCK_SIGOPS_WEIGHT - sigops_weight) // WITNESS_SCALE_FACTOR)) + extra_output_script = CScript(bytes([OP_CHECKSIG]*((MAX_BLOCK_SIGOPS_WEIGHT - sigops_weight) // WITNESS_SCALE_FACTOR))) coinbase_tx = create_coinbase(self.lastblockheight + 1, pubkey=cb_pubkey, extra_output_script=extra_output_script, fees=fees) block = create_block(self.tip, coinbase_tx, self.lastblocktime + 1, txlist=txs) diff --git a/test/functional/interface_bitcoin_cli.py b/test/functional/interface_bitcoin_cli.py index b1369c2615..90a543b51b 100755 --- a/test/functional/interface_bitcoin_cli.py +++ b/test/functional/interface_bitcoin_cli.py @@ -90,6 +90,10 @@ class TestBitcoinCli(BitcoinTestFramework): assert_raises_rpc_error(-8, "Parameter arg1 specified twice both as positional and named argument", self.nodes[0].cli.echo, 0, 1, arg1=1) assert_raises_rpc_error(-8, "Parameter arg1 specified twice both as positional and named argument", self.nodes[0].cli.echo, 0, None, 2, arg1=1) + self.log.info("Test that later cli named arguments values silently overwrite earlier ones") + assert_equal(self.nodes[0].cli("-named", "echo", "arg0=0", "arg1=1", "arg2=2", "arg1=3").send_cli(), ['0', '3', '2']) + assert_raises_rpc_error(-8, "Parameter args specified multiple times", self.nodes[0].cli("-named", "echo", "args=[0,1,2,3]", "4", "5", "6", ).send_cli) + user, password = get_auth_cookie(self.nodes[0].datadir, self.chain) self.log.info("Test -stdinrpcpass option") diff --git a/test/functional/interface_rpc.py b/test/functional/interface_rpc.py index 48082f3a17..3389746635 100755 --- a/test/functional/interface_rpc.py +++ b/test/functional/interface_rpc.py @@ -25,7 +25,7 @@ def expect_http_status(expected_http_status, expected_rpc_code, def test_work_queue_getblock(node, got_exceeded_error): while not got_exceeded_error: try: - node.cli('getrpcinfo').send_cli() + node.cli("waitfornewblock", "500").send_cli() except subprocess.CalledProcessError as e: assert_equal(e.output, 'error: Server response: Work queue depth exceeded\n') got_exceeded_error.append(True) diff --git a/test/functional/rpc_packages.py b/test/functional/rpc_packages.py index a512a6b675..df11750438 100755 --- a/test/functional/rpc_packages.py +++ b/test/functional/rpc_packages.py @@ -29,6 +29,7 @@ class RPCPackagesTest(BitcoinTestFramework): def set_test_params(self): self.num_nodes = 1 self.setup_clean_chain = True + self.extra_args = [["-whitelist=noban@127.0.0.1"]] # noban speeds up tx relay def assert_testres_equal(self, package_hex, testres_expected): """Shuffle package_hex and assert that the testmempoolaccept result matches testres_expected. This should only diff --git a/test/functional/rpc_rawtransaction.py b/test/functional/rpc_rawtransaction.py index 15fc947eef..b87f3ad6f3 100755 --- a/test/functional/rpc_rawtransaction.py +++ b/test/functional/rpc_rawtransaction.py @@ -14,6 +14,7 @@ Test the following RPCs: from collections import OrderedDict from decimal import Decimal +from itertools import product from test_framework.blocktools import COINBASE_MATURITY from test_framework.messages import ( @@ -81,6 +82,7 @@ class RawTransactionsTest(BitcoinTestFramework): self.generate(self.nodes[0], COINBASE_MATURITY + 1) self.getrawtransaction_tests() + self.getrawtransaction_verbosity_tests() self.createrawtransaction_tests() self.sendrawtransaction_tests() self.sendrawtransaction_testmempoolaccept_tests() @@ -116,6 +118,7 @@ class RawTransactionsTest(BitcoinTestFramework): # 4. valid parameters - supply txid and 1 for verbose. # We only check the "hex" field of the output so we don't need to update this test every time the output format changes. assert_equal(self.nodes[n].getrawtransaction(txId, 1)["hex"], tx['hex']) + assert_equal(self.nodes[n].getrawtransaction(txId, 2)["hex"], tx['hex']) # 5. valid parameters - supply txid and True for non-verbose assert_equal(self.nodes[n].getrawtransaction(txId, True)["hex"], tx['hex']) @@ -126,13 +129,14 @@ class RawTransactionsTest(BitcoinTestFramework): # 6. invalid parameters - supply txid and invalid boolean values (strings) for verbose for value in ["True", "False"]: - assert_raises_rpc_error(-3, "not of expected type bool", self.nodes[n].getrawtransaction, txid=txId, verbose=value) + assert_raises_rpc_error(-3, "not of expected type number", self.nodes[n].getrawtransaction, txid=txId, verbose=value) + assert_raises_rpc_error(-3, "not of expected type number", self.nodes[n].getrawtransaction, txid=txId, verbosity=value) # 7. invalid parameters - supply txid and empty array - assert_raises_rpc_error(-3, "not of expected type bool", self.nodes[n].getrawtransaction, txId, []) + assert_raises_rpc_error(-3, "not of expected type number", self.nodes[n].getrawtransaction, txId, []) # 8. invalid parameters - supply txid and empty dict - assert_raises_rpc_error(-3, "not of expected type bool", self.nodes[n].getrawtransaction, txId, {}) + assert_raises_rpc_error(-3, "not of expected type number", self.nodes[n].getrawtransaction, txId, {}) # Make a tx by sending, then generate 2 blocks; block1 has the tx in it tx = self.wallet.send_self_transfer(from_node=self.nodes[2])['txid'] @@ -145,9 +149,10 @@ class RawTransactionsTest(BitcoinTestFramework): assert_equal(gottx['in_active_chain'], True) if n == 0: self.log.info("Test getrawtransaction with -txindex, without blockhash: 'in_active_chain' should be absent") - gottx = self.nodes[n].getrawtransaction(txid=tx, verbose=True) - assert_equal(gottx['txid'], tx) - assert 'in_active_chain' not in gottx + for v in [1,2]: + gottx = self.nodes[n].getrawtransaction(txid=tx, verbosity=v) + assert_equal(gottx['txid'], tx) + assert 'in_active_chain' not in gottx else: self.log.info("Test getrawtransaction without -txindex, without blockhash: expect the call to raise") assert_raises_rpc_error(-5, err_msg, self.nodes[n].getrawtransaction, txid=tx, verbose=True) @@ -172,6 +177,70 @@ class RawTransactionsTest(BitcoinTestFramework): block = self.nodes[0].getblock(self.nodes[0].getblockhash(0)) assert_raises_rpc_error(-5, "The genesis block coinbase is not considered an ordinary transaction", self.nodes[0].getrawtransaction, block['merkleroot']) + def getrawtransaction_verbosity_tests(self): + tx = self.wallet.send_self_transfer(from_node=self.nodes[1])['txid'] + [block1] = self.generate(self.nodes[1], 1) + fields = [ + 'blockhash', + 'blocktime', + 'confirmations', + 'hash', + 'hex', + 'in_active_chain', + 'locktime', + 'size', + 'time', + 'txid', + 'vin', + 'vout', + 'vsize', + 'weight', + ] + prevout_fields = [ + 'generated', + 'height', + 'value', + 'scriptPubKey', + ] + script_pub_key_fields = [ + 'address', + 'asm', + 'hex', + 'type', + ] + # node 0 & 2 with verbosity 1 & 2 + for n, v in product([0, 2], [1, 2]): + self.log.info(f"Test getrawtransaction_verbosity {v} {'with' if n == 0 else 'without'} -txindex, with blockhash") + gottx = self.nodes[n].getrawtransaction(txid=tx, verbosity=v, blockhash=block1) + missing_fields = set(fields).difference(gottx.keys()) + if missing_fields: + raise AssertionError(f"fields {', '.join(missing_fields)} are not in transaction") + + assert(len(gottx['vin']) > 0) + if v == 1: + assert('fee' not in gottx) + assert('prevout' not in gottx['vin'][0]) + if v == 2: + assert(isinstance(gottx['fee'], Decimal)) + assert('prevout' in gottx['vin'][0]) + prevout = gottx['vin'][0]['prevout'] + script_pub_key = prevout['scriptPubKey'] + + missing_fields = set(prevout_fields).difference(prevout.keys()) + if missing_fields: + raise AssertionError(f"fields {', '.join(missing_fields)} are not in transaction") + + missing_fields = set(script_pub_key_fields).difference(script_pub_key.keys()) + if missing_fields: + raise AssertionError(f"fields {', '.join(missing_fields)} are not in transaction") + + # check verbosity 2 without blockhash but with txindex + assert('fee' in self.nodes[0].getrawtransaction(txid=tx, verbosity=2)) + # check that coinbase has no fee or does not throw any errors for verbosity 2 + coin_base = self.nodes[1].getblock(block1)['tx'][0] + gottx = self.nodes[1].getrawtransaction(txid=coin_base, verbosity=2, blockhash=block1) + assert('fee' not in gottx) + def createrawtransaction_tests(self): self.log.info("Test createrawtransaction") # Test `createrawtransaction` required parameters diff --git a/test/functional/test_framework/blocktools.py b/test/functional/test_framework/blocktools.py index 574ea10356..f8e94ca6ba 100644 --- a/test/functional/test_framework/blocktools.py +++ b/test/functional/test_framework/blocktools.py @@ -61,6 +61,7 @@ WITNESS_COMMITMENT_HEADER = b"\xaa\x21\xa9\xed" NORMAL_GBT_REQUEST_PARAMS = {"rules": ["segwit"]} VERSIONBITS_LAST_OLD_BLOCK_VERSION = 4 +MIN_BLOCKS_TO_KEEP = 288 def create_block(hashprev=None, coinbase=None, ntime=None, *, version=None, tmpl=None, txlist=None): @@ -120,7 +121,7 @@ def script_BIP34_coinbase_height(height): return CScript([CScriptNum(height)]) -def create_coinbase(height, pubkey=None, extra_output_script=None, fees=0, nValue=50): +def create_coinbase(height, pubkey=None, *, script_pubkey=None, extra_output_script=None, fees=0, nValue=50): """Create a coinbase transaction. If pubkey is passed in, the coinbase output will be a P2PK output; @@ -138,6 +139,8 @@ def create_coinbase(height, pubkey=None, extra_output_script=None, fees=0, nValu coinbaseoutput.nValue += fees if pubkey is not None: coinbaseoutput.scriptPubKey = key_to_p2pk_script(pubkey) + elif script_pubkey is not None: + coinbaseoutput.scriptPubKey = script_pubkey else: coinbaseoutput.scriptPubKey = CScript([OP_TRUE]) coinbase.vout = [coinbaseoutput] diff --git a/test/functional/test_framework/test_framework.py b/test/functional/test_framework/test_framework.py index 7cfeae3ff6..a3f2bae2d9 100755 --- a/test/functional/test_framework/test_framework.py +++ b/test/functional/test_framework/test_framework.py @@ -214,11 +214,11 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass): self.options.descriptors = None elif self.options.descriptors is None: # Some wallet is either required or optionally used by the test. - # Prefer BDB unless it isn't available - if self.is_bdb_compiled(): - self.options.descriptors = False - elif self.is_sqlite_compiled(): + # Prefer SQLite unless it isn't available + if self.is_sqlite_compiled(): self.options.descriptors = True + elif self.is_bdb_compiled(): + self.options.descriptors = False else: # If neither are compiled, tests requiring a wallet will be skipped and the value of self.options.descriptors won't matter # It still needs to exist and be None in order for tests to work however. diff --git a/test/functional/test_framework/test_shell.py b/test/functional/test_framework/test_shell.py index 26df128f1f..2d8935dfe6 100644 --- a/test/functional/test_framework/test_shell.py +++ b/test/functional/test_framework/test_shell.py @@ -16,6 +16,9 @@ class TestShell: start a single TestShell at a time.""" class __TestShell(BitcoinTestFramework): + def add_options(self, parser): + self.add_wallet_options(parser) + def set_test_params(self): pass diff --git a/test/functional/test_framework/wallet.py b/test/functional/test_framework/wallet.py index a94b9fe3fd..ecde329a1e 100644 --- a/test/functional/test_framework/wallet.py +++ b/test/functional/test_framework/wallet.py @@ -141,6 +141,10 @@ class MiniWallet: if out['scriptPubKey']['hex'] == self._scriptPubKey.hex(): self._utxos.append(self._create_utxo(txid=tx["txid"], vout=out["n"], value=out["value"], height=0)) + def scan_txs(self, txs): + for tx in txs: + self.scan_tx(tx) + def sign_tx(self, tx, fixed_length=True): """Sign tx that has been created by MiniWallet in P2PK mode""" assert_equal(self._mode, MiniWalletMode.RAW_P2PK) diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py index 31b308546d..00e2d92f5f 100755 --- a/test/functional/test_runner.py +++ b/test/functional/test_runner.py @@ -85,61 +85,87 @@ EXTENDED_SCRIPTS = [ 'feature_pruning.py', 'feature_dbcrash.py', 'feature_index_prune.py', + 'wallet_pruning.py --legacy-wallet', ] BASE_SCRIPTS = [ # Scripts that are run by default. # Longest test should go first, to favor running tests in parallel - 'wallet_hd.py --legacy-wallet', - 'wallet_hd.py --descriptors', - 'wallet_backup.py --legacy-wallet', - 'wallet_backup.py --descriptors', # vv Tests less than 5m vv + 'feature_fee_estimation.py', + 'feature_taproot.py', + 'feature_block.py', + # vv Tests less than 2m vv 'mining_getblocktemplate_longpoll.py', + 'p2p_segwit.py', 'feature_maxuploadtarget.py', - 'feature_block.py', + 'mempool_updatefromblock.py', + 'mempool_persist.py --descriptors', + # vv Tests less than 60s vv + 'rpc_psbt.py --legacy-wallet', + 'rpc_psbt.py --descriptors', 'wallet_fundrawtransaction.py --legacy-wallet', 'wallet_fundrawtransaction.py --descriptors', - 'p2p_compactblocks.py', - 'p2p_compactblocks_blocksonly.py', + 'wallet_bumpfee.py --legacy-wallet', + 'wallet_bumpfee.py --descriptors', + 'wallet_import_rescan.py --legacy-wallet', + 'wallet_backup.py --legacy-wallet', + 'wallet_backup.py --descriptors', 'feature_segwit.py --legacy-wallet', 'feature_segwit.py --descriptors', - # vv Tests less than 2m vv + 'p2p_tx_download.py', + 'wallet_avoidreuse.py --legacy-wallet', + 'wallet_avoidreuse.py --descriptors', + 'feature_abortnode.py', + 'wallet_address_types.py --legacy-wallet', + 'wallet_address_types.py --descriptors', 'wallet_basic.py --legacy-wallet', 'wallet_basic.py --descriptors', - 'wallet_labels.py --legacy-wallet', - 'wallet_labels.py --descriptors', - 'p2p_segwit.py', + 'feature_maxtipage.py', + 'wallet_multiwallet.py --legacy-wallet', + 'wallet_multiwallet.py --descriptors', + 'wallet_multiwallet.py --usecli', + 'p2p_dns_seeds.py', + 'wallet_groups.py --legacy-wallet', + 'wallet_groups.py --descriptors', + 'p2p_blockfilters.py', + 'feature_assumevalid.py', + 'wallet_taproot.py --descriptors', + 'feature_bip68_sequence.py', + 'rpc_packages.py', + 'rpc_bind.py --ipv4', + 'rpc_bind.py --ipv6', + 'rpc_bind.py --nonloopback', + 'p2p_headers_sync_with_minchainwork.py', + 'p2p_feefilter.py', + 'feature_csv_activation.py', + 'p2p_sendheaders.py', + 'wallet_listtransactions.py --legacy-wallet', + 'wallet_listtransactions.py --descriptors', + # vv Tests less than 30s vv + 'p2p_invalid_messages.py', + 'rpc_createmultisig.py', 'p2p_timeouts.py', - 'p2p_tx_download.py', - 'mempool_updatefromblock.py', 'wallet_dump.py --legacy-wallet', - 'feature_taproot.py', 'rpc_signer.py', 'wallet_signer.py --descriptors', - # vv Tests less than 60s vv - 'p2p_sendheaders.py', 'wallet_importmulti.py --legacy-wallet', 'mempool_limit.py', 'rpc_txoutproof.py', 'wallet_listreceivedby.py --legacy-wallet', 'wallet_listreceivedby.py --descriptors', 'wallet_abandonconflict.py --legacy-wallet', - 'p2p_dns_seeds.py', 'wallet_abandonconflict.py --descriptors', - 'feature_csv_activation.py', - 'wallet_address_types.py --legacy-wallet', - 'wallet_address_types.py --descriptors', - 'feature_bip68_sequence.py', - 'p2p_feefilter.py', - 'rpc_packages.py', 'feature_reindex.py', - 'feature_abortnode.py', - # vv Tests less than 30s vv + 'wallet_labels.py --legacy-wallet', + 'wallet_labels.py --descriptors', + 'p2p_compactblocks.py', + 'p2p_compactblocks_blocksonly.py', + 'wallet_hd.py --legacy-wallet', + 'wallet_hd.py --descriptors', 'wallet_keypool_topup.py --legacy-wallet', 'wallet_keypool_topup.py --descriptors', 'wallet_fast_rescan.py --descriptors', - 'feature_fee_estimation.py', 'interface_zmq.py', 'rpc_invalid_address_message.py', 'interface_bitcoin_cli.py --legacy-wallet', @@ -157,20 +183,12 @@ BASE_SCRIPTS = [ 'rpc_misc.py', 'interface_rest.py', 'mempool_spend_coinbase.py', - 'wallet_avoidreuse.py --legacy-wallet', - 'wallet_avoidreuse.py --descriptors', 'wallet_avoid_mixing_output_types.py --descriptors', 'mempool_reorg.py', - 'mempool_persist.py --descriptors', 'p2p_block_sync.py', - 'wallet_multiwallet.py --legacy-wallet', - 'wallet_multiwallet.py --descriptors', - 'wallet_multiwallet.py --usecli', 'wallet_createwallet.py --legacy-wallet', 'wallet_createwallet.py --usecli', 'wallet_createwallet.py --descriptors', - 'wallet_listtransactions.py --legacy-wallet', - 'wallet_listtransactions.py --descriptors', 'wallet_watchonly.py --legacy-wallet', 'wallet_watchonly.py --usecli --legacy-wallet', 'wallet_reorgsrestore.py', @@ -180,8 +198,6 @@ BASE_SCRIPTS = [ 'interface_usdt_net.py', 'interface_usdt_utxocache.py', 'interface_usdt_validation.py', - 'rpc_psbt.py --legacy-wallet', - 'rpc_psbt.py --descriptors', 'rpc_users.py', 'rpc_whitelist.py', 'feature_proxy.py', @@ -189,13 +205,10 @@ BASE_SCRIPTS = [ 'wallet_signrawtransactionwithwallet.py --legacy-wallet', 'wallet_signrawtransactionwithwallet.py --descriptors', 'rpc_signrawtransactionwithkey.py', - 'p2p_headers_sync_with_minchainwork.py', 'rpc_rawtransaction.py --legacy-wallet', - 'wallet_groups.py --legacy-wallet', 'wallet_transactiontime_rescan.py --descriptors', 'wallet_transactiontime_rescan.py --legacy-wallet', 'p2p_addrv2_relay.py', - 'wallet_groups.py --descriptors', 'p2p_compactblocks_hb.py', 'p2p_disconnect_ban.py', 'rpc_decodescript.py', @@ -211,7 +224,6 @@ BASE_SCRIPTS = [ 'wallet_keypool.py --descriptors', 'wallet_descriptor.py --descriptors', 'wallet_miniscript.py --descriptors', - 'feature_maxtipage.py', 'p2p_nobloomfilter_messages.py', 'p2p_filter.py', 'rpc_setban.py', @@ -219,9 +231,7 @@ BASE_SCRIPTS = [ 'mining_prioritisetransaction.py', 'p2p_invalid_locator.py', 'p2p_invalid_block.py', - 'p2p_invalid_messages.py', 'p2p_invalid_tx.py', - 'feature_assumevalid.py', 'example_test.py', 'wallet_txn_doublespend.py --legacy-wallet', 'wallet_multisig_descriptor_psbt.py --descriptors', @@ -237,7 +247,6 @@ BASE_SCRIPTS = [ 'feature_rbf.py', 'mempool_packages.py', 'mempool_package_onemore.py', - 'rpc_createmultisig.py', 'mempool_package_limits.py', 'feature_versionbits_warning.py', 'rpc_preciousblock.py', @@ -254,18 +263,12 @@ BASE_SCRIPTS = [ 'feature_nulldummy.py', 'mempool_accept.py', 'mempool_expiry.py', - 'wallet_import_rescan.py --legacy-wallet', 'wallet_import_with_label.py --legacy-wallet', 'wallet_importdescriptors.py --descriptors', 'wallet_upgradewallet.py --legacy-wallet', - 'rpc_bind.py --ipv4', - 'rpc_bind.py --ipv6', - 'rpc_bind.py --nonloopback', 'wallet_crosschain.py', 'mining_basic.py', 'feature_signet.py', - 'wallet_bumpfee.py --legacy-wallet', - 'wallet_bumpfee.py --descriptors', 'wallet_implicitsegwit.py --legacy-wallet', 'rpc_named_arguments.py', 'feature_startupnotify.py', @@ -296,7 +299,6 @@ BASE_SCRIPTS = [ 'wallet_sendall.py --legacy-wallet', 'wallet_sendall.py --descriptors', 'wallet_create_tx.py --descriptors', - 'wallet_taproot.py --descriptors', 'wallet_inactive_hdchains.py --legacy-wallet', 'p2p_fingerprint.py', 'feature_uacomment.py', @@ -309,7 +311,6 @@ BASE_SCRIPTS = [ 'p2p_add_connections.py', 'feature_bind_port_discover.py', 'p2p_unrequested_blocks.py', - 'p2p_blockfilters.py', 'p2p_message_capture.py', 'feature_includeconf.py', 'feature_addrman.py', diff --git a/test/functional/wallet_import_rescan.py b/test/functional/wallet_import_rescan.py index 64f8f2eb6e..4dc39e83f8 100755 --- a/test/functional/wallet_import_rescan.py +++ b/test/functional/wallet_import_rescan.py @@ -179,7 +179,16 @@ class ImportRescanTest(BitcoinTestFramework): # Create one transaction on node 0 with a unique amount for # each possible type of wallet import RPC. + last_variants = [] for i, variant in enumerate(IMPORT_VARIANTS): + if i % 10 == 0: + blockhash = self.generate(self.nodes[0], 1)[0] + conf_height = self.nodes[0].getblockcount() + timestamp = self.nodes[0].getblockheader(blockhash)["time"] + for var in last_variants: + var.confirmation_height = conf_height + var.timestamp = timestamp + last_variants.clear() variant.label = "label {} {}".format(i, variant) variant.address = self.nodes[1].getaddressinfo(self.nodes[1].getnewaddress( label=variant.label, @@ -188,9 +197,15 @@ class ImportRescanTest(BitcoinTestFramework): variant.key = self.nodes[1].dumpprivkey(variant.address["address"]) variant.initial_amount = get_rand_amount() variant.initial_txid = self.nodes[0].sendtoaddress(variant.address["address"], variant.initial_amount) - self.generate(self.nodes[0], 1) # Generate one block for each send - variant.confirmation_height = self.nodes[0].getblockcount() - variant.timestamp = self.nodes[0].getblockheader(self.nodes[0].getbestblockhash())["time"] + last_variants.append(variant) + + blockhash = self.generate(self.nodes[0], 1)[0] + conf_height = self.nodes[0].getblockcount() + timestamp = self.nodes[0].getblockheader(blockhash)["time"] + for var in last_variants: + var.confirmation_height = conf_height + var.timestamp = timestamp + last_variants.clear() # Generate a block further in the future (past the rescan window). assert_equal(self.nodes[0].getrawmempool(), []) @@ -217,11 +232,14 @@ class ImportRescanTest(BitcoinTestFramework): variant.check() # Create new transactions sending to each address. - for variant in IMPORT_VARIANTS: + for i, variant in enumerate(IMPORT_VARIANTS): + if i % 10 == 0: + blockhash = self.generate(self.nodes[0], 1)[0] + conf_height = self.nodes[0].getblockcount() + 1 variant.sent_amount = get_rand_amount() variant.sent_txid = self.nodes[0].sendtoaddress(variant.address["address"], variant.sent_amount) - self.generate(self.nodes[0], 1) # Generate one block for each send - variant.confirmation_height = self.nodes[0].getblockcount() + variant.confirmation_height = conf_height + self.generate(self.nodes[0], 1) assert_equal(self.nodes[0].getrawmempool(), []) self.sync_all() diff --git a/test/functional/wallet_keypool.py b/test/functional/wallet_keypool.py index aa31757f35..8b485a9324 100755 --- a/test/functional/wallet_keypool.py +++ b/test/functional/wallet_keypool.py @@ -204,6 +204,9 @@ class KeyPoolTest(BitcoinTestFramework): res = w2.walletcreatefundedpsbt(inputs=[], outputs=[{destination: 0.00010000}], options={"subtractFeeFromOutputs": [0], "feeRate": 0.00010, "changeAddress": addr.pop()}) assert_equal("psbt" in res, True) + if not self.options.descriptors: + msg = "Error: Private keys are disabled for this wallet" + assert_raises_rpc_error(-4, msg, w2.keypoolrefill, 100) if __name__ == '__main__': KeyPoolTest().main() diff --git a/test/functional/wallet_migration.py b/test/functional/wallet_migration.py index 37625e50d8..2997372bab 100755 --- a/test/functional/wallet_migration.py +++ b/test/functional/wallet_migration.py @@ -40,11 +40,13 @@ class WalletMigrationTest(BitcoinTestFramework): assert_equal(file_magic, b'SQLite format 3\x00') assert_equal(self.nodes[0].get_wallet_rpc(wallet_name).getwalletinfo()["format"], "sqlite") - def create_legacy_wallet(self, wallet_name): - self.nodes[0].createwallet(wallet_name=wallet_name) + def create_legacy_wallet(self, wallet_name, disable_private_keys=False): + self.nodes[0].createwallet(wallet_name=wallet_name, descriptors=False, disable_private_keys=disable_private_keys) wallet = self.nodes[0].get_wallet_rpc(wallet_name) - assert_equal(wallet.getwalletinfo()["descriptors"], False) - assert_equal(wallet.getwalletinfo()["format"], "bdb") + info = wallet.getwalletinfo() + assert_equal(info["descriptors"], False) + assert_equal(info["format"], "bdb") + assert_equal(info["private_keys_enabled"], not disable_private_keys) return wallet def assert_addr_info_equal(self, addr_info, addr_info_old): @@ -187,11 +189,9 @@ class WalletMigrationTest(BitcoinTestFramework): # Some keys in multisig do not belong to this wallet self.log.info("Test migration of a wallet that has some keys in a multisig") - self.nodes[0].createwallet(wallet_name="multisig1") - multisig1 = self.nodes[0].get_wallet_rpc("multisig1") + multisig1 = self.create_legacy_wallet("multisig1") ms_info = multisig1.addmultisigaddress(2, [multisig1.getnewaddress(), pub1, pub2]) ms_info2 = multisig1.addmultisigaddress(2, [multisig1.getnewaddress(), pub1, pub2]) - assert_equal(multisig1.getwalletinfo()["descriptors"], False) addr1 = ms_info["address"] addr2 = ms_info2["address"] @@ -256,9 +256,7 @@ class WalletMigrationTest(BitcoinTestFramework): # Wallet with an imported address. Should be the same thing as the multisig test self.log.info("Test migration of a wallet with watchonly imports") - self.nodes[0].createwallet(wallet_name="imports0") - imports0 = self.nodes[0].get_wallet_rpc("imports0") - assert_equal(imports0.getwalletinfo()["descriptors"], False) + imports0 = self.create_legacy_wallet("imports0") # Exteranl address label imports0.setlabel(default.getnewaddress(), "external") @@ -318,11 +316,7 @@ class WalletMigrationTest(BitcoinTestFramework): # Migrating an actual watchonly wallet should not create a new watchonly wallet self.log.info("Test migration of a pure watchonly wallet") - self.nodes[0].createwallet(wallet_name="watchonly0", disable_private_keys=True) - watchonly0 = self.nodes[0].get_wallet_rpc("watchonly0") - info = watchonly0.getwalletinfo() - assert_equal(info["descriptors"], False) - assert_equal(info["private_keys_enabled"], False) + watchonly0 = self.create_legacy_wallet("watchonly0", disable_private_keys=True) addr = default.getnewaddress() desc = default.getaddressinfo(addr)["desc"] @@ -345,11 +339,7 @@ class WalletMigrationTest(BitcoinTestFramework): # Migrating a wallet with pubkeys added to the keypool self.log.info("Test migration of a pure watchonly wallet with pubkeys in keypool") - self.nodes[0].createwallet(wallet_name="watchonly1", disable_private_keys=True) - watchonly1 = self.nodes[0].get_wallet_rpc("watchonly1") - info = watchonly1.getwalletinfo() - assert_equal(info["descriptors"], False) - assert_equal(info["private_keys_enabled"], False) + watchonly1 = self.create_legacy_wallet("watchonly1", disable_private_keys=True) addr1 = default.getnewaddress(address_type="bech32") addr2 = default.getnewaddress(address_type="bech32") diff --git a/test/functional/wallet_pruning.py b/test/functional/wallet_pruning.py new file mode 100755 index 0000000000..6d8475ce8d --- /dev/null +++ b/test/functional/wallet_pruning.py @@ -0,0 +1,158 @@ +#!/usr/bin/env python3 +# Copyright (c) 2022 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +"""Test wallet import on pruned node.""" +import os + +from test_framework.util import assert_equal, assert_raises_rpc_error +from test_framework.blocktools import ( + COINBASE_MATURITY, + create_block +) +from test_framework.blocktools import create_coinbase +from test_framework.test_framework import BitcoinTestFramework + +from test_framework.script import ( + CScript, + OP_RETURN, + OP_TRUE, +) + +class WalletPruningTest(BitcoinTestFramework): + def add_options(self, parser): + self.add_wallet_options(parser, descriptors=False) + + def set_test_params(self): + self.setup_clean_chain = True + self.num_nodes = 2 + self.wallet_names = [] + self.extra_args = [ + [], # node dedicated to mining + ['-prune=550'], # node dedicated to testing pruning + ] + + def skip_test_if_missing_module(self): + self.skip_if_no_wallet() + self.skip_if_no_bdb() + + def mine_large_blocks(self, node, n): + # Get the block parameters for the first block + best_block = node.getblock(node.getbestblockhash()) + height = int(best_block["height"]) + 1 + self.nTime = max(self.nTime, int(best_block["time"])) + 1 + previousblockhash = int(best_block["hash"], 16) + big_script = CScript([OP_RETURN] + [OP_TRUE] * 950000) + for _ in range(n): + block = create_block(hashprev=previousblockhash, ntime=self.nTime, coinbase=create_coinbase(height, script_pubkey=big_script)) + block.solve() + + # Submit to the node + node.submitblock(block.serialize().hex()) + + previousblockhash = block.sha256 + height += 1 + + # Simulate 10 minutes of work time per block + # Important for matching a timestamp with a block +- some window + self.nTime += 600 + for n in self.nodes: + if n.running: + n.setmocktime(self.nTime) # Update node's time to accept future blocks + self.sync_all() + + def test_wallet_import_pruned(self, wallet_name): + self.log.info("Make sure we can import wallet when pruned and required blocks are still available") + + wallet_file = wallet_name + ".dat" + wallet_birthheight = self.get_birthheight(wallet_file) + + # Verify that the block at wallet's birthheight is available at the pruned node + self.nodes[1].getblock(self.nodes[1].getblockhash(wallet_birthheight)) + + # Import wallet into pruned node + self.nodes[1].createwallet(wallet_name="wallet_pruned", descriptors=False, load_on_startup=True) + self.nodes[1].importwallet(os.path.join(self.nodes[0].datadir, wallet_file)) + + # Make sure that prune node's wallet correctly accounts for balances + assert_equal(self.nodes[1].getbalance(), self.nodes[0].getbalance()) + + self.log.info("- Done") + + def test_wallet_import_pruned_with_missing_blocks(self, wallet_name): + self.log.info("Make sure we cannot import wallet when pruned and required blocks are not available") + + wallet_file = wallet_name + ".dat" + wallet_birthheight = self.get_birthheight(wallet_file) + + # Verify that the block at wallet's birthheight is not available at the pruned node + assert_raises_rpc_error(-1, "Block not available (pruned data)", self.nodes[1].getblock, self.nodes[1].getblockhash(wallet_birthheight)) + + # Make sure wallet cannot be imported because of missing blocks + # This will try to rescan blocks `TIMESTAMP_WINDOW` (2h) before the wallet birthheight. + # There are 6 blocks an hour, so 11 blocks (excluding birthheight). + assert_raises_rpc_error(-4, f"Pruned blocks from height {wallet_birthheight - 11} required to import keys. Use RPC call getblockchaininfo to determine your pruned height.", self.nodes[1].importwallet, os.path.join(self.nodes[0].datadir, wallet_file)) + self.log.info("- Done") + + def get_birthheight(self, wallet_file): + """Gets birthheight of a wallet on node0""" + with open(os.path.join(self.nodes[0].datadir, wallet_file), 'r', encoding="utf8") as f: + for line in f: + if line.startswith('# * Best block at time of backup'): + wallet_birthheight = int(line.split(' ')[9]) + return wallet_birthheight + + def has_block(self, block_index): + """Checks if the pruned node has the specific blk0000*.dat file""" + return os.path.isfile(os.path.join(self.nodes[1].datadir, self.chain, "blocks", f"blk{block_index:05}.dat")) + + def create_wallet(self, wallet_name, *, unload=False): + """Creates and dumps a wallet on the non-pruned node0 to be later import by the pruned node""" + self.nodes[0].createwallet(wallet_name=wallet_name, descriptors=False, load_on_startup=True) + self.nodes[0].dumpwallet(os.path.join(self.nodes[0].datadir, wallet_name + ".dat")) + if (unload): + self.nodes[0].unloadwallet(wallet_name) + + def run_test(self): + self.nTime = 0 + self.log.info("Warning! This test requires ~1.3GB of disk space") + + self.log.info("Generating a long chain of blocks...") + + # A blk*.dat file is 128MB + # Generate 250 light blocks + self.generate(self.nodes[0], 250, sync_fun=self.no_op) + # Generate 50MB worth of large blocks in the blk00000.dat file + self.mine_large_blocks(self.nodes[0], 50) + + # Create a wallet which birth's block is in the blk00000.dat file + wallet_birthheight_1 = "wallet_birthheight_1" + assert_equal(self.has_block(1), False) + self.create_wallet(wallet_birthheight_1, unload=True) + + # Generate enough large blocks to reach pruning disk limit + # Not pruning yet because we are still below PruneAfterHeight + self.mine_large_blocks(self.nodes[0], 600) + self.log.info("- Long chain created") + + # Create a wallet with birth height > wallet_birthheight_1 + wallet_birthheight_2 = "wallet_birthheight_2" + self.create_wallet(wallet_birthheight_2) + + # Fund wallet to later verify that importwallet correctly accounts for balances + self.generatetoaddress(self.nodes[0], COINBASE_MATURITY + 1, self.nodes[0].getnewaddress(), sync_fun=self.no_op) + + # We've reached pruning storage & height limit but + # pruning doesn't run until another chunk (blk*.dat file) is allocated. + # That's why we are generating another 5 large blocks + self.mine_large_blocks(self.nodes[0], 5) + + # blk00000.dat file is now pruned from node1 + assert_equal(self.has_block(0), False) + + self.test_wallet_import_pruned(wallet_birthheight_2) + self.test_wallet_import_pruned_with_missing_blocks(wallet_birthheight_1) + +if __name__ == '__main__': + WalletPruningTest().main() diff --git a/test/functional/wallet_signer.py b/test/functional/wallet_signer.py index 9ab7154424..0b63ba8491 100755 --- a/test/functional/wallet_signer.py +++ b/test/functional/wallet_signer.py @@ -13,6 +13,7 @@ import platform from test_framework.test_framework import BitcoinTestFramework from test_framework.util import ( assert_equal, + assert_greater_than, assert_raises_rpc_error, ) @@ -169,7 +170,7 @@ class WalletSignerTest(BitcoinTestFramework): assert_equal(result[1], {'success': True}) assert_equal(mock_wallet.getwalletinfo()["txcount"], 1) dest = self.nodes[0].getnewaddress(address_type='bech32') - mock_psbt = mock_wallet.walletcreatefundedpsbt([], {dest:0.5}, 0, {}, True)['psbt'] + mock_psbt = mock_wallet.walletcreatefundedpsbt([], {dest:0.5}, 0, {'replaceable': True}, True)['psbt'] mock_psbt_signed = mock_wallet.walletprocesspsbt(psbt=mock_psbt, sign=True, sighashtype="ALL", bip32derivs=True) mock_psbt_final = mock_wallet.finalizepsbt(mock_psbt_signed["psbt"]) mock_tx = mock_psbt_final["hex"] @@ -209,6 +210,7 @@ class WalletSignerTest(BitcoinTestFramework): self.log.info('Test send using hww1') + # Don't broadcast transaction yet so the RPC returns the raw hex res = hww.send(outputs={dest:0.5},options={"add_to_wallet": False}) assert(res["complete"]) assert_equal(res["hex"], mock_tx) @@ -218,6 +220,25 @@ class WalletSignerTest(BitcoinTestFramework): res = hww.sendall(recipients=[{dest:0.5}, hww.getrawchangeaddress()],options={"add_to_wallet": False}) assert(res["complete"]) assert_equal(res["hex"], mock_tx) + # Broadcast transaction so we can bump the fee + hww.sendrawtransaction(res["hex"]) + + self.log.info('Prepare fee bumped mock PSBT') + + # Now that the transaction is broadcast, bump fee in mock wallet: + orig_tx_id = res["txid"] + mock_psbt_bumped = mock_wallet.psbtbumpfee(orig_tx_id)["psbt"] + mock_psbt_bumped_signed = mock_wallet.walletprocesspsbt(psbt=mock_psbt_bumped, sign=True, sighashtype="ALL", bip32derivs=True) + + with open(os.path.join(self.nodes[1].cwd, "mock_psbt"), "w", encoding="utf8") as f: + f.write(mock_psbt_bumped_signed["psbt"]) + + self.log.info('Test bumpfee using hww1') + + # Bump fee + res = hww.bumpfee(orig_tx_id) + assert_greater_than(res["fee"], res["origfee"]) + assert_equal(res["errors"], []) # # Handle error thrown by script # self.set_mock_result(self.nodes[4], "2") diff --git a/test/lint/check-doc.py b/test/lint/check-doc.py index feaebc68e9..7955dd4f3e 100755 --- a/test/lint/check-doc.py +++ b/test/lint/check-doc.py @@ -15,7 +15,7 @@ import re FOLDER_GREP = 'src' FOLDER_TEST = 'src/test/' -REGEX_ARG = r'(?:ForceSet|SoftSet|Get|Is)(?:Bool)?Args?(?:Set)?\("(-[^"]+)"' +REGEX_ARG = r'\b(?:GetArg|GetArgs|GetBoolArg|GetIntArg|GetPathArg|IsArgSet|get_net)\("(-[^"]+)"' REGEX_DOC = r'AddArg\("(-[^"=]+?)(?:=|")' CMD_ROOT_DIR = '$(git rev-parse --show-toplevel)/{}'.format(FOLDER_GREP) CMD_GREP_ARGS = r"git grep --perl-regexp '{}' -- {} ':(exclude){}'".format(REGEX_ARG, CMD_ROOT_DIR, FOLDER_TEST) diff --git a/test/lint/spelling.ignore-words.txt b/test/lint/spelling.ignore-words.txt index 82f18010c1..d44dd70684 100644 --- a/test/lint/spelling.ignore-words.txt +++ b/test/lint/spelling.ignore-words.txt @@ -4,6 +4,7 @@ blockin bu cachable clen +crypted fo fpr hights |