diff options
Diffstat (limited to 'test')
209 files changed, 2444 insertions, 1271 deletions
diff --git a/test/functional/data/invalid_txs.py b/test/functional/data/invalid_txs.py index 3747b2a98d..33054fd517 100644 --- a/test/functional/data/invalid_txs.py +++ b/test/functional/data/invalid_txs.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2021 The Bitcoin Core developers +# Copyright (c) 2015-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. """ @@ -46,18 +46,19 @@ from test_framework.script import ( OP_MOD, OP_MUL, OP_OR, + OP_RETURN, OP_RIGHT, OP_RSHIFT, OP_SUBSTR, - OP_TRUE, OP_XOR, ) from test_framework.script_util import ( + MIN_PADDING, + MIN_STANDARD_TX_NONWITNESS_SIZE, script_to_p2sh_script, ) basic_p2sh = script_to_p2sh_script(CScript([OP_0])) - class BadTxTemplate: """Allows simple construction of a certain kind of invalid tx. Base class to be subclassed.""" __metaclass__ = abc.ABCMeta @@ -122,7 +123,9 @@ class SizeTooSmall(BadTxTemplate): def get_tx(self): tx = CTransaction() tx.vin.append(self.valid_txin) - tx.vout.append(CTxOut(0, CScript([OP_TRUE]))) + tx.vout.append(CTxOut(0, CScript([OP_RETURN] + ([OP_0] * (MIN_PADDING - 2))))) + assert len(tx.serialize_without_witness()) == 64 + assert MIN_STANDARD_TX_NONWITNESS_SIZE - 1 == 64 tx.calc_sha256() return tx diff --git a/test/functional/data/rpc_getblockstats.json b/test/functional/data/rpc_getblockstats.json index 16dbc5fe60..7d7460aacc 100644 --- a/test/functional/data/rpc_getblockstats.json +++ b/test/functional/data/rpc_getblockstats.json @@ -102,8 +102,8 @@ "00000020f44e7a48b9f221af95f3295c8dcefc5358934a68dc79e2933dc0794b350cad0a90fad2cd50b41d4ef45e76c2a456b98c180632bb4b44e0cd18ce90679fe54e552b4ae75affff7f200000000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401630101ffffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000", "0000002087454276cce83f4d19e0120f6e9728ac5905f7adaf6b27e3f5bbe43ab823f85db7d1f44666531483df3d67c15f2c231718ad93b63b851dce5ff4c4a67f524ffa2b4ae75affff7f200100000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401640101ffffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000", "000000202cdc3e99f07a80252dd6097faa0eddf3f2dde5ae390610e0bca94ecc25931551d31fceb8fe0a682f6017ca3dbb582f3a2f06e5d99ec99c42c8a744dd4c9216b82b4ae75affff7f200300000001020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401650101ffffffff0200f2052a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000", - "000000209b3ace9bd510918d20e87518c0cf5976cab3e28cc7af41259a89c6dd7668a32922808b8a082be71bcd6152cb8fd223650b5579a41344ba749e4d17b9bf211a9e2b4ae75affff7f200000000002020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401660101ffffffff026c03062a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9edb85d8f3c122c43a72f1e0dd122c8f7af040aa0b0a46001621110fb37818021510120000000000000000000000000000000000000000000000000000000000000000000000000020000000128394022bf44bff30d7399cb5a16e3b94fed67dc174c2e1d77df91bad5a51cb3000000006a47304402201c16d06a5c4353168b3881071aea7d1eb4d88eedfea53a9d6af9abb56da9060002205abf3ae535f1f1b5cfe8ba955535c2b20ac003e7d7720c5b7d2640ac2a04d19001210227d85ba011276cf25b51df6a188b75e604b38770a462b2d0e9fb2fc839ef5d3ffeffffff0294b89a3b000000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac00286bee0000000017a91452bab4f229415d0dc5c6d30b162f93a1a0cac5958765000000", - "000000200fa168b50a79ad24378a6b0f96e4c9f4ccb657a2663320d5fc1efd8ee7caa10ab42a31c444f2153387530a0979d4dc3dcc134b394c821227b8abff930c03c8412b4ae75affff7f200200000004020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401670101ffffffff02e015072a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ed20376d4bc90f9c689850eec3603cda658ba6295241730473ceb0e970b8d594150120000000000000000000000000000000000000000000000000000000000000000000000000020000000191e549a6cc852bbf1d3f11144b1a34079f64305e6971d2e685d2b40cd386e8a6000000006a47304402200bf62021c0a9a47ced8eba1e0998f5c71b2950763198d83ad284bd791241dbb00220446a05b7c35e7458924de88a8dcccab1ec6a106aa005345e55b482d8eb66337301210227d85ba011276cf25b51df6a188b75e604b38770a462b2d0e9fb2fc839ef5d3ffeffffff02acdbf405000000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac94d7a4350000000017a914dfa6f0b17d2c64962c94203e744a9d4179ed22c18766000000020000000112d2f07672102dc6f099c4be308f598e4c4da1a7e0cb462ae14f0444525a1332000000006a47304402200a6a2f544f3f9d299608a7c745e2326de176fb1cac03ae3e74943f4250b8896e02205023a5b4faff99865bf91f1263605a502c723628be9240c0b7bec81d2ed106f101210227d85ba011276cf25b51df6a188b75e604b38770a462b2d0e9fb2fc839ef5d3ffeffffff0200ca9a3b000000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac94166bee0000000017a914152cc82f7944f5c416de7dbffb052f7081765d7987660000000200000000010191e549a6cc852bbf1d3f11144b1a34079f64305e6971d2e685d2b40cd386e8a601000000171600147cc872ad7350c37fecab9c4c6d9f08aceb53bdb8feffffff02005ed0b20000000017a914aab1c8c53fe62e283a53efa28097709f4f2ed37b87e0bc9a3b000000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0247304402201b4476f238ed5d515bfcd6927d0d008a4993770763eca73e3ee66f69971831d902200f5215a6dfd90391dd63462cfdf69804fe31224c309ec9c38d33a04dce71c0ee0121028c9d2955a95301b699db62e97d54bf0a91feb44e5cd94bbf5b62f1df57fb643966000000" + "000000209b3ace9bd510918d20e87518c0cf5976cab3e28cc7af41259a89c6dd7668a329f03ef4716ad5d88bccfd71088bf2ec3eb5b32e0ff60f35f9becd73052bfa8af12b4ae75affff7f200000000002020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401660101ffffffff025803062a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9edb11910e4e0ee6ee6d6bad42736999d1eba649243dc781438e5ef845c7227aaad0120000000000000000000000000000000000000000000000000000000000000000000000000020000000128394022bf44bff30d7399cb5a16e3b94fed67dc174c2e1d77df91bad5a51cb3000000006a47304402200650c6c50bd4952ca13b7aa37d458a36628434bbb968701063cdd36d0725e36c02202e059ccf7a4a049de028c4f140e543baa7e69ea3663e3d1fdfbc8ba7247e82f901210227d85ba011276cf25b51df6a188b75e604b38770a462b2d0e9fb2fc839ef5d3ffeffffff02a8b89a3b000000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac00286bee0000000016001423838e991caedd69289d9dac88ca423cca683a2265000000", + "0000002061597bf3305ee2334b5d7fec136c2064b69d14955b50cd0b81f49ac33d88e506d80731ce60c2f275d29539a4d04c7e8c72aa4ade3c9baec24881a581fc524c6a2b4ae75affff7f200000000005020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401670101ffffffff023840072a010000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac0000000000000000266a24aa21a9ed89a3c65f40921b14168368cf4775a710ad17b613cedcff001f1f1ca3c863cc11012000000000000000000000000000000000000000000000000000000000000000000000000002000000000101bb475ac72ba1a45a96be584a689d6d0ace820d9587b0db53dc40a15555b9be770100000000feffffff02c0be9a3b000000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88ac005ed0b200000000160014a57f6871c70569e0543322614c5c85438b796a900247304402207893b5466cdbf9cdedcc73fb8e10e01c4dd9aac4345031ef0c35d91e82ff5fd602207f0e1d7e4270a532060fd1e6ad7881d18acb173fd4fd1d61f3c8ff6707bdb972012103bb4c79ca594b19bbec7ee6302af0ef4191345fa7f03a30ed4e042aeed680924b6600000002000000000101e6a996cbac10b2d6b2bb2796a4ebf639ee21b136e06c7d8c6bc62b7cb4a311870100000000feffffff0200ca9a3b000000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88acc088357700000000160014c48ea35298422796eb75ee9a04ebada66780083902473044022062963ff9f1f0d2c885060c083b97ddd67bd6e8a7daaed6578a62c1f905fd31a6022001836efe75bbce64d29700f5568aed78ce68f098ef0b8efdad0679e2f6c0ceb5012102228c4d459ce647b63b2681597a6e8391e4b5afab147f90747184a0893e4934c26600000002000000000101c654a35d86b4c3112ae9ec75e3bc271e0b8f928620386fdaf9517e8a9b511d850100000000feffffff024038f505000000001976a9142b4569203694fc997e13f2c0a1383b9e16c77a0d88acc0a73f7100000000160014cb1049e20cebfe756d657af0d47a3357c1db3c9702473044022024728eb27e884d08b2b95e670fd58c0e10106a18bc9e01f753f9ae474aa2c95e02201d5abe91302b373507dce1fd2f4b95ddc36e21d23eda6c476cc9efea80338ef90121036be02fd68622ce6740f0d028059af96a310c23c675f044732171df6f64f0fcb2660000000200000000010135c4ee1a780d8279ea7c79d3ad6fbb39315a13e6b51dd51f49db6e2f4796bada0100000000feffffff020000000000000000036a0121c8183f71000000001600147ea4d830ca77c20a23155a176a1472613dc4e7840247304402202db3527431e09ca8034d207134d79fc063939bd402286dd8b3872de3b2a746b402207faae5fb8838e6877822a9209fe2e81d4be4a69ce8164215e03f9b92d75e94f90121024d456d37df6f3436ee1685b78d90d2d24c4dd8c602d5f41405fe7f449b43415f00000000" ], "mocktime": 1525107225, "stats": [ @@ -142,13 +142,15 @@ "totalfee": 0, "txs": 1, "utxo_increase": 2, - "utxo_size_inc": 163 + "utxo_increase_actual": 1, + "utxo_size_inc": 163, + "utxo_size_inc_actual": 75 }, { - "avgfee": 4460, + "avgfee": 4440, "avgfeerate": 20, - "avgtxsize": 223, - "blockhash": "0aa1cae78efd1efcd5203366a257b6ccf4c9e4960f6b8a3724ad790ab568a10f", + "avgtxsize": 222, + "blockhash": "06e5883dc39af4810bcd505b95149db664206c13ec7f5d4b33e25e30f37b5961", "feerate_percentiles": [ 20, 20, @@ -158,65 +160,69 @@ ], "height": 102, "ins": 1, - "maxfee": 4460, + "maxfee": 4440, "maxfeerate": 20, - "maxtxsize": 223, - "medianfee": 4460, + "maxtxsize": 222, + "medianfee": 4440, "mediantime": 1525107242, - "mediantxsize": 223, - "minfee": 4460, + "mediantxsize": 222, + "minfee": 4440, "minfeerate": 20, - "mintxsize": 223, + "mintxsize": 222, "outs": 4, "subsidy": 5000000000, "swtotal_size": 0, "swtotal_weight": 0, "swtxs": 0, "time": 1525107243, - "total_out": 4999995540, - "total_size": 223, - "total_weight": 892, - "totalfee": 4460, + "total_out": 4999995560, + "total_size": 222, + "total_weight": 888, + "totalfee": 4440, "txs": 2, "utxo_increase": 3, - "utxo_size_inc": 236 + "utxo_increase_actual": 2, + "utxo_size_inc": 235, + "utxo_size_inc_actual": 147 }, { - "avgfee": 24906, - "avgfeerate": 121, - "avgtxsize": 231, - "blockhash": "53e416e2538bc783c42a7aea566e884321afed893e9e58cf356d6429759dfa46", + "avgfee": 21390, + "avgfeerate": 155, + "avgtxsize": 219, + "blockhash": "7474991c2ae3c94c4813d75b4c752028304b773dd4dce8d460dfa2d1e7b542a3", "feerate_percentiles": [ 20, 20, 20, - 300, - 300 + 301, + 301 ], "height": 103, - "ins": 3, - "maxfee": 66900, - "maxfeerate": 300, - "maxtxsize": 249, - "medianfee": 4460, + "ins": 4, + "maxfee": 43200, + "maxfeerate": 301, + "maxtxsize": 225, + "medianfee": 19740, "mediantime": 1525107243, - "mediantxsize": 223, - "minfee": 3360, + "mediantxsize": 225, + "minfee": 2880, "minfeerate": 20, - "mintxsize": 223, - "outs": 8, + "mintxsize": 203, + "outs": 10, "subsidy": 5000000000, - "swtotal_size": 249, - "swtotal_weight": 669, - "swtxs": 1, + "swtotal_size": 878, + "swtotal_weight": 2204, + "swtxs": 4, "time": 1525107243, - "total_out": 9999920820, - "total_size": 695, - "total_weight": 2453, - "totalfee": 74720, - "txs": 4, - "utxo_increase": 5, - "utxo_size_inc": 384 + "total_out": 10899908680, + "total_size": 878, + "total_weight": 2204, + "totalfee": 85560, + "txs": 5, + "utxo_increase": 6, + "utxo_increase_actual": 4, + "utxo_size_inc": 441, + "utxo_size_inc_actual": 300 } ] }
\ No newline at end of file diff --git a/test/functional/example_test.py b/test/functional/example_test.py index 9cf756060e..7f7aa065ad 100755 --- a/test/functional/example_test.py +++ b/test/functional/example_test.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2017-2021 The Bitcoin Core developers +# Copyright (c) 2017-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. """An example functional test @@ -79,6 +79,9 @@ class ExampleTest(BitcoinTestFramework): # Override the set_test_params(), skip_test_if_missing_module(), add_options(), setup_chain(), setup_network() # and setup_nodes() methods to customize the test setup as required. + def add_options(self, parser): + self.add_wallet_options(parser) + def set_test_params(self): """Override test parameters for your individual test. diff --git a/test/functional/feature_addrman.py b/test/functional/feature_addrman.py index 63abf0d9f8..28c3880513 100755 --- a/test/functional/feature_addrman.py +++ b/test/functional/feature_addrman.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2021 The Bitcoin Core developers +# Copyright (c) 2021-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 addrman functionality""" diff --git a/test/functional/feature_assumevalid.py b/test/functional/feature_assumevalid.py index 67cacaa9ce..36ee79dab9 100755 --- a/test/functional/feature_assumevalid.py +++ b/test/functional/feature_assumevalid.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2014-2021 The Bitcoin Core developers +# Copyright (c) 2014-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 logic for skipping signature validation on old blocks. @@ -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_bind_extra.py b/test/functional/feature_bind_extra.py index 5de9ff203c..4a94d2ce7b 100755 --- a/test/functional/feature_bind_extra.py +++ b/test/functional/feature_bind_extra.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2014-2021 The Bitcoin Core developers +# Copyright (c) 2014-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. """ diff --git a/test/functional/feature_bip68_sequence.py b/test/functional/feature_bip68_sequence.py index 5b43fe4f8e..894afffc79 100755 --- a/test/functional/feature_bip68_sequence.py +++ b/test/functional/feature_bip68_sequence.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2014-2021 The Bitcoin Core developers +# Copyright (c) 2014-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 BIP68 implementation.""" @@ -32,6 +32,7 @@ from test_framework.util import ( assert_raises_rpc_error, softfork_active, ) +from test_framework.wallet import MiniWallet SCRIPT_W0_SH_OP_TRUE = script_to_p2wsh_script(CScript([OP_TRUE])) @@ -44,6 +45,9 @@ SEQUENCE_LOCKTIME_MASK = 0x0000ffff NOT_FINAL_ERROR = "non-BIP68-final" class BIP68Test(BitcoinTestFramework): + def add_options(self, parser): + self.add_wallet_options(parser) + def set_test_params(self): self.num_nodes = 2 self.extra_args = [ @@ -55,14 +59,9 @@ class BIP68Test(BitcoinTestFramework): ], ] - def skip_test_if_missing_module(self): - self.skip_if_no_wallet() - def run_test(self): self.relayfee = self.nodes[0].getnetworkinfo()["relayfee"] - - # Generate some coins - self.generate(self.nodes[0], 110) + self.wallet = MiniWallet(self.nodes[0]) self.log.info("Running test disable flag") self.test_disable_flag() @@ -89,16 +88,10 @@ class BIP68Test(BitcoinTestFramework): # the first sequence bit is set. def test_disable_flag(self): # Create some unconfirmed inputs - new_addr = self.nodes[0].getnewaddress() - self.nodes[0].sendtoaddress(new_addr, 2) # send 2 BTC - - utxos = self.nodes[0].listunspent(0, 0) - assert len(utxos) > 0 - - utxo = utxos[0] + utxo = self.wallet.send_self_transfer(from_node=self.nodes[0])["new_utxo"] tx1 = CTransaction() - value = int((utxo["amount"] - self.relayfee) * COIN) + value = int((utxo["value"] - self.relayfee) * COIN) # Check that the disable flag disables relative locktime. # If sequence locks were used, this would require 1 block for the @@ -107,8 +100,8 @@ class BIP68Test(BitcoinTestFramework): tx1.vin = [CTxIn(COutPoint(int(utxo["txid"], 16), utxo["vout"]), nSequence=sequence_value)] tx1.vout = [CTxOut(value, SCRIPT_W0_SH_OP_TRUE)] - tx1_signed = self.nodes[0].signrawtransactionwithwallet(tx1.serialize().hex())["hex"] - tx1_id = self.nodes[0].sendrawtransaction(tx1_signed) + self.wallet.sign_tx(tx=tx1) + tx1_id = self.wallet.sendrawtransaction(from_node=self.nodes[0], tx_hex=tx1.serialize().hex()) tx1_id = int(tx1_id, 16) # This transaction will enable sequence-locks, so this transaction should @@ -122,13 +115,13 @@ class BIP68Test(BitcoinTestFramework): tx2.vout = [CTxOut(int(value - self.relayfee * COIN), SCRIPT_W0_SH_OP_TRUE)] tx2.rehash() - assert_raises_rpc_error(-26, NOT_FINAL_ERROR, self.nodes[0].sendrawtransaction, tx2.serialize().hex()) + assert_raises_rpc_error(-26, NOT_FINAL_ERROR, self.wallet.sendrawtransaction, from_node=self.nodes[0], tx_hex=tx2.serialize().hex()) # Setting the version back down to 1 should disable the sequence lock, # so this should be accepted. tx2.nVersion = 1 - self.nodes[0].sendrawtransaction(tx2.serialize().hex()) + self.wallet.sendrawtransaction(from_node=self.nodes[0], tx_hex=tx2.serialize().hex()) # Calculate the median time past of a prior block ("confirmations" before # the current tip). @@ -141,20 +134,13 @@ class BIP68Test(BitcoinTestFramework): # Create lots of confirmed utxos, and use them to generate lots of random # transactions. max_outputs = 50 - addresses = [] - while len(addresses) < max_outputs: - addresses.append(self.nodes[0].getnewaddress()) - while len(self.nodes[0].listunspent()) < 200: + while len(self.wallet.get_utxos(include_immature_coinbase=False, mark_as_spent=False)) < 200: import random - random.shuffle(addresses) num_outputs = random.randint(1, max_outputs) - outputs = {} - for i in range(num_outputs): - outputs[addresses[i]] = random.randint(1, 20)*0.01 - self.nodes[0].sendmany("", outputs) - self.generate(self.nodes[0], 1) + self.wallet.send_self_transfer_multi(from_node=self.nodes[0], num_outputs=num_outputs) + self.generate(self.wallet, 1) - utxos = self.nodes[0].listunspent() + utxos = self.wallet.get_utxos(include_immature_coinbase=False) # Try creating a lot of random transactions. # Each time, choose a random number of inputs, and randomly set @@ -211,19 +197,20 @@ class BIP68Test(BitcoinTestFramework): sequence_value = ((cur_time - orig_time) >> SEQUENCE_LOCKTIME_GRANULARITY)+1 sequence_value |= SEQUENCE_LOCKTIME_TYPE_FLAG tx.vin.append(CTxIn(COutPoint(int(utxos[j]["txid"], 16), utxos[j]["vout"]), nSequence=sequence_value)) - value += utxos[j]["amount"]*COIN + value += utxos[j]["value"]*COIN # Overestimate the size of the tx - signatures should be less than 120 bytes, and leave 50 for the output tx_size = len(tx.serialize().hex())//2 + 120*num_inputs + 50 tx.vout.append(CTxOut(int(value - self.relayfee * tx_size * COIN / 1000), SCRIPT_W0_SH_OP_TRUE)) - rawtx = self.nodes[0].signrawtransactionwithwallet(tx.serialize().hex())["hex"] + self.wallet.sign_tx(tx=tx) if (using_sequence_locks and not should_pass): # This transaction should be rejected - assert_raises_rpc_error(-26, NOT_FINAL_ERROR, self.nodes[0].sendrawtransaction, rawtx) + assert_raises_rpc_error(-26, NOT_FINAL_ERROR, self.wallet.sendrawtransaction, from_node=self.nodes[0], tx_hex=tx.serialize().hex()) else: # This raw transaction should be accepted - self.nodes[0].sendrawtransaction(rawtx) - utxos = self.nodes[0].listunspent() + self.wallet.sendrawtransaction(from_node=self.nodes[0], tx_hex=tx.serialize().hex()) + self.wallet.rescan_utxos() + utxos = self.wallet.get_utxos(include_immature_coinbase=False) # Test that sequence locks on unconfirmed inputs must have nSequence # height or time of 0 to be accepted. @@ -234,8 +221,8 @@ class BIP68Test(BitcoinTestFramework): cur_height = self.nodes[0].getblockcount() # Create a mempool tx. - txid = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 2) - tx1 = tx_from_hex(self.nodes[0].getrawtransaction(txid)) + self.wallet.rescan_utxos() + tx1 = self.wallet.send_self_transfer(from_node=self.nodes[0])["tx"] tx1.rehash() # Anyone-can-spend mempool tx. @@ -244,11 +231,11 @@ class BIP68Test(BitcoinTestFramework): tx2.nVersion = 2 tx2.vin = [CTxIn(COutPoint(tx1.sha256, 0), nSequence=0)] tx2.vout = [CTxOut(int(tx1.vout[0].nValue - self.relayfee * COIN), SCRIPT_W0_SH_OP_TRUE)] - tx2_raw = self.nodes[0].signrawtransactionwithwallet(tx2.serialize().hex())["hex"] - tx2 = tx_from_hex(tx2_raw) + self.wallet.sign_tx(tx=tx2) + tx2_raw = tx2.serialize().hex() tx2.rehash() - self.nodes[0].sendrawtransaction(tx2_raw) + self.wallet.sendrawtransaction(from_node=self.nodes[0], tx_hex=tx2_raw) # Create a spend of the 0th output of orig_tx with a sequence lock # of 1, and test what happens when submitting. @@ -268,10 +255,10 @@ class BIP68Test(BitcoinTestFramework): if (orig_tx.hash in node.getrawmempool()): # sendrawtransaction should fail if the tx is in the mempool - assert_raises_rpc_error(-26, NOT_FINAL_ERROR, node.sendrawtransaction, tx.serialize().hex()) + assert_raises_rpc_error(-26, NOT_FINAL_ERROR, self.wallet.sendrawtransaction, from_node=node, tx_hex=tx.serialize().hex()) else: # sendrawtransaction should succeed if the tx is not in the mempool - node.sendrawtransaction(tx.serialize().hex()) + self.wallet.sendrawtransaction(from_node=node, tx_hex=tx.serialize().hex()) return tx @@ -284,7 +271,7 @@ class BIP68Test(BitcoinTestFramework): cur_time = int(time.time()) for _ in range(10): self.nodes[0].setmocktime(cur_time + 600) - self.generate(self.nodes[0], 1, sync_fun=self.no_op) + self.generate(self.wallet, 1, sync_fun=self.no_op) cur_time += 600 assert tx2.hash in self.nodes[0].getrawmempool() @@ -318,12 +305,12 @@ class BIP68Test(BitcoinTestFramework): tx5 = test_nonzero_locks(tx4, self.nodes[0], self.relayfee, use_height_lock=True) assert tx5.hash not in self.nodes[0].getrawmempool() - utxos = self.nodes[0].listunspent() - tx5.vin.append(CTxIn(COutPoint(int(utxos[0]["txid"], 16), utxos[0]["vout"]), nSequence=1)) - tx5.vout[0].nValue += int(utxos[0]["amount"]*COIN) - raw_tx5 = self.nodes[0].signrawtransactionwithwallet(tx5.serialize().hex())["hex"] + utxo = self.wallet.get_utxo() + tx5.vin.append(CTxIn(COutPoint(int(utxo["txid"], 16), utxo["vout"]), nSequence=1)) + tx5.vout[0].nValue += int(utxo["value"]*COIN) + self.wallet.sign_tx(tx=tx5) - assert_raises_rpc_error(-26, NOT_FINAL_ERROR, self.nodes[0].sendrawtransaction, raw_tx5) + assert_raises_rpc_error(-26, NOT_FINAL_ERROR, self.wallet.sendrawtransaction, from_node=self.nodes[0], tx_hex=tx5.serialize().hex()) # Test mempool-BIP68 consistency after reorg # @@ -359,7 +346,7 @@ class BIP68Test(BitcoinTestFramework): # Reset the chain and get rid of the mocktimed-blocks self.nodes[0].setmocktime(0) self.nodes[0].invalidateblock(self.nodes[0].getblockhash(cur_height+1)) - self.generate(self.nodes[0], 10, sync_fun=self.no_op) + self.generate(self.wallet, 10, sync_fun=self.no_op) # Make sure that BIP68 isn't being used to validate blocks prior to # activation height. If more blocks are mined prior to this test @@ -367,9 +354,8 @@ class BIP68Test(BitcoinTestFramework): # this test should be moved to run earlier, or deleted. def test_bip68_not_consensus(self): assert not softfork_active(self.nodes[0], 'csv') - txid = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 2) - tx1 = tx_from_hex(self.nodes[0].getrawtransaction(txid)) + tx1 = self.wallet.send_self_transfer(from_node=self.nodes[0])["tx"] tx1.rehash() # Make an anyone-can-spend transaction @@ -379,11 +365,12 @@ class BIP68Test(BitcoinTestFramework): tx2.vout = [CTxOut(int(tx1.vout[0].nValue - self.relayfee * COIN), SCRIPT_W0_SH_OP_TRUE)] # sign tx2 - tx2_raw = self.nodes[0].signrawtransactionwithwallet(tx2.serialize().hex())["hex"] + self.wallet.sign_tx(tx=tx2) + tx2_raw = tx2.serialize().hex() tx2 = tx_from_hex(tx2_raw) tx2.rehash() - self.nodes[0].sendrawtransaction(tx2.serialize().hex()) + self.wallet.sendrawtransaction(from_node=self.nodes[0], tx_hex=tx2_raw) # Now make an invalid spend of tx2 according to BIP68 sequence_value = 100 # 100 block relative locktime @@ -396,7 +383,7 @@ class BIP68Test(BitcoinTestFramework): tx3.vout = [CTxOut(int(tx2.vout[0].nValue - self.relayfee * COIN), SCRIPT_W0_SH_OP_TRUE)] tx3.rehash() - assert_raises_rpc_error(-26, NOT_FINAL_ERROR, self.nodes[0].sendrawtransaction, tx3.serialize().hex()) + assert_raises_rpc_error(-26, NOT_FINAL_ERROR, self.wallet.sendrawtransaction, from_node=self.nodes[0], tx_hex=tx3.serialize().hex()) # make a block that violates bip68; ensure that the tip updates block = create_block(tmpl=self.nodes[0].getblocktemplate(NORMAL_GBT_REQUEST_PARAMS), txlist=[tx1, tx2, tx3]) @@ -412,22 +399,19 @@ class BIP68Test(BitcoinTestFramework): min_activation_height = 432 height = self.nodes[0].getblockcount() assert_greater_than(min_activation_height - height, 2) - self.generate(self.nodes[0], min_activation_height - height - 2, sync_fun=self.no_op) + self.generate(self.wallet, min_activation_height - height - 2, sync_fun=self.no_op) assert not softfork_active(self.nodes[0], 'csv') - self.generate(self.nodes[0], 1, sync_fun=self.no_op) + self.generate(self.wallet, 1, sync_fun=self.no_op) assert softfork_active(self.nodes[0], 'csv') self.sync_blocks() # Use self.nodes[1] to test that version 2 transactions are standard. def test_version2_relay(self): - inputs = [ ] - outputs = { self.nodes[1].getnewaddress() : 1.0 } - rawtx = self.nodes[1].createrawtransaction(inputs, outputs) - rawtxfund = self.nodes[1].fundrawtransaction(rawtx)['hex'] - tx = tx_from_hex(rawtxfund) + mini_wallet = MiniWallet(self.nodes[1]) + mini_wallet.rescan_utxos() + tx = mini_wallet.create_self_transfer()["tx"] tx.nVersion = 2 - tx_signed = self.nodes[1].signrawtransactionwithwallet(tx.serialize().hex())["hex"] - self.nodes[1].sendrawtransaction(tx_signed) + mini_wallet.sendrawtransaction(from_node=self.nodes[1], tx_hex=tx.serialize().hex()) if __name__ == '__main__': BIP68Test().main() diff --git a/test/functional/feature_block.py b/test/functional/feature_block.py index 850cb8334c..22b1918b85 100755 --- a/test/functional/feature_block.py +++ b/test/functional/feature_block.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2021 The Bitcoin Core developers +# Copyright (c) 2015-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 block processing.""" diff --git a/test/functional/feature_cltv.py b/test/functional/feature_cltv.py index 9d32749a08..7730db9672 100755 --- a/test/functional/feature_cltv.py +++ b/test/functional/feature_cltv.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2021 The Bitcoin Core developers +# Copyright (c) 2015-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 BIP65 (CHECKLOCKTIMEVERIFY). @@ -8,6 +8,7 @@ Test that the CHECKLOCKTIMEVERIFY soft-fork activates. """ from test_framework.blocktools import ( + TIME_GENESIS_BLOCK, create_block, create_coinbase, ) @@ -61,7 +62,7 @@ def cltv_invalidate(tx, failure_reason): # +-------------------------------------------------+------------+--------------+ [[OP_CHECKLOCKTIMEVERIFY], None, None], [[OP_1NEGATE, OP_CHECKLOCKTIMEVERIFY, OP_DROP], None, None], - [[CScriptNum(100), OP_CHECKLOCKTIMEVERIFY, OP_DROP], 0, 1296688602], # timestamp of genesis block + [[CScriptNum(100), OP_CHECKLOCKTIMEVERIFY, OP_DROP], 0, TIME_GENESIS_BLOCK], [[CScriptNum(100), OP_CHECKLOCKTIMEVERIFY, OP_DROP], 0, 50], [[CScriptNum(50), OP_CHECKLOCKTIMEVERIFY, OP_DROP], SEQUENCE_FINAL, 50], ][failure_reason] diff --git a/test/functional/feature_coinstatsindex.py b/test/functional/feature_coinstatsindex.py index 2e21638f80..eff4d9b149 100755 --- a/test/functional/feature_coinstatsindex.py +++ b/test/functional/feature_coinstatsindex.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2020-2021 The Bitcoin Core developers +# Copyright (c) 2020-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 coinstatsindex across nodes. @@ -221,7 +221,7 @@ class CoinStatsIndexTest(BitcoinTestFramework): self.generate(index_node, 1, sync_fun=self.no_op) res10 = index_node.gettxoutsetinfo('muhash') - assert(res8['txouts'] < res10['txouts']) + assert res8['txouts'] < res10['txouts'] self.log.info("Test that the index works with -reindex") @@ -268,12 +268,12 @@ class CoinStatsIndexTest(BitcoinTestFramework): res2 = index_node.gettxoutsetinfo(hash_type='muhash', hash_or_height=112) assert_equal(res["bestblock"], block) assert_equal(res["muhash"], res2["muhash"]) - assert(res["muhash"] != res_invalid["muhash"]) + assert res["muhash"] != res_invalid["muhash"] # Test that requesting reorged out block by hash is still returning correct results res_invalid2 = index_node.gettxoutsetinfo(hash_type='muhash', hash_or_height=reorg_block) assert_equal(res_invalid2["muhash"], res_invalid["muhash"]) - assert(res["muhash"] != res_invalid2["muhash"]) + assert res["muhash"] != res_invalid2["muhash"] # Add another block, so we don't depend on reconsiderblock remembering which # blocks were touched by invalidateblock diff --git a/test/functional/feature_config_args.py b/test/functional/feature_config_args.py index 112dbb9e6a..d5e5ed47d6 100755 --- a/test/functional/feature_config_args.py +++ b/test/functional/feature_config_args.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2017-2021 The Bitcoin Core developers +# Copyright (c) 2017-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 various command line arguments and configuration file parameters.""" @@ -12,6 +12,9 @@ from test_framework import util class ConfArgsTest(BitcoinTestFramework): + def add_options(self, parser): + self.add_wallet_options(parser) + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 1 diff --git a/test/functional/feature_csv_activation.py b/test/functional/feature_csv_activation.py index bff95c3b94..a88a97c813 100755 --- a/test/functional/feature_csv_activation.py +++ b/test/functional/feature_csv_activation.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2021 The Bitcoin Core developers +# Copyright (c) 2015-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 CSV soft fork activation. diff --git a/test/functional/feature_dbcrash.py b/test/functional/feature_dbcrash.py index f606f26e70..e2bc566f53 100755 --- a/test/functional/feature_dbcrash.py +++ b/test/functional/feature_dbcrash.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2017-2021 The Bitcoin Core developers +# Copyright (c) 2017-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 recovery from a crash during chainstate writing. @@ -202,7 +202,6 @@ class ChainstateWriteCrashTest(BitcoinTestFramework): def run_test(self): self.wallet = MiniWallet(self.nodes[3]) - self.wallet.rescan_utxos() initial_height = self.nodes[3].getblockcount() self.generate(self.nodes[3], COINBASE_MATURITY, sync_fun=self.no_op) diff --git a/test/functional/feature_dersig.py b/test/functional/feature_dersig.py index 9a46839969..4a66863d91 100755 --- a/test/functional/feature_dersig.py +++ b/test/functional/feature_dersig.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2021 The Bitcoin Core developers +# Copyright (c) 2015-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 BIP66 (DER SIG). diff --git a/test/functional/feature_fee_estimation.py b/test/functional/feature_fee_estimation.py index b0cbcf4edf..05ee556ece 100755 --- a/test/functional/feature_fee_estimation.py +++ b/test/functional/feature_fee_estimation.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2014-2021 The Bitcoin Core developers +# Copyright (c) 2014-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 fee estimation code.""" @@ -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,12 +57,15 @@ 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}) - return (tx.serialize().hex(), fee) + return (tx.get_vsize(), fee) def check_raw_estimates(node, fees_seen): @@ -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,9 +158,10 @@ 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) - (txhex, fee) = small_txpuzzle_randfee( + (tx_bytes, fee) = small_txpuzzle_randfee( self.wallet, self.nodes[from_index], self.confutxo, @@ -166,9 +169,12 @@ class EstimateFeeTest(BitcoinTestFramework): Decimal("0.005"), min_fee, min_fee, + batch_sendtx_reqs, ) - tx_kbytes = (len(txhex) // 2) / 1000.0 + 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]) @@ -280,7 +297,6 @@ class EstimateFeeTest(BitcoinTestFramework): # Split two coinbases into many small utxos self.start_node(0) self.wallet = MiniWallet(self.nodes[0]) - self.wallet.rescan_utxos() self.initial_split(self.nodes[0]) self.log.info("Finished splitting") diff --git a/test/functional/feature_filelock.py b/test/functional/feature_filelock.py index 945ece6a33..bb4104bf8e 100755 --- a/test/functional/feature_filelock.py +++ b/test/functional/feature_filelock.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2018-2021 The Bitcoin Core developers +# Copyright (c) 2018-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. """Check that it's not possible to start a second bitcoind instance using the same datadir or wallet.""" @@ -11,6 +11,9 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.test_node import ErrorMatch class FilelockTest(BitcoinTestFramework): + def add_options(self, parser): + self.add_wallet_options(parser) + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 2 diff --git a/test/functional/feature_index_prune.py b/test/functional/feature_index_prune.py index 4b7a50c1c7..77a056346a 100755 --- a/test/functional/feature_index_prune.py +++ b/test/functional/feature_index_prune.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2020-2021 The Bitcoin Core developers +# Copyright (c) 2020-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 indices in conjunction with prune.""" @@ -62,7 +62,7 @@ class FeatureIndexPruneTest(BitcoinTestFramework): for node in filter_nodes: assert_greater_than(len(node.getblockfilter(tip)['filter']), 0) for node in stats_nodes: - assert(node.gettxoutsetinfo(hash_type="muhash", hash_or_height=tip)['muhash']) + assert node.gettxoutsetinfo(hash_type="muhash", hash_or_height=tip)['muhash'] self.mine_batches(500) self.sync_index(height=700) @@ -80,14 +80,14 @@ class FeatureIndexPruneTest(BitcoinTestFramework): for node in filter_nodes: assert_greater_than(len(node.getblockfilter(tip)['filter']), 0) for node in stats_nodes: - assert(node.gettxoutsetinfo(hash_type="muhash", hash_or_height=tip)['muhash']) + assert node.gettxoutsetinfo(hash_type="muhash", hash_or_height=tip)['muhash'] self.log.info("check if we can access the blockfilter and coinstats of a pruned block") height_hash = self.nodes[0].getblockhash(2) for node in filter_nodes: assert_greater_than(len(node.getblockfilter(height_hash)['filter']), 0) for node in stats_nodes: - assert(node.gettxoutsetinfo(hash_type="muhash", hash_or_height=height_hash)['muhash']) + assert node.gettxoutsetinfo(hash_type="muhash", hash_or_height=height_hash)['muhash'] # mine and sync index up to a height that will later be the pruneheight self.generate(self.nodes[0], 51) diff --git a/test/functional/feature_init.py b/test/functional/feature_init.py index 56d093c396..70a802dc58 100755 --- a/test/functional/feature_init.py +++ b/test/functional/feature_init.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2021 The Bitcoin Core developers +# Copyright (c) 2021-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. """Stress tests related to node initialization.""" @@ -17,6 +17,9 @@ class InitStressTest(BitcoinTestFramework): subsequent starts. """ + def add_options(self, parser): + self.add_wallet_options(parser) + def set_test_params(self): self.setup_clean_chain = False self.num_nodes = 1 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_maxuploadtarget.py b/test/functional/feature_maxuploadtarget.py index 3ea412002a..28a8959e93 100755 --- a/test/functional/feature_maxuploadtarget.py +++ b/test/functional/feature_maxuploadtarget.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2021 The Bitcoin Core developers +# Copyright (c) 2015-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 behavior of -maxuploadtarget. diff --git a/test/functional/feature_minchainwork.py b/test/functional/feature_minchainwork.py index fb4024b1b0..078d2ef63c 100755 --- a/test/functional/feature_minchainwork.py +++ b/test/functional/feature_minchainwork.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2017-2021 The Bitcoin Core developers +# Copyright (c) 2017-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 logic for setting nMinimumChainWork on command line. diff --git a/test/functional/feature_notifications.py b/test/functional/feature_notifications.py index e038afa1ad..8cb633d454 100755 --- a/test/functional/feature_notifications.py +++ b/test/functional/feature_notifications.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2014-2021 The Bitcoin Core developers +# Copyright (c) 2014-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 the -alertnotify, -blocknotify and -walletnotify options.""" @@ -24,11 +24,14 @@ def notify_outputname(walletname, txid): class NotificationsTest(BitcoinTestFramework): + def add_options(self, parser): + self.add_wallet_options(parser) + def set_test_params(self): self.num_nodes = 2 self.setup_clean_chain = True # The experimental syscall sandbox feature (-sandbox) is not compatible with -alertnotify, - # -blocknotify or -walletnotify (which all invoke execve). + # -blocknotify, -walletnotify or -shutdownnotify (which all invoke execve). self.disable_syscall_sandbox = True def setup_network(self): @@ -36,14 +39,18 @@ class NotificationsTest(BitcoinTestFramework): self.alertnotify_dir = os.path.join(self.options.tmpdir, "alertnotify") self.blocknotify_dir = os.path.join(self.options.tmpdir, "blocknotify") self.walletnotify_dir = os.path.join(self.options.tmpdir, "walletnotify") + self.shutdownnotify_dir = os.path.join(self.options.tmpdir, "shutdownnotify") + self.shutdownnotify_file = os.path.join(self.shutdownnotify_dir, "shutdownnotify.txt") os.mkdir(self.alertnotify_dir) os.mkdir(self.blocknotify_dir) os.mkdir(self.walletnotify_dir) + os.mkdir(self.shutdownnotify_dir) # -alertnotify and -blocknotify on node0, walletnotify on node1 self.extra_args = [[ f"-alertnotify=echo > {os.path.join(self.alertnotify_dir, '%s')}", f"-blocknotify=echo > {os.path.join(self.blocknotify_dir, '%s')}", + f"-shutdownnotify=echo > {self.shutdownnotify_file}", ], [ f"-walletnotify=echo %h_%b > {os.path.join(self.walletnotify_dir, notify_outputname('%w', '%s'))}", ]] @@ -159,6 +166,10 @@ class NotificationsTest(BitcoinTestFramework): # TODO: add test for `-alertnotify` large fork notifications + self.log.info("test -shutdownnotify") + self.stop_nodes() + self.wait_until(lambda: os.path.isfile(self.shutdownnotify_file), timeout=10) + def expect_wallet_notify(self, tx_details): self.wait_until(lambda: len(os.listdir(self.walletnotify_dir)) >= len(tx_details), timeout=10) # Should have no more and no less files than expected diff --git a/test/functional/feature_nulldummy.py b/test/functional/feature_nulldummy.py index 9bfb79057e..d7558c830e 100755 --- a/test/functional/feature_nulldummy.py +++ b/test/functional/feature_nulldummy.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2016-2021 The Bitcoin Core developers +# Copyright (c) 2016-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 NULLDUMMY softfork. diff --git a/test/functional/feature_proxy.py b/test/functional/feature_proxy.py index 18b079cd71..662007d65e 100755 --- a/test/functional/feature_proxy.py +++ b/test/functional/feature_proxy.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2021 The Bitcoin Core developers +# Copyright (c) 2015-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 bitcoind with different proxy configuration. diff --git a/test/functional/feature_pruning.py b/test/functional/feature_pruning.py index 7dbeccbc09..664ed779db 100755 --- a/test/functional/feature_pruning.py +++ b/test/functional/feature_pruning.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2014-2021 The Bitcoin Core developers +# Copyright (c) 2014-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 the pruning code. @@ -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 @@ -76,6 +65,9 @@ def calc_usage(blockdir): return sum(os.path.getsize(blockdir + f) for f in os.listdir(blockdir) if os.path.isfile(os.path.join(blockdir, f))) / (1024. * 1024.) class PruneTest(BitcoinTestFramework): + def add_options(self, parser): + self.add_wallet_options(parser) + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 6 @@ -92,7 +84,7 @@ class PruneTest(BitcoinTestFramework): ["-maxreceivebuffer=20000", "-prune=550"], ["-maxreceivebuffer=20000"], ["-maxreceivebuffer=20000"], - ["-prune=550"], + ["-prune=550", "-blockfilterindex=1"], ] self.rpc_timeout = 120 @@ -342,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" @@ -364,7 +356,7 @@ class PruneTest(BitcoinTestFramework): self.connect_nodes(0, 5) nds = [self.nodes[0], self.nodes[5]] self.sync_blocks(nds, wait=5, timeout=300) - self.restart_node(5, extra_args=["-prune=550"]) # restart to trigger rescan + self.restart_node(5, extra_args=["-prune=550", "-blockfilterindex=1"]) # restart to trigger rescan self.log.info("Success") def run_test(self): @@ -480,7 +472,20 @@ class PruneTest(BitcoinTestFramework): self.log.info("Test invalid pruning command line options") self.test_invalid_command_line_options() + self.test_scanblocks_pruned() + self.log.info("Done") + def test_scanblocks_pruned(self): + node = self.nodes[5] + genesis_blockhash = node.getblockhash(0) + false_positive_spk = bytes.fromhex("001400000000000000000000000000000000000cadcb") + + assert genesis_blockhash in node.scanblocks( + "start", [{"desc": f"raw({false_positive_spk.hex()})"}], 0, 0)['relevant_blocks'] + + assert_raises_rpc_error(-1, "Block not available (pruned data)", node.scanblocks, + "start", [{"desc": f"raw({false_positive_spk.hex()})"}], 0, 0, "basic", {"filter_false_positives": True}) + if __name__ == '__main__': PruneTest().main() diff --git a/test/functional/feature_rbf.py b/test/functional/feature_rbf.py index 7603248ae5..0a84a66a8f 100755 --- a/test/functional/feature_rbf.py +++ b/test/functional/feature_rbf.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2014-2021 The Bitcoin Core developers +# Copyright (c) 2014-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 the RBF code.""" @@ -21,6 +21,9 @@ from test_framework.address import ADDRESS_BCRT1_UNSPENDABLE MAX_REPLACEMENT_LIMIT = 100 class ReplaceByFeeTest(BitcoinTestFramework): + def add_options(self, parser): + self.add_wallet_options(parser) + def set_test_params(self): self.num_nodes = 2 self.extra_args = [ @@ -39,10 +42,6 @@ class ReplaceByFeeTest(BitcoinTestFramework): def run_test(self): self.wallet = MiniWallet(self.nodes[0]) - # the pre-mined test framework chain contains coinbase outputs to the - # MiniWallet's default address in blocks 76-100 (see method - # BitcoinTestFramework._initialize_chain()) - self.wallet.rescan_utxos() self.log.info("Running test simple doublespend...") self.test_simple_doublespend() @@ -395,7 +394,6 @@ class ReplaceByFeeTest(BitcoinTestFramework): """ normal_node = self.nodes[1] wallet = MiniWallet(normal_node) - wallet.rescan_utxos() # Clear mempools to avoid cross-node sync failure. for node in self.nodes: self.generate(node, 1) diff --git a/test/functional/feature_segwit.py b/test/functional/feature_segwit.py index 7f2a615be1..77f3e4feda 100755 --- a/test/functional/feature_segwit.py +++ b/test/functional/feature_segwit.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2016-2021 The Bitcoin Core developers +# Copyright (c) 2016-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 the SegWit changeover logic.""" @@ -78,6 +78,9 @@ txs_mined = {} # txindex from txid to blockhash class SegWitTest(BitcoinTestFramework): + def add_options(self, parser): + self.add_wallet_options(parser) + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 3 diff --git a/test/functional/feature_signet.py b/test/functional/feature_signet.py index 4c1e48af6d..b41fe378af 100755 --- a/test/functional/feature_signet.py +++ b/test/functional/feature_signet.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2019-2021 The Bitcoin Core developers +# Copyright (c) 2019-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 basic signet functionality""" diff --git a/test/functional/feature_startupnotify.py b/test/functional/feature_startupnotify.py index c6aa837768..ff5272b281 100755 --- a/test/functional/feature_startupnotify.py +++ b/test/functional/feature_startupnotify.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2020 The Bitcoin Core developers +# Copyright (c) 2020-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 -startupnotify.""" @@ -29,9 +29,14 @@ class StartupNotifyTest(BitcoinTestFramework): self.wait_until(lambda: os.path.exists(tmpdir_file)) self.log.info("Test -startupnotify is executed once") - with open(tmpdir_file, "r", encoding="utf8") as f: - file_content = f.read() - assert_equal(file_content.count(FILE_NAME), 1) + + def get_count(): + with open(tmpdir_file, "r", encoding="utf8") as f: + file_content = f.read() + return file_content.count(FILE_NAME) + + self.wait_until(lambda: get_count() > 0) + assert_equal(get_count(), 1) self.log.info("Test node is fully started") assert_equal(self.nodes[0].getblockcount(), 200) diff --git a/test/functional/feature_syscall_sandbox.py b/test/functional/feature_syscall_sandbox.py index e430542845..2200f6c2e6 100755 --- a/test/functional/feature_syscall_sandbox.py +++ b/test/functional/feature_syscall_sandbox.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2021 The Bitcoin Core developers +# Copyright (c) 2021-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 bitcoind aborts if a disallowed syscall is used when compiled with the syscall sandbox.""" diff --git a/test/functional/feature_taproot.py b/test/functional/feature_taproot.py index cbb2e0338b..144e01c367 100755 --- a/test/functional/feature_taproot.py +++ b/test/functional/feature_taproot.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2019-2021 The Bitcoin Core developers +# Copyright (c) 2019-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 Taproot softfork (BIPs 340-342) @@ -96,7 +96,14 @@ from test_framework.util import ( assert_equal, random_bytes, ) -from test_framework.key import generate_privkey, compute_xonly_pubkey, sign_schnorr, tweak_add_privkey, ECKey +from test_framework.key import ( + generate_privkey, + compute_xonly_pubkey, + sign_schnorr, + tweak_add_privkey, + ECKey, + SECP256K1 +) from test_framework.address import ( hash160, program_to_witness, @@ -661,6 +668,44 @@ def spenders_taproot_active(): # Test with signature with bit flipped. add_spender(spenders, "sig/bitflip", tap=tap, key=secs[0], failure={"signature": bitflipper(default_signature)}, **ERR_SIG_SCHNORR) + # == Test involving an internal public key not on the curve == + + # X-only public keys are 32 bytes, but not every 32-byte array is a valid public key; only + # around 50% of them are. This does not affect users using correct software; these "keys" have + # no corresponding private key, and thus will never appear as output of key + # generation/derivation/tweaking. + # + # Using an invalid public key as P2TR output key makes the UTXO unspendable. Revealing an + # invalid public key as internal key in a P2TR script path spend also makes the spend invalid. + # These conditions are explicitly spelled out in BIP341. + # + # It is however hard to create test vectors for this, because it involves "guessing" how a + # hypothetical incorrect implementation deals with an obviously-invalid condition, and making + # sure that guessed behavior (accepting it in certain condition) doesn't occur. + # + # The test case added here tries to detect a very specific bug a verifier could have: if they + # don't verify whether or not a revealed internal public key in a script path spend is valid, + # and (correctly) implement output_key == tweak(internal_key, tweakval) but (incorrectly) treat + # tweak(invalid_key, tweakval) as equal the public key corresponding to private key tweakval. + # This may seem like a far-fetched edge condition to test for, but in fact, the BIP341 wallet + # pseudocode did exactly that (but obviously only triggerable by someone invoking the tweaking + # function with an invalid public key, which shouldn't happen). + + # Generate an invalid public key + while True: + invalid_pub = random_bytes(32) + if not SECP256K1.is_x_coord(int.from_bytes(invalid_pub, 'big')): + break + + # Implement a test case that detects validation logic which maps invalid public keys to the + # point at infinity in the tweaking logic. + tap = taproot_construct(invalid_pub, [("true", CScript([OP_1]))], treat_internal_as_infinity=True) + add_spender(spenders, "output/invalid_x", tap=tap, key_tweaked=tap.tweak, failure={"leaf": "true", "inputs": []}, **ERR_WITNESS_PROGRAM_MISMATCH) + + # Do the same thing without invalid point, to make sure there is no mistake in the test logic. + tap = taproot_construct(pubs[0], [("true", CScript([OP_1]))]) + add_spender(spenders, "output/invalid_x_mock", tap=tap, key=secs[0], leaf="true", inputs=[]) + # == Tests for signature hashing == # Run all tests once with no annex, and once with a valid random annex. @@ -1229,6 +1274,7 @@ UTXOData = namedtuple('UTXOData', 'outpoint,output,spender') class TaprootTest(BitcoinTestFramework): def add_options(self, parser): + self.add_wallet_options(parser) parser.add_argument("--dumptests", dest="dump_tests", default=False, action="store_true", help="Dump generated test cases to directory set by TEST_DUMP_DIR environment variable") @@ -1246,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/feature_txindex_compatibility.py b/test/functional/feature_txindex_compatibility.py index 20b023d82c..48fefaa0ba 100755 --- a/test/functional/feature_txindex_compatibility.py +++ b/test/functional/feature_txindex_compatibility.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2021 The Bitcoin Core developers +# Copyright (c) 2021-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 that legacy txindex will be disabled on upgrade. @@ -14,7 +14,7 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.wallet import MiniWallet -class MempoolCompatibilityTest(BitcoinTestFramework): +class TxindexCompatibilityTest(BitcoinTestFramework): def set_test_params(self): self.num_nodes = 3 self.extra_args = [ @@ -33,7 +33,7 @@ class MempoolCompatibilityTest(BitcoinTestFramework): versions=[ 160300, # Last release with legacy txindex None, # For MiniWallet, without migration code - 200100, # Any release with migration code (0.17.x - 22.x) + 220000, # Last release with migration code (0.17.x - 22.x) ], ) self.start_nodes() @@ -42,7 +42,6 @@ class MempoolCompatibilityTest(BitcoinTestFramework): def run_test(self): mini_wallet = MiniWallet(self.nodes[1]) - mini_wallet.rescan_utxos() spend_utxo = mini_wallet.get_utxo() mini_wallet.send_self_transfer(from_node=self.nodes[1], utxo_to_spend=spend_utxo) self.generate(self.nodes[1], 1) @@ -89,4 +88,4 @@ class MempoolCompatibilityTest(BitcoinTestFramework): if __name__ == "__main__": - MempoolCompatibilityTest().main() + TxindexCompatibilityTest().main() diff --git a/test/functional/feature_utxo_set_hash.py b/test/functional/feature_utxo_set_hash.py index 4d486bc6f4..0f510ced89 100755 --- a/test/functional/feature_utxo_set_hash.py +++ b/test/functional/feature_utxo_set_hash.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2020-2021 The Bitcoin Core developers +# Copyright (c) 2020-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 UTXO set hash value calculation in gettxoutsetinfo.""" diff --git a/test/functional/feature_versionbits_warning.py b/test/functional/feature_versionbits_warning.py index 1572463308..0a9e1d4448 100755 --- a/test/functional/feature_versionbits_warning.py +++ b/test/functional/feature_versionbits_warning.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2016-2021 The Bitcoin Core developers +# Copyright (c) 2016-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 version bits warning system. diff --git a/test/functional/interface_bitcoin_cli.py b/test/functional/interface_bitcoin_cli.py index db5564ac50..25ea557217 100755 --- a/test/functional/interface_bitcoin_cli.py +++ b/test/functional/interface_bitcoin_cli.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2017-2021 The Bitcoin Core developers +# Copyright (c) 2017-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 bitcoin-cli""" @@ -66,11 +66,12 @@ def cli_get_info_string_to_dict(cli_get_info_string): class TestBitcoinCli(BitcoinTestFramework): + def add_options(self, parser): + self.add_wallet_options(parser) + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 1 - if self.is_specified_wallet_compiled(): - self.requires_wallet = True def skip_test_if_missing_module(self): self.skip_if_no_cli() @@ -84,6 +85,15 @@ class TestBitcoinCli(BitcoinTestFramework): rpc_response = self.nodes[0].getblockchaininfo() assert_equal(cli_response, rpc_response) + self.log.info("Test named arguments") + assert_equal(self.nodes[0].cli.echo(0, 1, arg3=3, arg5=5), ['0', '1', None, '3', None, '5']) + 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") @@ -114,6 +124,7 @@ class TestBitcoinCli(BitcoinTestFramework): self.log.info("Test -getinfo returns expected network and blockchain info") if self.is_specified_wallet_compiled(): + self.import_deterministic_coinbase_privkeys() self.nodes[0].encryptwallet(password) cli_get_info_string = self.nodes[0].cli('-getinfo').send_cli() cli_get_info = cli_get_info_string_to_dict(cli_get_info_string) diff --git a/test/functional/interface_rest.py b/test/functional/interface_rest.py index 24252610be..0dddc8946a 100755 --- a/test/functional/interface_rest.py +++ b/test/functional/interface_rest.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2014-2021 The Bitcoin Core developers +# Copyright (c) 2014-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 the REST API.""" @@ -96,7 +96,6 @@ class RESTTest (BitcoinTestFramework): def run_test(self): self.url = urllib.parse.urlparse(self.nodes[0].url) self.wallet = MiniWallet(self.nodes[0]) - self.wallet.rescan_utxos() self.log.info("Broadcast test transaction and sync nodes") txid, _ = self.wallet.send_to(from_node=self.nodes[0], scriptPubKey=getnewdestination()[1], amount=int(0.1 * COIN)) diff --git a/test/functional/interface_rpc.py b/test/functional/interface_rpc.py index 48082f3a17..3725c89719 100755 --- a/test/functional/interface_rpc.py +++ b/test/functional/interface_rpc.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2018-2021 The Bitcoin Core developers +# Copyright (c) 2018-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. """Tests some generic aspects of the RPC interface.""" @@ -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/interface_usdt_coinselection.py b/test/functional/interface_usdt_coinselection.py index ef32feda99..a3c830bb51 100755 --- a/test/functional/interface_usdt_coinselection.py +++ b/test/functional/interface_usdt_coinselection.py @@ -97,6 +97,9 @@ int trace_aps_create_tx(struct pt_regs *ctx) { class CoinSelectionTracepointTest(BitcoinTestFramework): + def add_options(self, parser): + self.add_wallet_options(parser) + def set_test_params(self): self.num_nodes = 1 self.setup_clean_chain = True diff --git a/test/functional/interface_usdt_utxocache.py b/test/functional/interface_usdt_utxocache.py index 2280de1479..23785b1e8a 100755 --- a/test/functional/interface_usdt_utxocache.py +++ b/test/functional/interface_usdt_utxocache.py @@ -144,7 +144,6 @@ class UTXOCacheTracepointTest(BitcoinTestFramework): def run_test(self): self.wallet = MiniWallet(self.nodes[0]) - self.generate(self.wallet, 101) self.test_uncache() self.test_add_spent() @@ -357,8 +356,8 @@ class UTXOCacheTracepointTest(BitcoinTestFramework): "size": event.size }) # sanity checks only - assert(event.memory > 0) - assert(event.duration > 0) + assert event.memory > 0 + assert event.duration > 0 handle_flush_succeeds += 1 bpf["utxocache_flush"].open_perf_buffer(handle_utxocache_flush) diff --git a/test/functional/interface_usdt_validation.py b/test/functional/interface_usdt_validation.py index 8953dd023b..4323aef771 100755 --- a/test/functional/interface_usdt_validation.py +++ b/test/functional/interface_usdt_validation.py @@ -112,7 +112,7 @@ class ValidationTracepointTest(BitcoinTestFramework): assert_equal(len([tx["vin"] for tx in block["tx"]]), event.inputs) assert_equal(0, event.sigops) # no sigops in coinbase tx # only plausibility checks - assert(event.duration > 0) + assert event.duration > 0 del expected_blocks[block_hash] blocks_checked += 1 diff --git a/test/functional/interface_zmq.py b/test/functional/interface_zmq.py index 7d8d10589b..2f41f9aa53 100755 --- a/test/functional/interface_zmq.py +++ b/test/functional/interface_zmq.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2021 The Bitcoin Core developers +# Copyright (c) 2015-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 the ZMQ notification interface.""" @@ -22,6 +22,7 @@ from test_framework.messages import ( from test_framework.util import ( assert_equal, assert_raises_rpc_error, + p2p_port, ) from test_framework.wallet import ( MiniWallet, @@ -106,6 +107,7 @@ class ZMQTest (BitcoinTestFramework): # This test isn't testing txn relay/timing, so set whitelist on the # peers for instant txn relay. This speeds up the test run time 2-3x. self.extra_args = [["-whitelist=noban@127.0.0.1"]] * self.num_nodes + self.zmq_port_base = p2p_port(self.num_nodes + 1) def skip_test_if_missing_module(self): self.skip_if_no_py3_zmq() @@ -179,7 +181,7 @@ class ZMQTest (BitcoinTestFramework): # Invalid zmq arguments don't take down the node, see #17185. self.restart_node(0, ["-zmqpubrawtx=foo", "-zmqpubhashtx=bar"]) - address = 'tcp://127.0.0.1:28332' + address = f"tcp://127.0.0.1:{self.zmq_port_base}" subs = self.setup_zmq_test([(topic, address) for topic in ["hashblock", "hashtx", "rawblock", "rawtx"]]) hashblock = subs[0] @@ -213,7 +215,6 @@ class ZMQTest (BitcoinTestFramework): assert_equal([txid.hex()], self.nodes[1].getblock(hash)["tx"]) - self.wallet.rescan_utxos() self.log.info("Wait for tx from second node") payment_tx = self.wallet.send_self_transfer(from_node=self.nodes[1]) payment_txid = payment_tx['txid'] @@ -246,7 +247,7 @@ class ZMQTest (BitcoinTestFramework): def test_reorg(self): - address = 'tcp://127.0.0.1:28333' + address = f"tcp://127.0.0.1:{self.zmq_port_base}" # Should only notify the tip if a reorg occurs hashblock, hashtx = self.setup_zmq_test( @@ -300,7 +301,7 @@ class ZMQTest (BitcoinTestFramework): <32-byte hash>A<8-byte LE uint> : Transactionhash added mempool """ self.log.info("Testing 'sequence' publisher") - [seq] = self.setup_zmq_test([("sequence", "tcp://127.0.0.1:28333")]) + [seq] = self.setup_zmq_test([("sequence", f"tcp://127.0.0.1:{self.zmq_port_base}")]) self.disconnect_nodes(0, 1) # Mempool sequence number starts at 1 @@ -444,7 +445,7 @@ class ZMQTest (BitcoinTestFramework): """ self.log.info("Testing 'mempool sync' usage of sequence notifier") - [seq] = self.setup_zmq_test([("sequence", "tcp://127.0.0.1:28333")]) + [seq] = self.setup_zmq_test([("sequence", f"tcp://127.0.0.1:{self.zmq_port_base}")]) # In-memory counter, should always start at 1 next_mempool_seq = self.nodes[0].getrawmempool(mempool_sequence=True)["mempool_sequence"] @@ -549,8 +550,8 @@ class ZMQTest (BitcoinTestFramework): # chain lengths on node0 and node1; for this test we only need node0, so # we can disable syncing blocks on the setup) subscribers = self.setup_zmq_test([ - ("hashblock", "tcp://127.0.0.1:28334"), - ("hashblock", "tcp://127.0.0.1:28335"), + ("hashblock", f"tcp://127.0.0.1:{self.zmq_port_base + 1}"), + ("hashblock", f"tcp://127.0.0.1:{self.zmq_port_base + 2}"), ], sync_blocks=False) # Generate 1 block in nodes[0] and receive all notifications @@ -567,7 +568,7 @@ class ZMQTest (BitcoinTestFramework): self.log.info("Testing IPv6") # Set up subscriber using IPv6 loopback address subscribers = self.setup_zmq_test([ - ("hashblock", "tcp://[::1]:28332") + ("hashblock", f"tcp://[::1]:{self.zmq_port_base}") ], ipv6=True) # Generate 1 block in nodes[0] diff --git a/test/functional/mempool_accept.py b/test/functional/mempool_accept.py index 02ec18140c..362b407062 100755 --- a/test/functional/mempool_accept.py +++ b/test/functional/mempool_accept.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2017-2021 The Bitcoin Core developers +# Copyright (c) 2017-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 mempool acceptance of raw transactions.""" @@ -14,7 +14,9 @@ from test_framework.messages import ( MAX_BIP125_RBF_SEQUENCE, COIN, COutPoint, + CTransaction, CTxIn, + CTxInWitness, CTxOut, MAX_BLOCK_WEIGHT, MAX_MONEY, @@ -26,13 +28,19 @@ from test_framework.script import ( OP_0, OP_HASH160, OP_RETURN, + OP_TRUE, ) from test_framework.script_util import ( + DUMMY_MIN_OP_RETURN_SCRIPT, keys_to_multisig_script, + MIN_PADDING, + MIN_STANDARD_TX_NONWITNESS_SIZE, script_to_p2sh_script, + script_to_p2wsh_script, ) from test_framework.util import ( assert_equal, + assert_greater_than, assert_raises_rpc_error, ) from test_framework.wallet import MiniWallet @@ -50,14 +58,17 @@ class MempoolAcceptanceTest(BitcoinTestFramework): """Wrapper to check result of testmempoolaccept on node_0's mempool""" result_test = self.nodes[0].testmempoolaccept(*args, **kwargs) for r in result_test: - r.pop('wtxid') # Skip check for now + # Skip these checks for now + r.pop('wtxid') + if "fees" in r: + r["fees"].pop("effective-feerate") + r["fees"].pop("effective-includes") assert_equal(result_expected, result_test) assert_equal(self.nodes[0].getmempoolinfo()['size'], self.mempool_size) # Must not change mempool state def run_test(self): node = self.nodes[0] self.wallet = MiniWallet(node) - self.wallet.rescan_utxos() self.log.info('Start with empty mempool, and 200 blocks') self.mempool_size = 0 @@ -333,6 +344,35 @@ class MempoolAcceptanceTest(BitcoinTestFramework): maxfeerate=0, ) + # Prep for tiny-tx tests with wsh(OP_TRUE) output + seed_tx = self.wallet.send_to(from_node=node, scriptPubKey=script_to_p2wsh_script(CScript([OP_TRUE])), amount=COIN) + self.generate(node, 1) + + self.log.info('A tiny transaction(in non-witness bytes) that is disallowed') + tx = CTransaction() + tx.vin.append(CTxIn(COutPoint(int(seed_tx[0], 16), seed_tx[1]), b"", SEQUENCE_FINAL)) + tx.wit.vtxinwit = [CTxInWitness()] + tx.wit.vtxinwit[0].scriptWitness.stack = [CScript([OP_TRUE])] + tx.vout.append(CTxOut(0, CScript([OP_RETURN] + ([OP_0] * (MIN_PADDING - 2))))) + # Note it's only non-witness size that matters! + assert_equal(len(tx.serialize_without_witness()), 64) + assert_equal(MIN_STANDARD_TX_NONWITNESS_SIZE - 1, 64) + assert_greater_than(len(tx.serialize()), 64) + + self.check_mempool_result( + result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': 'tx-size-small'}], + rawtxs=[tx.serialize().hex()], + maxfeerate=0, + ) + + self.log.info('Minimally-small transaction(in non-witness bytes) that is allowed') + tx.vout[0] = CTxOut(COIN - 1000, DUMMY_MIN_OP_RETURN_SCRIPT) + assert_equal(len(tx.serialize_without_witness()), MIN_STANDARD_TX_NONWITNESS_SIZE) + self.check_mempool_result( + result_expected=[{'txid': tx.rehash(), 'allowed': True, 'vsize': tx.get_vsize(), 'fees': { 'base': Decimal('0.00001000')}}], + rawtxs=[tx.serialize().hex()], + maxfeerate=0, + ) if __name__ == '__main__': MempoolAcceptanceTest().main() diff --git a/test/functional/mempool_compatibility.py b/test/functional/mempool_compatibility.py index c545a7f68d..a7bdc49695 100755 --- a/test/functional/mempool_compatibility.py +++ b/test/functional/mempool_compatibility.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2017-2021 The Bitcoin Core developers +# Copyright (c) 2017-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 that mempool.dat is both backward and forward compatible between versions @@ -7,7 +7,7 @@ NOTE: The test is designed to prevent cases when compatibility is broken accidentally. In case we need to break mempool compatibility we can continue to use the test by just bumping the version number. -The previous release v0.19.1 is required by this test, see test/README.md. +Previous releases are required by this test, see test/README.md. """ import os @@ -23,23 +23,22 @@ from test_framework.wallet import ( class MempoolCompatibilityTest(BitcoinTestFramework): def set_test_params(self): self.num_nodes = 2 - self.wallet_names = [None] def skip_test_if_missing_module(self): self.skip_if_no_previous_releases() def setup_network(self): self.add_nodes(self.num_nodes, versions=[ - 190100, # oldest version with getmempoolinfo.loaded (used to avoid intermittent issues) + 200100, # Last release with previous mempool format None, ]) self.start_nodes() - self.import_deterministic_coinbase_privkeys() def run_test(self): self.log.info("Test that mempool.dat is compatible between versions") old_node, new_node = self.nodes + assert "unbroadcastcount" not in old_node.getmempoolinfo() new_wallet = MiniWallet(new_node, mode=MiniWalletMode.RAW_P2PK) self.generate(new_wallet, 1, sync_fun=self.no_op) self.generate(new_node, COINBASE_MATURITY, sync_fun=self.no_op) @@ -48,11 +47,10 @@ class MempoolCompatibilityTest(BitcoinTestFramework): # unbroadcasted_tx won't pass old_node's `MemPoolAccept::PreChecks`. self.connect_nodes(0, 1) self.sync_blocks() - recipient = old_node.getnewaddress() self.stop_node(1) self.log.info("Add a transaction to mempool on old node and shutdown") - old_tx_hash = old_node.sendtoaddress(recipient, 0.0001) + old_tx_hash = new_wallet.send_self_transfer(from_node=old_node)["txid"] assert old_tx_hash in old_node.getrawmempool() self.stop_node(0) diff --git a/test/functional/mempool_datacarrier.py b/test/functional/mempool_datacarrier.py index 13df564a37..c370d8fa91 100755 --- a/test/functional/mempool_datacarrier.py +++ b/test/functional/mempool_datacarrier.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2020-2021 The Bitcoin Core developers +# Copyright (c) 2020-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 datacarrier functionality""" @@ -44,7 +44,6 @@ class DataCarrierTest(BitcoinTestFramework): def run_test(self): self.wallet = MiniWallet(self.nodes[0]) - self.wallet.rescan_utxos() # By default, only 80 bytes are used for data (+1 for OP_RETURN, +2 for the pushdata opcodes). default_size_data = random_bytes(MAX_OP_RETURN_RELAY - 3) diff --git a/test/functional/mempool_dust.py b/test/functional/mempool_dust.py new file mode 100755 index 0000000000..41a26e82da --- /dev/null +++ b/test/functional/mempool_dust.py @@ -0,0 +1,115 @@ +#!/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 dust limit mempool policy (`-dustrelayfee` parameter)""" +from decimal import Decimal + +from test_framework.key import ECKey +from test_framework.messages import ( + COIN, + CTxOut, +) +from test_framework.script import ( + CScript, + OP_RETURN, + OP_TRUE, +) +from test_framework.script_util import ( + key_to_p2pk_script, + key_to_p2pkh_script, + key_to_p2wpkh_script, + keys_to_multisig_script, + output_key_to_p2tr_script, + program_to_witness_script, + script_to_p2sh_script, + script_to_p2wsh_script, +) +from test_framework.test_framework import BitcoinTestFramework +from test_framework.test_node import TestNode +from test_framework.util import ( + assert_equal, + get_fee, +) +from test_framework.wallet import MiniWallet + + +DUST_RELAY_TX_FEE = 3000 # default setting [sat/kvB] + + +class DustRelayFeeTest(BitcoinTestFramework): + def set_test_params(self): + self.num_nodes = 1 + + def test_dust_output(self, node: TestNode, dust_relay_fee: Decimal, + output_script: CScript, type_desc: str) -> None: + # determine dust threshold (see `GetDustThreshold`) + if output_script[0] == OP_RETURN: + dust_threshold = 0 + else: + tx_size = len(CTxOut(nValue=0, scriptPubKey=output_script).serialize()) + tx_size += 67 if output_script.IsWitnessProgram() else 148 + dust_threshold = int(get_fee(tx_size, dust_relay_fee) * COIN) + self.log.info(f"-> Test {type_desc} output (size {len(output_script)}, limit {dust_threshold})") + + # amount right on the dust threshold should pass + tx = self.wallet.create_self_transfer()["tx"] + tx.vout.append(CTxOut(nValue=dust_threshold, scriptPubKey=output_script)) + tx.vout[0].nValue -= dust_threshold # keep total output value constant + tx_good_hex = tx.serialize().hex() + res = node.testmempoolaccept([tx_good_hex])[0] + assert_equal(res['allowed'], True) + + # amount just below the dust threshold should fail + if dust_threshold > 0: + tx.vout[1].nValue -= 1 + res = node.testmempoolaccept([tx.serialize().hex()])[0] + assert_equal(res['allowed'], False) + assert_equal(res['reject-reason'], 'dust') + + # finally send the transaction to avoid running out of MiniWallet UTXOs + self.wallet.sendrawtransaction(from_node=node, tx_hex=tx_good_hex) + + def run_test(self): + self.wallet = MiniWallet(self.nodes[0]) + + # prepare output scripts of each standard type + key = ECKey() + key.generate(compressed=False) + uncompressed_pubkey = key.get_pubkey().get_bytes() + key.generate(compressed=True) + pubkey = key.get_pubkey().get_bytes() + + output_scripts = ( + (key_to_p2pk_script(uncompressed_pubkey), "P2PK (uncompressed)"), + (key_to_p2pk_script(pubkey), "P2PK (compressed)"), + (key_to_p2pkh_script(pubkey), "P2PKH"), + (script_to_p2sh_script(CScript([OP_TRUE])), "P2SH"), + (key_to_p2wpkh_script(pubkey), "P2WPKH"), + (script_to_p2wsh_script(CScript([OP_TRUE])), "P2WSH"), + (output_key_to_p2tr_script(pubkey[1:]), "P2TR"), + # witness programs for segwitv2+ can be between 2 and 40 bytes + (program_to_witness_script(2, b'\x66' * 2), "P2?? (future witness version 2)"), + (program_to_witness_script(16, b'\x77' * 40), "P2?? (future witness version 16)"), + # largest possible output script considered standard + (keys_to_multisig_script([uncompressed_pubkey]*3), "bare multisig (m-of-3)"), + (CScript([OP_RETURN, b'superimportanthash']), "null data (OP_RETURN)"), + ) + + # test default (no parameter), disabled (=0) and a bunch of arbitrary dust fee rates [sat/kvB] + for dustfee_sat_kvb in (DUST_RELAY_TX_FEE, 0, 1, 66, 500, 1337, 12345, 21212, 333333): + dustfee_btc_kvb = dustfee_sat_kvb / Decimal(COIN) + if dustfee_sat_kvb == DUST_RELAY_TX_FEE: + self.log.info(f"Test default dust limit setting ({dustfee_sat_kvb} sat/kvB)...") + else: + dust_parameter = f"-dustrelayfee={dustfee_btc_kvb:.8f}" + self.log.info(f"Test dust limit setting {dust_parameter} ({dustfee_sat_kvb} sat/kvB)...") + self.restart_node(0, extra_args=[dust_parameter]) + + for output_script, description in output_scripts: + self.test_dust_output(self.nodes[0], dustfee_btc_kvb, output_script, description) + self.generate(self.nodes[0], 1) + + +if __name__ == '__main__': + DustRelayFeeTest().main() diff --git a/test/functional/mempool_expiry.py b/test/functional/mempool_expiry.py index 21721177e6..15a5f765df 100755 --- a/test/functional/mempool_expiry.py +++ b/test/functional/mempool_expiry.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2020-2021 The Bitcoin Core developers +# Copyright (c) 2020-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. """Tests that a mempool transaction expires after a given timeout and that its @@ -12,7 +12,6 @@ definable expiry timeout via the '-mempoolexpiry=<n>' command line argument from datetime import timedelta -from test_framework.blocktools import COINBASE_MATURITY from test_framework.messages import DEFAULT_MEMPOOL_EXPIRY_HOURS from test_framework.test_framework import BitcoinTestFramework from test_framework.util import ( @@ -27,17 +26,11 @@ CUSTOM_MEMPOOL_EXPIRY = 10 # hours class MempoolExpiryTest(BitcoinTestFramework): def set_test_params(self): self.num_nodes = 1 - self.setup_clean_chain = True def test_transaction_expiry(self, timeout): """Tests that a transaction expires after the expiry timeout and its children are removed as well.""" node = self.nodes[0] - self.wallet = MiniWallet(node) - - # Add enough mature utxos to the wallet so that all txs spend confirmed coins. - self.generate(self.wallet, 4) - self.generate(node, COINBASE_MATURITY) # Send a parent transaction that will expire. parent_txid = self.wallet.send_self_transfer(from_node=node)['txid'] @@ -97,6 +90,8 @@ class MempoolExpiryTest(BitcoinTestFramework): assert_equal(half_expiry_time, node.getmempoolentry(independent_txid)['time']) def run_test(self): + self.wallet = MiniWallet(self.nodes[0]) + self.log.info('Test default mempool expiry timeout of %d hours.' % DEFAULT_MEMPOOL_EXPIRY_HOURS) self.test_transaction_expiry(DEFAULT_MEMPOOL_EXPIRY_HOURS) diff --git a/test/functional/mempool_limit.py b/test/functional/mempool_limit.py index 7080662b49..d38a37f952 100755 --- a/test/functional/mempool_limit.py +++ b/test/functional/mempool_limit.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2014-2021 The Bitcoin Core developers +# Copyright (c) 2014-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 mempool limiting together/eviction with the wallet.""" @@ -25,7 +25,6 @@ class MempoolLimitTest(BitcoinTestFramework): self.extra_args = [[ "-datacarriersize=100000", "-maxmempool=5", - "-spendzeroconfchange=0", ]] self.supports_cli = False diff --git a/test/functional/mempool_package_limits.py b/test/functional/mempool_package_limits.py index 1f12e93982..63f5a3e3d3 100755 --- a/test/functional/mempool_package_limits.py +++ b/test/functional/mempool_package_limits.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2021 The Bitcoin Core developers +# Copyright (c) 2021-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 logic for limiting mempool and package ancestors/descendants.""" @@ -45,7 +45,7 @@ class MempoolPackageLimitsTest(BitcoinTestFramework): assert_equal(0, node.getmempoolinfo()["size"]) chain_hex = [] - chaintip_utxo = self.wallet.send_self_transfer_chain(from_node=node, chain_length=mempool_count) + chaintip_utxo = self.wallet.send_self_transfer_chain(from_node=node, chain_length=mempool_count)[-1]["new_utxo"] # in-package transactions for _ in range(package_count): tx = self.wallet.create_self_transfer(utxo_to_spend=chaintip_utxo) @@ -100,13 +100,13 @@ class MempoolPackageLimitsTest(BitcoinTestFramework): package_hex = [] # Chain A (M2a... M12a) - chain_a_tip_utxo = self.wallet.send_self_transfer_chain(from_node=node, chain_length=11, utxo_to_spend=m1_utxos[0]) + chain_a_tip_utxo = self.wallet.send_self_transfer_chain(from_node=node, chain_length=11, utxo_to_spend=m1_utxos[0])[-1]["new_utxo"] # Pa pa_hex = self.wallet.create_self_transfer(utxo_to_spend=chain_a_tip_utxo)["hex"] package_hex.append(pa_hex) # Chain B (M2b... M13b) - chain_b_tip_utxo = self.wallet.send_self_transfer_chain(from_node=node, chain_length=12, utxo_to_spend=m1_utxos[1]) + chain_b_tip_utxo = self.wallet.send_self_transfer_chain(from_node=node, chain_length=12, utxo_to_spend=m1_utxos[1])[-1]["new_utxo"] # Pb pb_hex = self.wallet.create_self_transfer(utxo_to_spend=chain_b_tip_utxo)["hex"] package_hex.append(pb_hex) @@ -145,7 +145,7 @@ class MempoolPackageLimitsTest(BitcoinTestFramework): m1_utxos = self.wallet.send_self_transfer_multi(from_node=node, num_outputs=2)['new_utxos'] # Chain M2...M24 - self.wallet.send_self_transfer_chain(from_node=node, chain_length=23, utxo_to_spend=m1_utxos[0]) + self.wallet.send_self_transfer_chain(from_node=node, chain_length=23, utxo_to_spend=m1_utxos[0])[-1]["new_utxo"] # P1 p1_tx = self.wallet.create_self_transfer(utxo_to_spend=m1_utxos[1]) @@ -191,7 +191,7 @@ class MempoolPackageLimitsTest(BitcoinTestFramework): # Two chains of 13 transactions each for _ in range(2): - chain_tip_utxo = self.wallet.send_self_transfer_chain(from_node=node, chain_length=12) + chain_tip_utxo = self.wallet.send_self_transfer_chain(from_node=node, chain_length=12)[-1]["new_utxo"] # Save the 13th transaction for the package tx = self.wallet.create_self_transfer(utxo_to_spend=chain_tip_utxo) package_hex.append(tx["hex"]) @@ -234,7 +234,7 @@ class MempoolPackageLimitsTest(BitcoinTestFramework): self.log.info("Check that in-mempool and in-package ancestors are calculated properly in packages") # Two chains of 12 transactions each for _ in range(2): - chaintip_utxo = self.wallet.send_self_transfer_chain(from_node=node, chain_length=12) + chaintip_utxo = self.wallet.send_self_transfer_chain(from_node=node, chain_length=12)[-1]["new_utxo"] # last 2 transactions will be the parents of Pc pc_parent_utxos.append(chaintip_utxo) diff --git a/test/functional/mempool_package_onemore.py b/test/functional/mempool_package_onemore.py index 9a981bd5a5..921c190668 100755 --- a/test/functional/mempool_package_onemore.py +++ b/test/functional/mempool_package_onemore.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2014-2021 The Bitcoin Core developers +# Copyright (c) 2014-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 descendant package tracking carve-out allowing one final transaction in @@ -31,7 +31,6 @@ class MempoolPackagesTest(BitcoinTestFramework): def run_test(self): self.wallet = MiniWallet(self.nodes[0]) - self.wallet.rescan_utxos() # DEFAULT_ANCESTOR_LIMIT transactions off a confirmed tx should be fine chain = [] diff --git a/test/functional/mempool_packages.py b/test/functional/mempool_packages.py index def0b1fce4..0387282862 100755 --- a/test/functional/mempool_packages.py +++ b/test/functional/mempool_packages.py @@ -1,12 +1,11 @@ #!/usr/bin/env python3 -# Copyright (c) 2014-2021 The Bitcoin Core developers +# Copyright (c) 2014-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 descendant package tracking code.""" from decimal import Decimal -from test_framework.blocktools import COINBASE_MATURITY from test_framework.messages import ( COIN, DEFAULT_ANCESTOR_LIMIT, @@ -17,9 +16,8 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.util import ( assert_equal, assert_raises_rpc_error, - chain_transaction, ) - +from test_framework.wallet import MiniWallet # custom limits for node1 CUSTOM_ANCESTOR_LIMIT = 5 @@ -28,6 +26,9 @@ assert CUSTOM_DESCENDANT_LIMIT >= CUSTOM_ANCESTOR_LIMIT class MempoolPackagesTest(BitcoinTestFramework): + def add_options(self, parser): + self.add_wallet_options(parser) + def set_test_params(self): self.num_nodes = 2 self.extra_args = [ @@ -42,43 +43,33 @@ class MempoolPackagesTest(BitcoinTestFramework): ], ] - def skip_test_if_missing_module(self): - self.skip_if_no_wallet() - def run_test(self): - # Mine some blocks and have them mature. + self.wallet = MiniWallet(self.nodes[0]) + self.wallet.rescan_utxos() + + if self.is_specified_wallet_compiled(): + self.nodes[0].createwallet("watch_wallet", disable_private_keys=True) + self.nodes[0].importaddress(self.wallet.get_address()) + peer_inv_store = self.nodes[0].add_p2p_connection(P2PTxInvStore()) # keep track of invs - self.generate(self.nodes[0], COINBASE_MATURITY + 1) - utxo = self.nodes[0].listunspent(10) - txid = utxo[0]['txid'] - vout = utxo[0]['vout'] - value = utxo[0]['amount'] - assert 'ancestorcount' not in utxo[0] - assert 'ancestorsize' not in utxo[0] - assert 'ancestorfees' not in utxo[0] - - fee = Decimal("0.0001") + # DEFAULT_ANCESTOR_LIMIT transactions off a confirmed tx should be fine - chain = [] - witness_chain = [] + chain = self.wallet.create_self_transfer_chain(chain_length=DEFAULT_ANCESTOR_LIMIT) + witness_chain = [t["wtxid"] for t in chain] ancestor_vsize = 0 ancestor_fees = Decimal(0) - for i in range(DEFAULT_ANCESTOR_LIMIT): - (txid, sent_value) = chain_transaction(self.nodes[0], [txid], [0], value, fee, 1) - value = sent_value - chain.append(txid) - # We need the wtxids to check P2P announcements - witnesstx = self.nodes[0].gettransaction(txid=txid, verbose=True)['decoded'] - witness_chain.append(witnesstx['hash']) + for i, t in enumerate(chain): + ancestor_vsize += t["tx"].get_vsize() + ancestor_fees += t["fee"] + self.wallet.sendrawtransaction(from_node=self.nodes[0], tx_hex=t["hex"]) # Check that listunspent ancestor{count, size, fees} yield the correct results - wallet_unspent = self.nodes[0].listunspent(minconf=0) - this_unspent = next(utxo_info for utxo_info in wallet_unspent if utxo_info['txid'] == txid) - assert_equal(this_unspent['ancestorcount'], i + 1) - ancestor_vsize += self.nodes[0].getrawtransaction(txid=txid, verbose=True)['vsize'] - assert_equal(this_unspent['ancestorsize'], ancestor_vsize) - ancestor_fees -= self.nodes[0].gettransaction(txid=txid)['fee'] - assert_equal(this_unspent['ancestorfees'], ancestor_fees * COIN) + if self.is_specified_wallet_compiled(): + wallet_unspent = self.nodes[0].listunspent(minconf=0) + this_unspent = next(utxo_info for utxo_info in wallet_unspent if utxo_info["txid"] == t["txid"]) + assert_equal(this_unspent['ancestorcount'], i + 1) + assert_equal(this_unspent['ancestorsize'], ancestor_vsize) + assert_equal(this_unspent['ancestorfees'], ancestor_fees * COIN) # Wait until mempool transactions have passed initial broadcast (sent inv and received getdata) # Otherwise, getrawmempool may be inconsistent with getmempoolentry if unbroadcast changes in between @@ -96,15 +87,20 @@ class MempoolPackagesTest(BitcoinTestFramework): ancestor_count = DEFAULT_ANCESTOR_LIMIT assert_equal(ancestor_fees, sum([mempool[tx]['fees']['base'] for tx in mempool])) + # Adding one more transaction on to the chain should fail. + next_hop = self.wallet.create_self_transfer(utxo_to_spend=chain[-1]["new_utxo"])["hex"] + assert_raises_rpc_error(-26, "too-long-mempool-chain", lambda: self.nodes[0].sendrawtransaction(next_hop)) + descendants = [] - ancestors = list(chain) + ancestors = [t["txid"] for t in chain] + chain = [t["txid"] for t in chain] for x in reversed(chain): # Check that getmempoolentry is consistent with getrawmempool entry = self.nodes[0].getmempoolentry(x) assert_equal(entry, mempool[x]) # Check that gettxspendingprevout is consistent with getrawmempool - witnesstx = self.nodes[0].gettransaction(txid=x, verbose=True)['decoded'] + witnesstx = self.nodes[0].getrawtransaction(txid=x, verbose=True) for tx_in in witnesstx["vin"]: spending_result = self.nodes[0].gettxspendingprevout([ {'txid' : tx_in["txid"], 'vout' : tx_in["vout"]} ]) assert_equal(spending_result, [ {'txid' : tx_in["txid"], 'vout' : tx_in["vout"], 'spendingtxid' : x} ]) @@ -190,9 +186,6 @@ class MempoolPackagesTest(BitcoinTestFramework): descendant_fees += entry['fees']['base'] assert_equal(entry['fees']['descendant'], descendant_fees + Decimal('0.00001')) - # Adding one more transaction on to the chain should fail. - assert_raises_rpc_error(-26, "too-long-mempool-chain", chain_transaction, self.nodes[0], [txid], [vout], value, fee, 1) - # Check that prioritising a tx before it's added to the mempool works # First clear the mempool by mining a block. self.generate(self.nodes[0], 1) @@ -229,28 +222,23 @@ class MempoolPackagesTest(BitcoinTestFramework): # TODO: test ancestor size limits # Now test descendant chain limits - txid = utxo[1]['txid'] - value = utxo[1]['amount'] - vout = utxo[1]['vout'] - transaction_package = [] tx_children = [] # First create one parent tx with 10 children - (txid, sent_value) = chain_transaction(self.nodes[0], [txid], [vout], value, fee, 10) - parent_transaction = txid - for i in range(10): - transaction_package.append({'txid': txid, 'vout': i, 'amount': sent_value}) + tx_with_children = self.wallet.send_self_transfer_multi(from_node=self.nodes[0], num_outputs=10) + parent_transaction = tx_with_children["txid"] + transaction_package = tx_with_children["new_utxos"] # Sign and send up to MAX_DESCENDANT transactions chained off the parent tx chain = [] # save sent txs for the purpose of checking node1's mempool later (see below) for _ in range(DEFAULT_DESCENDANT_LIMIT - 1): utxo = transaction_package.pop(0) - (txid, sent_value) = chain_transaction(self.nodes[0], [utxo['txid']], [utxo['vout']], utxo['amount'], fee, 10) + new_tx = self.wallet.send_self_transfer_multi(from_node=self.nodes[0], num_outputs=10, utxos_to_spend=[utxo]) + txid = new_tx["txid"] chain.append(txid) if utxo['txid'] is parent_transaction: tx_children.append(txid) - for j in range(10): - transaction_package.append({'txid': txid, 'vout': j, 'amount': sent_value}) + transaction_package.extend(new_tx["new_utxos"]) mempool = self.nodes[0].getrawmempool(True) assert_equal(mempool[parent_transaction]['descendantcount'], DEFAULT_DESCENDANT_LIMIT) @@ -260,8 +248,8 @@ class MempoolPackagesTest(BitcoinTestFramework): assert_equal(mempool[child]['depends'], [parent_transaction]) # Sending one more chained transaction will fail - utxo = transaction_package.pop(0) - assert_raises_rpc_error(-26, "too-long-mempool-chain", chain_transaction, self.nodes[0], [utxo['txid']], [utxo['vout']], utxo['amount'], fee, 10) + next_hop = self.wallet.create_self_transfer(utxo_to_spend=transaction_package.pop(0))["hex"] + assert_raises_rpc_error(-26, "too-long-mempool-chain", lambda: self.nodes[0].sendrawtransaction(next_hop)) # Check that node1's mempool is as expected, containing: # - txs from previous ancestor test (-> custom ancestor limit) @@ -301,42 +289,19 @@ class MempoolPackagesTest(BitcoinTestFramework): # last block. # Create tx0 with 2 outputs - utxo = self.nodes[0].listunspent() - txid = utxo[0]['txid'] - value = utxo[0]['amount'] - vout = utxo[0]['vout'] - - send_value = (value - fee) / 2 - inputs = [ {'txid' : txid, 'vout' : vout} ] - outputs = {} - for _ in range(2): - outputs[self.nodes[0].getnewaddress()] = send_value - rawtx = self.nodes[0].createrawtransaction(inputs, outputs) - signedtx = self.nodes[0].signrawtransactionwithwallet(rawtx) - txid = self.nodes[0].sendrawtransaction(signedtx['hex']) - tx0_id = txid - value = send_value + tx0 = self.wallet.send_self_transfer_multi(from_node=self.nodes[0], num_outputs=2) # Create tx1 - tx1_id, _ = chain_transaction(self.nodes[0], [tx0_id], [0], value, fee, 1) + tx1 = self.wallet.send_self_transfer(from_node=self.nodes[0], utxo_to_spend=tx0["new_utxos"][0]) # Create tx2-7 - vout = 1 - txid = tx0_id - for _ in range(6): - (txid, sent_value) = chain_transaction(self.nodes[0], [txid], [vout], value, fee, 1) - vout = 0 - value = sent_value + tx7 = self.wallet.send_self_transfer_chain(from_node=self.nodes[0], utxo_to_spend=tx0["new_utxos"][1], chain_length=6)[-1] # Mine these in a block self.generate(self.nodes[0], 1) # Now generate tx8, with a big fee - inputs = [ {'txid' : tx1_id, 'vout': 0}, {'txid' : txid, 'vout': 0} ] - outputs = { self.nodes[0].getnewaddress() : send_value + value - 4*fee } - rawtx = self.nodes[0].createrawtransaction(inputs, outputs) - signedtx = self.nodes[0].signrawtransactionwithwallet(rawtx) - txid = self.nodes[0].sendrawtransaction(signedtx['hex']) + self.wallet.send_self_transfer_multi(from_node=self.nodes[0], utxos_to_spend=[tx1["new_utxo"], tx7["new_utxo"]], fee_per_output=40000) self.sync_mempools() # Now try to disconnect the tip on each node... diff --git a/test/functional/mempool_persist.py b/test/functional/mempool_persist.py index b6fa7fbd91..f818801136 100755 --- a/test/functional/mempool_persist.py +++ b/test/functional/mempool_persist.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2014-2021 The Bitcoin Core developers +# Copyright (c) 2014-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 mempool persistence. @@ -50,13 +50,15 @@ from test_framework.wallet import MiniWallet class MempoolPersistTest(BitcoinTestFramework): + def add_options(self, parser): + self.add_wallet_options(parser, legacy=False) + def set_test_params(self): self.num_nodes = 3 self.extra_args = [[], ["-persistmempool=0"], []] def run_test(self): self.mini_wallet = MiniWallet(self.nodes[2]) - self.mini_wallet.rescan_utxos() if self.is_sqlite_compiled(): self.nodes[2].createwallet( wallet_name="watch", diff --git a/test/functional/mempool_reorg.py b/test/functional/mempool_reorg.py index 47ff520713..3a5bc1ebcd 100755 --- a/test/functional/mempool_reorg.py +++ b/test/functional/mempool_reorg.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2014-2021 The Bitcoin Core developers +# Copyright (c) 2014-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 mempool re-org scenarios. @@ -31,7 +31,6 @@ class MempoolCoinbaseTest(BitcoinTestFramework): self.log.info("Add 4 coinbase utxos to the miniwallet") # Block 76 contains the first spendable coinbase txs. first_block = 76 - wallet.rescan_utxos() # Three scenarios for re-orging coinbase spends in the memory pool: # 1. Direct coinbase spend : spend_1 diff --git a/test/functional/mempool_resurrect.py b/test/functional/mempool_resurrect.py index 3e610d02ac..c10052372d 100755 --- a/test/functional/mempool_resurrect.py +++ b/test/functional/mempool_resurrect.py @@ -4,7 +4,6 @@ # file COPYING or http://www.opensource.org/licenses/mit-license.php. """Test resurrection of mined transactions when the blockchain is re-organized.""" -from test_framework.blocktools import COINBASE_MATURITY from test_framework.test_framework import BitcoinTestFramework from test_framework.util import assert_equal from test_framework.wallet import MiniWallet @@ -13,16 +12,11 @@ from test_framework.wallet import MiniWallet class MempoolCoinbaseTest(BitcoinTestFramework): def set_test_params(self): self.num_nodes = 1 - self.setup_clean_chain = True def run_test(self): node = self.nodes[0] wallet = MiniWallet(node) - # Add enough mature utxos to the wallet so that all txs spend confirmed coins - self.generate(wallet, 3) - self.generate(node, COINBASE_MATURITY) - # Spend block 1/2/3's coinbase transactions # Mine a block # Create three more transactions, spending the spends diff --git a/test/functional/mempool_spend_coinbase.py b/test/functional/mempool_spend_coinbase.py index 3585871350..a7cb2ba602 100755 --- a/test/functional/mempool_spend_coinbase.py +++ b/test/functional/mempool_spend_coinbase.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2014-2021 The Bitcoin Core developers +# Copyright (c) 2014-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 spending coinbase transactions. @@ -28,7 +28,6 @@ class MempoolSpendCoinbaseTest(BitcoinTestFramework): chain_height = 198 self.nodes[0].invalidateblock(self.nodes[0].getblockhash(chain_height + 1)) assert_equal(chain_height, self.nodes[0].getblockcount()) - wallet.rescan_utxos() # Coinbase at height chain_height-100+1 ok in mempool, should # get mined. Coinbase at height chain_height-100+2 is diff --git a/test/functional/mempool_unbroadcast.py b/test/functional/mempool_unbroadcast.py index 7587adc257..12de750731 100755 --- a/test/functional/mempool_unbroadcast.py +++ b/test/functional/mempool_unbroadcast.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2017-2021 The Bitcoin Core developers +# Copyright (c) 2017-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 that the mempool ensures transaction delivery by periodically sending @@ -15,14 +15,14 @@ from test_framework.wallet import MiniWallet MAX_INITIAL_BROADCAST_DELAY = 15 * 60 # 15 minutes in seconds class MempoolUnbroadcastTest(BitcoinTestFramework): + def add_options(self, parser): + self.add_wallet_options(parser) + def set_test_params(self): self.num_nodes = 2 - if self.is_wallet_compiled(): - self.requires_wallet = True def run_test(self): self.wallet = MiniWallet(self.nodes[0]) - self.wallet.rescan_utxos() self.test_broadcast() self.test_txn_removal() @@ -35,6 +35,7 @@ class MempoolUnbroadcastTest(BitcoinTestFramework): self.log.info("Generate transactions that only node 0 knows about") if self.is_wallet_compiled(): + self.import_deterministic_coinbase_privkeys() # generate a wallet txn addr = node.getnewaddress() wallet_tx_hsh = node.sendtoaddress(addr, 0.0001) diff --git a/test/functional/mempool_updatefromblock.py b/test/functional/mempool_updatefromblock.py index f97c2223a6..68cbb5dbed 100755 --- a/test/functional/mempool_updatefromblock.py +++ b/test/functional/mempool_updatefromblock.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2020-2021 The Bitcoin Core developers +# Copyright (c) 2020-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 mempool descendants/ancestors information update. diff --git a/test/functional/mining_basic.py b/test/functional/mining_basic.py index ac7eb96ac1..332099516c 100755 --- a/test/functional/mining_basic.py +++ b/test/functional/mining_basic.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2014-2021 The Bitcoin Core developers +# Copyright (c) 2014-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 mining RPCs diff --git a/test/functional/mining_getblocktemplate_longpoll.py b/test/functional/mining_getblocktemplate_longpoll.py index e928ee4936..ec492f9e72 100755 --- a/test/functional/mining_getblocktemplate_longpoll.py +++ b/test/functional/mining_getblocktemplate_longpoll.py @@ -8,7 +8,6 @@ from decimal import Decimal import random import threading -from test_framework.blocktools import COINBASE_MATURITY from test_framework.test_framework import BitcoinTestFramework from test_framework.util import get_rpc_proxy from test_framework.wallet import MiniWallet @@ -62,9 +61,6 @@ class GetBlockTemplateLPTest(BitcoinTestFramework): thr.join(5) # wait 5 seconds or until thread exits assert not thr.is_alive() - # Add enough mature utxos to the wallets, so that all txs spend confirmed coins - self.generate(self.nodes[0], COINBASE_MATURITY) - self.log.info("Test that introducing a new transaction into the mempool will terminate the longpoll") thr = LongpollThread(self.nodes[0]) thr.start() diff --git a/test/functional/mining_prioritisetransaction.py b/test/functional/mining_prioritisetransaction.py index 581cf5896e..a4481c15a0 100755 --- a/test/functional/mining_prioritisetransaction.py +++ b/test/functional/mining_prioritisetransaction.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2021 The Bitcoin Core developers +# Copyright (c) 2015-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 the prioritisetransaction mining RPC.""" @@ -106,7 +106,6 @@ class PrioritiseTransactionTest(BitcoinTestFramework): def run_test(self): self.wallet = MiniWallet(self.nodes[0]) - self.wallet.rescan_utxos() # Test `prioritisetransaction` required parameters assert_raises_rpc_error(-1, "prioritisetransaction", self.nodes[0].prioritisetransaction) diff --git a/test/functional/mocks/invalid_signer.py b/test/functional/mocks/invalid_signer.py index 14f9fed72e..7bfa9051ac 100755 --- a/test/functional/mocks/invalid_signer.py +++ b/test/functional/mocks/invalid_signer.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2018-2021 The Bitcoin Core developers +# Copyright (c) 2018-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. @@ -10,7 +10,7 @@ import json def perform_pre_checks(): mock_result_path = os.path.join(os.getcwd(), "mock_result") - if(os.path.isfile(mock_result_path)): + if os.path.isfile(mock_result_path): with open(mock_result_path, "r", encoding="utf8") as f: mock_result = f.read() if mock_result[0]: diff --git a/test/functional/mocks/signer.py b/test/functional/mocks/signer.py index 6699914249..5f4fad6380 100755 --- a/test/functional/mocks/signer.py +++ b/test/functional/mocks/signer.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2018-2021 The Bitcoin Core developers +# Copyright (c) 2018-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. @@ -10,7 +10,7 @@ import json def perform_pre_checks(): mock_result_path = os.path.join(os.getcwd(), "mock_result") - if(os.path.isfile(mock_result_path)): + if os.path.isfile(mock_result_path): with open(mock_result_path, "r", encoding="utf8") as f: mock_result = f.read() if mock_result[0]: diff --git a/test/functional/p2p_addr_relay.py b/test/functional/p2p_addr_relay.py index e2e9b6dcb2..e002a520c6 100755 --- a/test/functional/p2p_addr_relay.py +++ b/test/functional/p2p_addr_relay.py @@ -49,7 +49,7 @@ class AddrReceiver(P2PInterface): def on_addr(self, message): for addr in message.addrs: self.num_ipv4_received += 1 - if(self.test_addr_contents): + if self.test_addr_contents: # relay_tests checks the content of the addr messages match # expectations based on the message creation in setup_addr_msg assert_equal(addr.nServices, 9) diff --git a/test/functional/p2p_blockfilters.py b/test/functional/p2p_blockfilters.py index ef12b5f6b7..2da9037a69 100755 --- a/test/functional/p2p_blockfilters.py +++ b/test/functional/p2p_blockfilters.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2019-2021 The Bitcoin Core developers +# Copyright (c) 2019-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. """Tests NODE_COMPACT_FILTERS (BIP 157/158). diff --git a/test/functional/p2p_blocksonly.py b/test/functional/p2p_blocksonly.py index 231d2e12c9..110a1bd03f 100755 --- a/test/functional/p2p_blocksonly.py +++ b/test/functional/p2p_blocksonly.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2019-2021 The Bitcoin Core developers +# Copyright (c) 2019-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 p2p blocksonly mode & block-relay-only connections.""" @@ -20,8 +20,6 @@ class P2PBlocksOnly(BitcoinTestFramework): def run_test(self): self.miniwallet = MiniWallet(self.nodes[0]) - # Add enough mature utxos to the wallet, so that all txs spend confirmed coins - self.miniwallet.rescan_utxos() self.blocksonly_mode_tests() self.blocks_relay_conn_tests() @@ -104,7 +102,7 @@ class P2PBlocksOnly(BitcoinTestFramework): self.nodes[0].setmocktime(int(time.time()) + 60) conn.sync_send_with_ping() - assert(int(txid, 16) not in conn.get_invs()) + assert int(txid, 16) not in conn.get_invs() def check_p2p_inv_violation(self, peer): self.log.info("Check that tx-invs from P2P are rejected and result in disconnect") diff --git a/test/functional/p2p_compactblocks.py b/test/functional/p2p_compactblocks.py index 3cbb948e3c..23eeea50bc 100755 --- a/test/functional/p2p_compactblocks.py +++ b/test/functional/p2p_compactblocks.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2016-2021 The Bitcoin Core developers +# Copyright (c) 2016-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 compact blocks (BIP 152).""" diff --git a/test/functional/p2p_disconnect_ban.py b/test/functional/p2p_disconnect_ban.py index 7284ecde83..b2f0659eda 100755 --- a/test/functional/p2p_disconnect_ban.py +++ b/test/functional/p2p_disconnect_ban.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2014-2020 The Bitcoin Core developers +# Copyright (c) 2014-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 node disconnect and ban behavior""" @@ -46,6 +46,9 @@ class DisconnectBanTest(BitcoinTestFramework): assert_raises_rpc_error(-30, "Error: Invalid IP/Subnet", self.nodes[1].setban, "127.0.0.1/42", "add") assert_equal(len(self.nodes[1].listbanned()), 1) # still only one banned ip because 127.0.0.1 is within the range of 127.0.0.0/24 + self.log.info("setban: fail to ban with past absolute timestamp") + assert_raises_rpc_error(-8, "Error: Absolute timestamp is in the past", self.nodes[1].setban, "127.27.0.1", "add", 123, True) + self.log.info("setban remove: fail to unban a non-banned subnet") assert_raises_rpc_error(-30, "Error: Unban failed", self.nodes[1].setban, "127.0.0.1", "remove") assert_equal(len(self.nodes[1].listbanned()), 1) @@ -66,9 +69,13 @@ class DisconnectBanTest(BitcoinTestFramework): self.nodes[1].setban("2001:4d48:ac57:400:cacf:e9ff:fe1d:9c63/19", "add", 1000) # ban for 1000 seconds listBeforeShutdown = self.nodes[1].listbanned() assert_equal("192.168.0.1/32", listBeforeShutdown[2]['address']) + + self.log.info("setban: test banning with absolute timestamp") + self.nodes[1].setban("192.168.0.2", "add", old_time + 120, True) + # Move time forward by 3 seconds so the third ban has expired self.nodes[1].setmocktime(old_time + 3) - assert_equal(len(self.nodes[1].listbanned()), 3) + assert_equal(len(self.nodes[1].listbanned()), 4) self.log.info("Test ban_duration and time_remaining") for ban in self.nodes[1].listbanned(): @@ -78,13 +85,17 @@ class DisconnectBanTest(BitcoinTestFramework): elif ban["address"] == "2001:4d48:ac57:400:cacf:e9ff:fe1d:9c63/19": assert_equal(ban["ban_duration"], 1000) assert_equal(ban["time_remaining"], 997) + elif ban["address"] == "192.168.0.2/32": + assert_equal(ban["ban_duration"], 120) + assert_equal(ban["time_remaining"], 117) self.restart_node(1) listAfterShutdown = self.nodes[1].listbanned() assert_equal("127.0.0.0/24", listAfterShutdown[0]['address']) assert_equal("127.0.0.0/32", listAfterShutdown[1]['address']) - assert_equal("/19" in listAfterShutdown[2]['address'], True) + assert_equal("192.168.0.2/32", listAfterShutdown[2]['address']) + assert_equal("/19" in listAfterShutdown[3]['address'], True) # Clear ban lists self.nodes[1].clearbanned() @@ -96,7 +107,7 @@ class DisconnectBanTest(BitcoinTestFramework): self.log.info("disconnectnode: fail to disconnect when calling with address and nodeid") address1 = self.nodes[0].getpeerinfo()[0]['addr'] - node1 = self.nodes[0].getpeerinfo()[0]['addr'] + node1 = self.nodes[0].getpeerinfo()[0]["id"] assert_raises_rpc_error(-32602, "Only one of address and nodeid should be provided.", self.nodes[0].disconnectnode, address=address1, nodeid=node1) self.log.info("disconnectnode: fail to disconnect when calling with junk address") diff --git a/test/functional/p2p_dos_header_tree.py b/test/functional/p2p_dos_header_tree.py index 1f904644fc..4b4346af49 100755 --- a/test/functional/p2p_dos_header_tree.py +++ b/test/functional/p2p_dos_header_tree.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2019-2021 The Bitcoin Core developers +# Copyright (c) 2019-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 that we reject low difficulty headers to prevent our block tree from filling up with useless bloat""" diff --git a/test/functional/p2p_feefilter.py b/test/functional/p2p_feefilter.py index b65e927d5b..6b03cdf877 100755 --- a/test/functional/p2p_feefilter.py +++ b/test/functional/p2p_feefilter.py @@ -6,7 +6,6 @@ from decimal import Decimal -from test_framework.blocktools import COINBASE_MATURITY from test_framework.messages import MSG_TX, MSG_WTX, msg_feefilter from test_framework.p2p import P2PInterface, p2p_lock from test_framework.test_framework import BitcoinTestFramework @@ -80,9 +79,6 @@ class FeeFilterTest(BitcoinTestFramework): node1 = self.nodes[1] node0 = self.nodes[0] miniwallet = MiniWallet(node1) - # Add enough mature utxos to the wallet, so that all txs spend confirmed coins - self.generate(miniwallet, 5) - self.generate(node1, COINBASE_MATURITY) conn = self.nodes[0].add_p2p_connection(TestP2PConn()) diff --git a/test/functional/p2p_filter.py b/test/functional/p2p_filter.py index 3cf92b0316..b3e68ca536 100755 --- a/test/functional/p2p_filter.py +++ b/test/functional/p2p_filter.py @@ -214,7 +214,6 @@ class FilterTest(BitcoinTestFramework): def run_test(self): self.wallet = MiniWallet(self.nodes[0]) - self.wallet.rescan_utxos() filter_peer = self.nodes[0].add_p2p_connection(P2PBloomFilter()) self.log.info('Test filter size limits') diff --git a/test/functional/p2p_getaddr_caching.py b/test/functional/p2p_getaddr_caching.py index 8907c34a89..1c9ad7289b 100755 --- a/test/functional/p2p_getaddr_caching.py +++ b/test/functional/p2p_getaddr_caching.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2020 The Bitcoin Core developers +# Copyright (c) 2020-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 addr response caching""" @@ -60,7 +60,7 @@ class AddrTest(BitcoinTestFramework): # Need to make sure we hit MAX_ADDR_TO_SEND records in the addr response later because # only a fraction of all known addresses can be cached and returned. - assert(len(self.nodes[0].getnodeaddresses(0)) > int(MAX_ADDR_TO_SEND / (MAX_PCT_ADDR_TO_SEND / 100))) + assert len(self.nodes[0].getnodeaddresses(0)) > int(MAX_ADDR_TO_SEND / (MAX_PCT_ADDR_TO_SEND / 100)) last_response_on_local_bind = None last_response_on_onion_bind1 = None @@ -85,9 +85,9 @@ class AddrTest(BitcoinTestFramework): if i > 0: # Responses from different binds should be unique - assert(last_response_on_local_bind != addr_receiver_onion1.get_received_addrs()) - assert(last_response_on_local_bind != addr_receiver_onion2.get_received_addrs()) - assert(last_response_on_onion_bind1 != addr_receiver_onion2.get_received_addrs()) + assert last_response_on_local_bind != addr_receiver_onion1.get_received_addrs() + assert last_response_on_local_bind != addr_receiver_onion2.get_received_addrs() + assert last_response_on_onion_bind1 != addr_receiver_onion2.get_received_addrs() # Responses on from the same bind should be the same assert_equal(last_response_on_local_bind, addr_receiver_local.get_received_addrs()) assert_equal(last_response_on_onion_bind1, addr_receiver_onion1.get_received_addrs()) @@ -119,9 +119,9 @@ class AddrTest(BitcoinTestFramework): addr_receiver_onion2.wait_until(addr_receiver_onion2.addr_received) # new response is different - assert(set(last_response_on_local_bind) != set(addr_receiver_local.get_received_addrs())) - assert(set(last_response_on_onion_bind1) != set(addr_receiver_onion1.get_received_addrs())) - assert(set(last_response_on_onion_bind2) != set(addr_receiver_onion2.get_received_addrs())) + assert set(last_response_on_local_bind) != set(addr_receiver_local.get_received_addrs()) + assert set(last_response_on_onion_bind1) != set(addr_receiver_onion1.get_received_addrs()) + assert set(last_response_on_onion_bind2) != set(addr_receiver_onion2.get_received_addrs()) if __name__ == '__main__': AddrTest().main() diff --git a/test/functional/p2p_headers_sync_with_minchainwork.py b/test/functional/p2p_headers_sync_with_minchainwork.py index 991e3348ed..b07077c668 100755 --- a/test/functional/p2p_headers_sync_with_minchainwork.py +++ b/test/functional/p2p_headers_sync_with_minchainwork.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2019-2021 The Bitcoin Core developers +# Copyright (c) 2019-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 that we reject low difficulty headers to prevent our block tree from filling up with useless bloat""" @@ -57,7 +57,7 @@ class RejectLowDifficultyHeadersTest(BitcoinTestFramework): def check_node3_chaintips(num_tips, tip_hash, height): node3_chaintips = self.nodes[3].getchaintips() - assert(len(node3_chaintips) == num_tips) + assert len(node3_chaintips) == num_tips assert { 'height': height, 'hash': tip_hash, @@ -69,7 +69,7 @@ class RejectLowDifficultyHeadersTest(BitcoinTestFramework): for node in self.nodes[1:3]: chaintips = node.getchaintips() - assert(len(chaintips) == 1) + assert len(chaintips) == 1 assert { 'height': 0, 'hash': '0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206', @@ -89,7 +89,7 @@ class RejectLowDifficultyHeadersTest(BitcoinTestFramework): 'status': 'active', } in self.nodes[2].getchaintips() - assert(len(self.nodes[2].getchaintips()) == 1) + assert len(self.nodes[2].getchaintips()) == 1 self.log.info("Check that node3 accepted these headers as well") check_node3_chaintips(2, self.nodes[0].getbestblockhash(), NODE1_BLOCKS_REQUIRED) diff --git a/test/functional/p2p_i2p_sessions.py b/test/functional/p2p_i2p_sessions.py index 4e52522b81..9e7fdc6e14 100755 --- a/test/functional/p2p_i2p_sessions.py +++ b/test/functional/p2p_i2p_sessions.py @@ -23,12 +23,12 @@ class I2PSessions(BitcoinTestFramework): self.log.info("Ensure we create a persistent session when -i2pacceptincoming=1") node0 = self.nodes[0] - with node0.assert_debug_log(expected_msgs=[f"Creating persistent SAM session"]): + with node0.assert_debug_log(expected_msgs=["Creating persistent SAM session"]): node0.addnode(node=addr, command="onetry") self.log.info("Ensure we create a transient session when -i2pacceptincoming=0") node1 = self.nodes[1] - with node1.assert_debug_log(expected_msgs=[f"Creating transient SAM session"]): + with node1.assert_debug_log(expected_msgs=["Creating transient SAM session"]): node1.addnode(node=addr, command="onetry") diff --git a/test/functional/p2p_invalid_tx.py b/test/functional/p2p_invalid_tx.py index 28efd5a81e..ae9dc816ab 100755 --- a/test/functional/p2p_invalid_tx.py +++ b/test/functional/p2p_invalid_tx.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2021 The Bitcoin Core developers +# Copyright (c) 2015-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 node responses to invalid transactions. diff --git a/test/functional/p2p_leak.py b/test/functional/p2p_leak.py index 936c22197c..645488f24d 100755 --- a/test/functional/p2p_leak.py +++ b/test/functional/p2p_leak.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2017-2021 The Bitcoin Core developers +# Copyright (c) 2017-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 message sending before handshake completion. diff --git a/test/functional/p2p_leak_tx.py b/test/functional/p2p_leak_tx.py index 4c064b359e..6283dd89ac 100755 --- a/test/functional/p2p_leak_tx.py +++ b/test/functional/p2p_leak_tx.py @@ -4,7 +4,6 @@ # file COPYING or http://www.opensource.org/licenses/mit-license.php. """Test that we don't leak txs to inbound peers that we haven't yet announced to""" -from test_framework.blocktools import COINBASE_MATURITY from test_framework.messages import msg_getdata, CInv, MSG_TX from test_framework.p2p import p2p_lock, P2PDataStore from test_framework.test_framework import BitcoinTestFramework @@ -26,9 +25,6 @@ class P2PLeakTxTest(BitcoinTestFramework): def run_test(self): gen_node = self.nodes[0] # The block and tx generating node miniwallet = MiniWallet(gen_node) - # Add enough mature utxos to the wallet, so that all txs spend confirmed coins - self.generate(miniwallet, 1) - self.generate(gen_node, COINBASE_MATURITY) inbound_peer = self.nodes[0].add_p2p_connection(P2PNode()) # An "attacking" inbound peer diff --git a/test/functional/p2p_message_capture.py b/test/functional/p2p_message_capture.py index 87c77f4540..3ab0b79ba2 100755 --- a/test/functional/p2p_message_capture.py +++ b/test/functional/p2p_message_capture.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2020-2021 The Bitcoin Core developers +# Copyright (c) 2020-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 per-peer message capture capability. @@ -36,7 +36,7 @@ def mini_parser(dat_file): """ with open(dat_file, 'rb') as f_in: # This should have at least one message in it - assert(os.fstat(f_in.fileno()).st_size >= TIME_SIZE + LENGTH_SIZE + MSGTYPE_SIZE) + assert os.fstat(f_in.fileno()).st_size >= TIME_SIZE + LENGTH_SIZE + MSGTYPE_SIZE while True: tmp_header_raw = f_in.read(TIME_SIZE + LENGTH_SIZE + MSGTYPE_SIZE) if not tmp_header_raw: @@ -44,7 +44,7 @@ def mini_parser(dat_file): tmp_header = BytesIO(tmp_header_raw) tmp_header.read(TIME_SIZE) # skip the timestamp field msgtype = tmp_header.read(MSGTYPE_SIZE).rstrip(b'\x00') - assert(msgtype in MESSAGEMAP) + assert msgtype in MESSAGEMAP length: int = int.from_bytes(tmp_header.read(LENGTH_SIZE), "little") data = f_in.read(length) assert_equal(len(data), length) diff --git a/test/functional/p2p_permissions.py b/test/functional/p2p_permissions.py index 453a0920cc..41324682fc 100755 --- a/test/functional/p2p_permissions.py +++ b/test/functional/p2p_permissions.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2021 The Bitcoin Core developers +# Copyright (c) 2015-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 p2p permission message. @@ -7,30 +7,26 @@ Test that permissions are correctly calculated and applied """ -from test_framework.address import ADDRESS_BCRT1_P2WSH_OP_TRUE from test_framework.messages import ( - CTxInWitness, - tx_from_hex, + SEQUENCE_FINAL, ) from test_framework.p2p import P2PDataStore -from test_framework.script import ( - CScript, - OP_TRUE, -) from test_framework.test_node import ErrorMatch from test_framework.test_framework import BitcoinTestFramework from test_framework.util import ( assert_equal, p2p_port, ) +from test_framework.wallet import MiniWallet class P2PPermissionsTests(BitcoinTestFramework): def set_test_params(self): self.num_nodes = 2 - self.setup_clean_chain = True def run_test(self): + self.wallet = MiniWallet(self.nodes[0]) + self.check_tx_relay() self.checkpermission( @@ -94,8 +90,6 @@ class P2PPermissionsTests(BitcoinTestFramework): self.nodes[1].assert_start_raises_init_error(["-whitebind=noban@127.0.0.1", "-bind=127.0.0.1", "-listen=0"], "Cannot set -bind or -whitebind together with -listen=0", match=ErrorMatch.PARTIAL_REGEX) def check_tx_relay(self): - block_op_true = self.nodes[0].getblock(self.generatetoaddress(self.nodes[0], 100, ADDRESS_BCRT1_P2WSH_OP_TRUE)[0]) - self.log.debug("Create a connection from a forcerelay peer that rebroadcasts raw txs") # A test framework p2p connection is needed to send the raw transaction directly. If a full node was used, it could only # rebroadcast via the inv-getdata mechanism. However, even for forcerelay connections, a full node would @@ -104,18 +98,7 @@ class P2PPermissionsTests(BitcoinTestFramework): p2p_rebroadcast_wallet = self.nodes[1].add_p2p_connection(P2PDataStore()) self.log.debug("Send a tx from the wallet initially") - tx = tx_from_hex( - self.nodes[0].createrawtransaction( - inputs=[{ - 'txid': block_op_true['tx'][0], - 'vout': 0, - }], outputs=[{ - ADDRESS_BCRT1_P2WSH_OP_TRUE: 5, - }], - replaceable=False), - ) - tx.wit.vtxinwit = [CTxInWitness()] - tx.wit.vtxinwit[0].scriptWitness.stack = [CScript([OP_TRUE])] + tx = self.wallet.create_self_transfer(sequence=SEQUENCE_FINAL)['tx'] txid = tx.rehash() self.log.debug("Wait until tx is in node[1]'s mempool") diff --git a/test/functional/p2p_ping.py b/test/functional/p2p_ping.py index 2919f7aa7b..3ba30a42b1 100755 --- a/test/functional/p2p_ping.py +++ b/test/functional/p2p_ping.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2020-2021 The Bitcoin Core developers +# Copyright (c) 2020-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 ping message diff --git a/test/functional/p2p_segwit.py b/test/functional/p2p_segwit.py index 311b0b67db..b0900e49b8 100755 --- a/test/functional/p2p_segwit.py +++ b/test/functional/p2p_segwit.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2016-2021 The Bitcoin Core developers +# Copyright (c) 2016-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 segwit transactions and blocks on P2P network.""" @@ -622,8 +622,10 @@ class SegWitTest(BitcoinTestFramework): if not self.segwit_active: # Just check mempool acceptance, but don't add the transaction to the mempool, since witness is disallowed # in blocks and the tx is impossible to mine right now. - assert_equal( - self.nodes[0].testmempoolaccept([tx3.serialize_with_witness().hex()]), + testres3 = self.nodes[0].testmempoolaccept([tx3.serialize_with_witness().hex()]) + testres3[0]["fees"].pop("effective-feerate") + testres3[0]["fees"].pop("effective-includes") + assert_equal(testres3, [{ 'txid': tx3.hash, 'wtxid': tx3.getwtxid(), @@ -639,8 +641,10 @@ class SegWitTest(BitcoinTestFramework): tx3 = tx tx3.vout = [tx3_out] tx3.rehash() - assert_equal( - self.nodes[0].testmempoolaccept([tx3.serialize_with_witness().hex()]), + testres3_replaced = self.nodes[0].testmempoolaccept([tx3.serialize_with_witness().hex()]) + testres3_replaced[0]["fees"].pop("effective-feerate") + testres3_replaced[0]["fees"].pop("effective-includes") + assert_equal(testres3_replaced, [{ 'txid': tx3.hash, 'wtxid': tx3.getwtxid(), diff --git a/test/functional/p2p_sendtxrcncl.py b/test/functional/p2p_sendtxrcncl.py index fed9832a7d..0e349ef70c 100755 --- a/test/functional/p2p_sendtxrcncl.py +++ b/test/functional/p2p_sendtxrcncl.py @@ -10,6 +10,7 @@ from test_framework.messages import ( msg_verack, msg_version, msg_wtxidrelay, + NODE_BLOOM, ) from test_framework.p2p import ( P2PInterface, @@ -54,10 +55,8 @@ class PeerTrackMsgOrder(P2PInterface): super().on_message(message) self.messages.append(message) -def create_sendtxrcncl_msg(initiator=True): +def create_sendtxrcncl_msg(): sendtxrcncl_msg = msg_sendtxrcncl() - sendtxrcncl_msg.initiator = initiator - sendtxrcncl_msg.responder = not initiator sendtxrcncl_msg.version = 1 sendtxrcncl_msg.salt = 2 return sendtxrcncl_msg @@ -68,11 +67,11 @@ class SendTxRcnclTest(BitcoinTestFramework): self.extra_args = [['-txreconciliation']] def run_test(self): + # Check everything concerning *sending* SENDTXRCNCL + # First, *sending* to *inbound*. self.log.info('SENDTXRCNCL sent to an inbound') peer = self.nodes[0].add_p2p_connection(SendTxrcnclReceiver(), send_version=True, wait_for_verack=True) assert peer.sendtxrcncl_msg_received - assert not peer.sendtxrcncl_msg_received.initiator - assert peer.sendtxrcncl_msg_received.responder assert_equal(peer.sendtxrcncl_msg_received.version, 1) self.nodes[0].disconnect_p2ps() @@ -81,7 +80,7 @@ class SendTxRcnclTest(BitcoinTestFramework): peer.wait_for_verack() verack_index = [i for i, msg in enumerate(peer.messages) if msg.msgtype == b'verack'][0] sendtxrcncl_index = [i for i, msg in enumerate(peer.messages) if msg.msgtype == b'sendtxrcncl'][0] - assert(sendtxrcncl_index < verack_index) + assert sendtxrcncl_index < verack_index self.nodes[0].disconnect_p2ps() self.log.info('SENDTXRCNCL on pre-WTXID version should not be sent') @@ -108,30 +107,77 @@ class SendTxRcnclTest(BitcoinTestFramework): assert not peer.sendtxrcncl_msg_received self.nodes[0].disconnect_p2ps() + self.log.info('SENDTXRCNCL for fRelay=false should not be sent (with NODE_BLOOM offered)') + self.restart_node(0, ["-peerbloomfilters", "-txreconciliation"]) + peer = self.nodes[0].add_p2p_connection(SendTxrcnclReceiver(), send_version=False, wait_for_verack=False) + no_txrelay_version_msg = msg_version() + no_txrelay_version_msg.nVersion = P2P_VERSION + no_txrelay_version_msg.strSubVer = P2P_SUBVERSION + no_txrelay_version_msg.nServices = P2P_SERVICES + no_txrelay_version_msg.relay = 0 + peer.send_message(no_txrelay_version_msg) + peer.wait_for_verack() + assert peer.nServices & NODE_BLOOM != 0 + assert not peer.sendtxrcncl_msg_received + self.nodes[0].disconnect_p2ps() + + # Now, *sending* to *outbound*. + self.log.info('SENDTXRCNCL sent to an outbound') + peer = self.nodes[0].add_outbound_p2p_connection( + SendTxrcnclReceiver(), wait_for_verack=True, p2p_idx=0, connection_type="outbound-full-relay") + assert peer.sendtxrcncl_msg_received + assert_equal(peer.sendtxrcncl_msg_received.version, 1) + self.nodes[0].disconnect_p2ps() + + self.log.info('SENDTXRCNCL should not be sent if block-relay-only') + peer = self.nodes[0].add_outbound_p2p_connection( + SendTxrcnclReceiver(), wait_for_verack=True, p2p_idx=0, connection_type="block-relay-only") + assert not peer.sendtxrcncl_msg_received + self.nodes[0].disconnect_p2ps() + + self.log.info("SENDTXRCNCL should not be sent if feeler") + peer = self.nodes[0].add_outbound_p2p_connection(P2PFeelerReceiver(), p2p_idx=0, connection_type="feeler") + assert not peer.sendtxrcncl_msg_received + self.nodes[0].disconnect_p2ps() + + self.log.info("SENDTXRCNCL should not be sent if addrfetch") + peer = self.nodes[0].add_outbound_p2p_connection( + SendTxrcnclReceiver(), wait_for_verack=True, p2p_idx=0, connection_type="addr-fetch") + assert not peer.sendtxrcncl_msg_received + self.nodes[0].disconnect_p2ps() + + self.log.info('SENDTXRCNCL not sent if -txreconciliation flag is not set') + self.restart_node(0, []) + peer = self.nodes[0].add_p2p_connection(SendTxrcnclReceiver(), send_version=True, wait_for_verack=True) + assert not peer.sendtxrcncl_msg_received + self.nodes[0].disconnect_p2ps() + + self.log.info('SENDTXRCNCL not sent if blocksonly is set') + self.restart_node(0, ["-txreconciliation", "-blocksonly"]) + peer = self.nodes[0].add_p2p_connection(SendTxrcnclReceiver(), send_version=True, wait_for_verack=True) + assert not peer.sendtxrcncl_msg_received + self.nodes[0].disconnect_p2ps() + + # Check everything concerning *receiving* SENDTXRCNCL + # First, receiving from *inbound*. + self.restart_node(0, ["-txreconciliation"]) self.log.info('valid SENDTXRCNCL received') peer = self.nodes[0].add_p2p_connection(PeerNoVerack(), send_version=True, wait_for_verack=False) - peer.send_message(create_sendtxrcncl_msg()) - self.wait_until(lambda : "sendtxrcncl" in self.nodes[0].getpeerinfo()[-1]["bytesrecv_per_msg"]) + with self.nodes[0].assert_debug_log(["received: sendtxrcncl"]): + peer.send_message(create_sendtxrcncl_msg()) self.log.info('second SENDTXRCNCL triggers a disconnect') with self.nodes[0].assert_debug_log(["(sendtxrcncl received from already registered peer); disconnecting"]): peer.send_message(create_sendtxrcncl_msg()) peer.wait_for_disconnect() - self.log.info('SENDTXRCNCL with initiator=responder=0 triggers a disconnect') - sendtxrcncl_no_role = create_sendtxrcncl_msg() - sendtxrcncl_no_role.initiator = False - sendtxrcncl_no_role.responder = False + self.restart_node(0, []) + self.log.info('SENDTXRCNCL if no txreconciliation supported is ignored') peer = self.nodes[0].add_p2p_connection(PeerNoVerack(), send_version=True, wait_for_verack=False) - with self.nodes[0].assert_debug_log(["txreconciliation protocol violation"]): - peer.send_message(sendtxrcncl_no_role) - peer.wait_for_disconnect() + with self.nodes[0].assert_debug_log(['ignored, as our node does not have txreconciliation enabled']): + peer.send_message(create_sendtxrcncl_msg()) + self.nodes[0].disconnect_p2ps() - self.log.info('SENDTXRCNCL with initiator=0 and responder=1 from inbound triggers a disconnect') - sendtxrcncl_wrong_role = create_sendtxrcncl_msg(initiator=False) - peer = self.nodes[0].add_p2p_connection(PeerNoVerack(), send_version=True, wait_for_verack=False) - with self.nodes[0].assert_debug_log(["txreconciliation protocol violation"]): - peer.send_message(sendtxrcncl_wrong_role) - peer.wait_for_disconnect() + self.restart_node(0, ["-txreconciliation"]) self.log.info('SENDTXRCNCL with version=0 triggers a disconnect') sendtxrcncl_low_version = create_sendtxrcncl_msg() @@ -141,6 +187,26 @@ class SendTxRcnclTest(BitcoinTestFramework): peer.send_message(sendtxrcncl_low_version) peer.wait_for_disconnect() + self.log.info('SENDTXRCNCL with version=2 is valid') + sendtxrcncl_higher_version = create_sendtxrcncl_msg() + sendtxrcncl_higher_version.version = 2 + peer = self.nodes[0].add_p2p_connection(PeerNoVerack(), send_version=True, wait_for_verack=False) + with self.nodes[0].assert_debug_log(['Register peer=1']): + peer.send_message(sendtxrcncl_higher_version) + self.nodes[0].disconnect_p2ps() + + self.log.info('unexpected SENDTXRCNCL is ignored') + peer = self.nodes[0].add_p2p_connection(PeerNoVerack(), send_version=False, wait_for_verack=False) + old_version_msg = msg_version() + old_version_msg.nVersion = 70015 + old_version_msg.strSubVer = P2P_SUBVERSION + old_version_msg.nServices = P2P_SERVICES + old_version_msg.relay = 1 + peer.send_message(old_version_msg) + with self.nodes[0].assert_debug_log(['Ignore unexpected txreconciliation signal']): + peer.send_message(create_sendtxrcncl_msg()) + self.nodes[0].disconnect_p2ps() + self.log.info('sending SENDTXRCNCL after sending VERACK triggers a disconnect') peer = self.nodes[0].add_p2p_connection(P2PInterface()) with self.nodes[0].assert_debug_log(["sendtxrcncl received after verack"]): @@ -154,53 +220,14 @@ class SendTxRcnclTest(BitcoinTestFramework): peer.send_message(msg_verack()) self.nodes[0].disconnect_p2ps() - self.log.info('SENDTXRCNCL sent to an outbound') - peer = self.nodes[0].add_outbound_p2p_connection( - SendTxrcnclReceiver(), wait_for_verack=True, p2p_idx=0, connection_type="outbound-full-relay") - assert peer.sendtxrcncl_msg_received - assert peer.sendtxrcncl_msg_received.initiator - assert not peer.sendtxrcncl_msg_received.responder - assert_equal(peer.sendtxrcncl_msg_received.version, 1) - self.nodes[0].disconnect_p2ps() - - self.log.info('SENDTXRCNCL should not be sent if block-relay-only') - peer = self.nodes[0].add_outbound_p2p_connection( - SendTxrcnclReceiver(), wait_for_verack=True, p2p_idx=0, connection_type="block-relay-only") - assert not peer.sendtxrcncl_msg_received - self.nodes[0].disconnect_p2ps() - - self.log.info("SENDTXRCNCL should not be sent if feeler") - peer = self.nodes[0].add_outbound_p2p_connection(P2PFeelerReceiver(), p2p_idx=0, connection_type="feeler") - assert not peer.sendtxrcncl_msg_received - self.nodes[0].disconnect_p2ps() - + # Now, *receiving* from *outbound*. self.log.info('SENDTXRCNCL if block-relay-only triggers a disconnect') peer = self.nodes[0].add_outbound_p2p_connection( PeerNoVerack(), wait_for_verack=False, p2p_idx=0, connection_type="block-relay-only") with self.nodes[0].assert_debug_log(["we indicated no tx relay; disconnecting"]): - peer.send_message(create_sendtxrcncl_msg(initiator=False)) - peer.wait_for_disconnect() - - self.log.info('SENDTXRCNCL with initiator=1 and responder=0 from outbound triggers a disconnect') - sendtxrcncl_wrong_role = create_sendtxrcncl_msg(initiator=True) - peer = self.nodes[0].add_outbound_p2p_connection( - PeerNoVerack(), wait_for_verack=False, p2p_idx=0, connection_type="outbound-full-relay") - with self.nodes[0].assert_debug_log(["txreconciliation protocol violation"]): - peer.send_message(sendtxrcncl_wrong_role) + peer.send_message(create_sendtxrcncl_msg()) peer.wait_for_disconnect() - self.log.info('SENDTXRCNCL not sent if -txreconciliation flag is not set') - self.restart_node(0, []) - peer = self.nodes[0].add_p2p_connection(SendTxrcnclReceiver(), send_version=True, wait_for_verack=True) - assert not peer.sendtxrcncl_msg_received - self.nodes[0].disconnect_p2ps() - - self.log.info('SENDTXRCNCL not sent if blocksonly is set') - self.restart_node(0, ["-txreconciliation", "-blocksonly"]) - peer = self.nodes[0].add_p2p_connection(SendTxrcnclReceiver(), send_version=True, wait_for_verack=True) - assert not peer.sendtxrcncl_msg_received - self.nodes[0].disconnect_p2ps() - if __name__ == '__main__': SendTxRcnclTest().main() diff --git a/test/functional/p2p_timeouts.py b/test/functional/p2p_timeouts.py index 15a879ae3c..a308577c02 100755 --- a/test/functional/p2p_timeouts.py +++ b/test/functional/p2p_timeouts.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2016-2021 The Bitcoin Core developers +# Copyright (c) 2016-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 various net timeouts. diff --git a/test/functional/p2p_tx_privacy.py b/test/functional/p2p_tx_privacy.py new file mode 100755 index 0000000000..e674f6c3eb --- /dev/null +++ b/test/functional/p2p_tx_privacy.py @@ -0,0 +1,77 @@ +#!/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 that transaction announcements are only queued for peers that have +successfully completed the version handshake. + +Topology: + + tx_originator ----> node[0] <---- spy + +We test that a transaction sent by tx_originator is only relayed to spy +if it was received after spy's version handshake completed. + +1. Fully connect tx_originator +2. Connect spy (no version handshake) +3. tx_originator sends tx1 +4. spy completes the version handshake +5. tx_originator sends tx2 +6. We check that only tx2 is announced on the spy interface +""" +from test_framework.messages import ( + msg_wtxidrelay, + msg_verack, + msg_tx, + CInv, + MSG_WTX, +) +from test_framework.p2p import ( + P2PInterface, +) +from test_framework.test_framework import BitcoinTestFramework +from test_framework.wallet import MiniWallet + +class P2PTxSpy(P2PInterface): + def __init__(self): + super().__init__() + self.all_invs = [] + + def on_version(self, message): + self.send_message(msg_wtxidrelay()) + + def on_inv(self, message): + self.all_invs += message.inv + + def wait_for_inv_match(self, expected_inv): + self.wait_until(lambda: len(self.all_invs) == 1 and self.all_invs[0] == expected_inv) + +class TxPrivacyTest(BitcoinTestFramework): + def set_test_params(self): + self.num_nodes = 1 + + def run_test(self): + self.wallet = MiniWallet(self.nodes[0]) + + tx_originator = self.nodes[0].add_p2p_connection(P2PInterface()) + spy = self.nodes[0].add_p2p_connection(P2PTxSpy(), wait_for_verack=False) + spy.wait_for_verack() + + # tx_originator sends tx1 + tx1 = self.wallet.create_self_transfer()["tx"] + tx_originator.send_and_ping(msg_tx(tx1)) + + # Spy sends the verack + spy.send_and_ping(msg_verack()) + + # tx_originator sends tx2 + tx2 = self.wallet.create_self_transfer()["tx"] + tx_originator.send_and_ping(msg_tx(tx2)) + + # Spy should only get an inv for the second transaction as the first + # one was received pre-verack with the spy + spy.wait_for_inv_match(CInv(MSG_WTX, tx2.calc_sha256(True))) + +if __name__ == '__main__': + TxPrivacyTest().main() diff --git a/test/functional/p2p_unrequested_blocks.py b/test/functional/p2p_unrequested_blocks.py index 5030e7af26..f368434895 100755 --- a/test/functional/p2p_unrequested_blocks.py +++ b/test/functional/p2p_unrequested_blocks.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2021 The Bitcoin Core developers +# Copyright (c) 2015-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 processing of unrequested blocks. diff --git a/test/functional/rpc_blockchain.py b/test/functional/rpc_blockchain.py index 80e8fe55a3..19c73eebf0 100755 --- a/test/functional/rpc_blockchain.py +++ b/test/functional/rpc_blockchain.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2014-2021 The Bitcoin Core developers +# Copyright (c) 2014-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 RPCs related to blockchainstate. @@ -370,7 +370,7 @@ class BlockchainTest(BitcoinTestFramework): # hash_type muhash should return a different UTXO set hash. res6 = node.gettxoutsetinfo(hash_type='muhash') assert 'muhash' in res6 - assert(res['hash_serialized_2'] != res6['muhash']) + assert res['hash_serialized_2'] != res6['muhash'] # muhash should not be returned unless requested. for r in [res, res2, res3, res4, res5]: diff --git a/test/functional/rpc_createmultisig.py b/test/functional/rpc_createmultisig.py index 1ad3fc24c9..7d03ed2951 100755 --- a/test/functional/rpc_createmultisig.py +++ b/test/functional/rpc_createmultisig.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2021 The Bitcoin Core developers +# Copyright (c) 2015-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 multisig RPCs""" @@ -24,12 +24,13 @@ from test_framework.wallet import ( ) class RpcCreateMultiSigTest(BitcoinTestFramework): + def add_options(self, parser): + self.add_wallet_options(parser) + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 3 self.supports_cli = False - if self.is_bdb_compiled(): - self.requires_wallet = True def get_keys(self): self.pub = [] @@ -50,6 +51,7 @@ class RpcCreateMultiSigTest(BitcoinTestFramework): self.wallet = MiniWallet(test_node=node0) if self.is_bdb_compiled(): + self.import_deterministic_coinbase_privkeys() self.check_addmultisigaddress_errors() self.log.info('Generating blocks ...') diff --git a/test/functional/rpc_decodescript.py b/test/functional/rpc_decodescript.py index 343cb73989..a61710b739 100755 --- a/test/functional/rpc_decodescript.py +++ b/test/functional/rpc_decodescript.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2021 The Bitcoin Core developers +# Copyright (c) 2015-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 decoding scripts via decodescript RPC command.""" diff --git a/test/functional/rpc_deriveaddresses.py b/test/functional/rpc_deriveaddresses.py index a69326736d..e96b6bda90 100755 --- a/test/functional/rpc_deriveaddresses.py +++ b/test/functional/rpc_deriveaddresses.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2018-2019 The Bitcoin Core developers +# Copyright (c) 2018-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 the deriveaddresses rpc call.""" diff --git a/test/functional/rpc_dumptxoutset.py b/test/functional/rpc_dumptxoutset.py index 672c9a53dc..39a931be03 100755 --- a/test/functional/rpc_dumptxoutset.py +++ b/test/functional/rpc_dumptxoutset.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2019-2021 The Bitcoin Core developers +# Copyright (c) 2019-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 the generation of UTXO snapshots using `dumptxoutset`. diff --git a/test/functional/rpc_estimatefee.py b/test/functional/rpc_estimatefee.py index b057400887..dad3cbcf0c 100755 --- a/test/functional/rpc_estimatefee.py +++ b/test/functional/rpc_estimatefee.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2018-2020 The Bitcoin Core developers +# Copyright (c) 2018-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 the estimatefee RPCs. diff --git a/test/functional/rpc_generate.py b/test/functional/rpc_generate.py index 2b1dd20ea1..8948ccb48d 100755 --- a/test/functional/rpc_generate.py +++ b/test/functional/rpc_generate.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2020-2021 The Bitcoin Core developers +# Copyright (c) 2020-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 generate* RPCs.""" @@ -28,7 +28,6 @@ class RPCGenerateTest(BitcoinTestFramework): def test_generateblock(self): node = self.nodes[0] miniwallet = MiniWallet(node) - miniwallet.rescan_utxos() self.log.info('Generate an empty block to address') address = miniwallet.get_address() diff --git a/test/functional/rpc_getblockfrompeer.py b/test/functional/rpc_getblockfrompeer.py index 8bd3366e36..dddc779763 100755 --- a/test/functional/rpc_getblockfrompeer.py +++ b/test/functional/rpc_getblockfrompeer.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2020-2021 The Bitcoin Core developers +# Copyright (c) 2020-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 the getblockfrompeer RPC.""" diff --git a/test/functional/rpc_getblockstats.py b/test/functional/rpc_getblockstats.py index 1ea1ee5659..bf261befcc 100755 --- a/test/functional/rpc_getblockstats.py +++ b/test/functional/rpc_getblockstats.py @@ -43,6 +43,10 @@ class GetblockstatsTest(BitcoinTestFramework): def generate_test_data(self, filename): mocktime = 1525107225 self.nodes[0].setmocktime(mocktime) + self.nodes[0].createwallet(wallet_name='test') + privkey = self.nodes[0].get_deterministic_priv_key().key + self.nodes[0].importprivkey(privkey) + self.generate(self.nodes[0], COINBASE_MATURITY + 1) address = self.nodes[0].get_deterministic_priv_key().address @@ -53,6 +57,8 @@ class GetblockstatsTest(BitcoinTestFramework): self.nodes[0].sendtoaddress(address=address, amount=10, subtractfeefromamount=False) self.nodes[0].settxfee(amount=0.003) self.nodes[0].sendtoaddress(address=address, amount=1, subtractfeefromamount=True) + # Send to OP_RETURN output to test its exclusion from statistics + self.nodes[0].send(outputs={"data": "21"}) self.sync_all() self.generate(self.nodes[0], 1) @@ -161,6 +167,20 @@ class GetblockstatsTest(BitcoinTestFramework): assert_raises_rpc_error(-1, 'getblockstats hash_or_height ( stats )', self.nodes[0].getblockstats, '00', 1, 2) assert_raises_rpc_error(-1, 'getblockstats hash_or_height ( stats )', self.nodes[0].getblockstats) + self.log.info('Test block height 0') + genesis_stats = self.nodes[0].getblockstats(0) + assert_equal(genesis_stats["blockhash"], "0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206") + assert_equal(genesis_stats["utxo_increase"], 1) + assert_equal(genesis_stats["utxo_size_inc"], 117) + assert_equal(genesis_stats["utxo_increase_actual"], 0) + assert_equal(genesis_stats["utxo_size_inc_actual"], 0) + + self.log.info('Test tip including OP_RETURN') + tip_stats = self.nodes[0].getblockstats(tip) + assert_equal(tip_stats["utxo_increase"], 6) + assert_equal(tip_stats["utxo_size_inc"], 441) + assert_equal(tip_stats["utxo_increase_actual"], 4) + assert_equal(tip_stats["utxo_size_inc_actual"], 300) if __name__ == '__main__': GetblockstatsTest().main() diff --git a/test/functional/rpc_help.py b/test/functional/rpc_help.py index f683577c47..7acc3cbbd5 100755 --- a/test/functional/rpc_help.py +++ b/test/functional/rpc_help.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2018-2021 The Bitcoin Core developers +# Copyright (c) 2018-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 RPC help output.""" @@ -43,6 +43,9 @@ def process_mapping(fname): class HelpRpcTest(BitcoinTestFramework): + def add_options(self, parser): + self.add_wallet_options(parser) + def set_test_params(self): self.num_nodes = 1 self.supports_cli = False diff --git a/test/functional/rpc_invalid_address_message.py b/test/functional/rpc_invalid_address_message.py index fcc49d0a75..0c29efb85a 100755 --- a/test/functional/rpc_invalid_address_message.py +++ b/test/functional/rpc_invalid_address_message.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2020-2021 The Bitcoin Core developers +# Copyright (c) 2020-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 error messages for 'getaddressinfo' and 'validateaddress' RPC commands.""" @@ -39,6 +39,9 @@ INVALID_ADDRESS = 'asfah14i8fajz0123f' INVALID_ADDRESS_2 = '1q049ldschfnwystcqnsvyfpj23mpsg3jcedq9xv' class InvalidAddressErrorMessageTest(BitcoinTestFramework): + def add_options(self, parser): + self.add_wallet_options(parser) + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 1 @@ -92,15 +95,19 @@ class InvalidAddressErrorMessageTest(BitcoinTestFramework): self.check_invalid(INVALID_ADDRESS, 'Not a valid Bech32 or Base58 encoding') self.check_invalid(INVALID_ADDRESS_2, 'Not a valid Bech32 or Base58 encoding') + node = self.nodes[0] + + # Missing arg returns the help text + assert_raises_rpc_error(-1, "Return information about the given bitcoin address.", node.validateaddress) + # Explicit None is not allowed for required parameters + assert_raises_rpc_error(-3, "JSON value of type null is not of expected type string", node.validateaddress, None) + def test_getaddressinfo(self): node = self.nodes[0] assert_raises_rpc_error(-5, "Invalid Bech32 address data size", node.getaddressinfo, BECH32_INVALID_SIZE) - assert_raises_rpc_error(-5, "Not a valid Bech32 or Base58 encoding", node.getaddressinfo, BECH32_INVALID_PREFIX) - assert_raises_rpc_error(-5, "Invalid prefix for Base58-encoded address", node.getaddressinfo, BASE58_INVALID_PREFIX) - assert_raises_rpc_error(-5, "Not a valid Bech32 or Base58 encoding", node.getaddressinfo, INVALID_ADDRESS) def run_test(self): diff --git a/test/functional/rpc_invalidateblock.py b/test/functional/rpc_invalidateblock.py index 1e33e7ca9c..69c5397ce2 100755 --- a/test/functional/rpc_invalidateblock.py +++ b/test/functional/rpc_invalidateblock.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2014-2021 The Bitcoin Core developers +# Copyright (c) 2014-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 the invalidateblock RPC.""" diff --git a/test/functional/rpc_mempool_info.py b/test/functional/rpc_mempool_info.py index ae9c6572cf..246af22e50 100755 --- a/test/functional/rpc_mempool_info.py +++ b/test/functional/rpc_mempool_info.py @@ -18,7 +18,6 @@ class RPCMempoolInfoTest(BitcoinTestFramework): def run_test(self): self.wallet = MiniWallet(self.nodes[0]) - self.wallet.rescan_utxos() confirmed_utxo = self.wallet.get_utxo() # Create a tree of unconfirmed transactions in the mempool: diff --git a/test/functional/rpc_misc.py b/test/functional/rpc_misc.py index f6ee6a5215..43d1e2c731 100755 --- a/test/functional/rpc_misc.py +++ b/test/functional/rpc_misc.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2019-2021 The Bitcoin Core developers +# Copyright (c) 2019-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 RPC misc output.""" diff --git a/test/functional/rpc_named_arguments.py b/test/functional/rpc_named_arguments.py index 41b9312969..46d9ffceae 100755 --- a/test/functional/rpc_named_arguments.py +++ b/test/functional/rpc_named_arguments.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2016-2019 The Bitcoin Core developers +# Copyright (c) 2016-2020 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 using named arguments for RPCs.""" @@ -30,6 +30,9 @@ class NamedArgumentTest(BitcoinTestFramework): assert_equal(node.echo(arg1=1), [None, 1]) assert_equal(node.echo(arg9=None), [None]*10) assert_equal(node.echo(arg0=0,arg3=3,arg9=9), [0] + [None]*2 + [3] + [None]*5 + [9]) + assert_equal(node.echo(0, 1, arg3=3, arg5=5), [0, 1, None, 3, None, 5]) + assert_raises_rpc_error(-8, "Parameter arg1 specified twice both as positional and named argument", node.echo, 0, 1, arg1=1) + assert_raises_rpc_error(-8, "Parameter arg1 specified twice both as positional and named argument", node.echo, 0, None, 2, arg1=1) if __name__ == '__main__': NamedArgumentTest().main() diff --git a/test/functional/rpc_net.py b/test/functional/rpc_net.py index 65e3c82c87..5fdd5daddf 100755 --- a/test/functional/rpc_net.py +++ b/test/functional/rpc_net.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2017-2021 The Bitcoin Core developers +# Copyright (c) 2017-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 RPC calls related to net. @@ -11,7 +11,6 @@ from decimal import Decimal from itertools import product import time -from test_framework.blocktools import COINBASE_MATURITY import test_framework.messages from test_framework.p2p import ( P2PInterface, @@ -43,7 +42,6 @@ def assert_net_servicesnames(servicesflag, servicenames): class NetTest(BitcoinTestFramework): def set_test_params(self): - self.setup_clean_chain = True self.num_nodes = 2 self.extra_args = [["-minrelaytxfee=0.00001000"], ["-minrelaytxfee=0.00000500"]] self.supports_cli = False @@ -51,9 +49,6 @@ class NetTest(BitcoinTestFramework): def run_test(self): # We need miniwallet to make a transaction self.wallet = MiniWallet(self.nodes[0]) - self.generate(self.wallet, 1) - # Get out of IBD for the minfeefilter and getpeerinfo tests. - self.generate(self.nodes[0], COINBASE_MATURITY + 1) # By default, the test framework sets up an addnode connection from # node 1 --> node0. By connecting node0 --> node 1, we're left with @@ -112,7 +107,7 @@ class NetTest(BitcoinTestFramework): no_version_peer_conntime = int(time.time()) self.nodes[0].setmocktime(no_version_peer_conntime) with self.nodes[0].assert_debug_log([f"Added connection peer={no_version_peer_id}"]): - self.nodes[0].add_p2p_connection(P2PInterface(), send_version=False, wait_for_verack=False) + no_version_peer = self.nodes[0].add_p2p_connection(P2PInterface(), send_version=False, wait_for_verack=False) self.nodes[0].setmocktime(0) peer_info = self.nodes[0].getpeerinfo()[no_version_peer_id] peer_info.pop("addr") @@ -153,7 +148,8 @@ class NetTest(BitcoinTestFramework): "version": 0, }, ) - self.nodes[0].disconnect_p2ps() + no_version_peer.peer_disconnect() + self.wait_until(lambda: len(self.nodes[0].getpeerinfo()) == 2) def test_getnettotals(self): self.log.info("Test getnettotals") @@ -184,7 +180,8 @@ class NetTest(BitcoinTestFramework): self.nodes[0].setnetworkactive(state=False) assert_equal(self.nodes[0].getnetworkinfo()['networkactive'], False) # Wait a bit for all sockets to close - self.wait_until(lambda: self.nodes[0].getnetworkinfo()['connections'] == 0, timeout=3) + for n in self.nodes: + self.wait_until(lambda: n.getnetworkinfo()['connections'] == 0, timeout=3) with self.nodes[0].assert_debug_log(expected_msgs=['SetNetworkActive: true\n']): self.nodes[0].setnetworkactive(state=True) @@ -305,6 +302,9 @@ class NetTest(BitcoinTestFramework): assert_equal(node.addpeeraddress(address="", port=8333), {"success": False}) assert_equal(node.getnodeaddresses(count=0), []) + self.log.debug("Test that non-bool tried fails") + assert_raises_rpc_error(-3, "JSON value of type string is not of expected type bool", self.nodes[0].addpeeraddress, address="1.2.3.4", tried="True", port=1234) + self.log.debug("Test that adding an address with invalid port fails") assert_raises_rpc_error(-1, "JSON integer out of range", self.nodes[0].addpeeraddress, address="1.2.3.4", port=-1) assert_raises_rpc_error(-1, "JSON integer out of range", self.nodes[0].addpeeraddress,address="1.2.3.4", port=65536) diff --git a/test/functional/rpc_packages.py b/test/functional/rpc_packages.py index 9a563cbf5f..6cb9760b3d 100755 --- a/test/functional/rpc_packages.py +++ b/test/functional/rpc_packages.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2021 The Bitcoin Core developers +# Copyright (c) 2021-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. """RPCs that handle raw transaction packages.""" @@ -7,35 +7,30 @@ from decimal import Decimal import random -from test_framework.address import ADDRESS_BCRT1_P2WSH_OP_TRUE -from test_framework.test_framework import BitcoinTestFramework +from test_framework.blocktools import COINBASE_MATURITY from test_framework.messages import ( MAX_BIP125_RBF_SEQUENCE, - COIN, - CTxInWitness, tx_from_hex, ) from test_framework.p2p import P2PTxInvStore -from test_framework.script import ( - CScript, - OP_TRUE, -) +from test_framework.test_framework import BitcoinTestFramework from test_framework.util import ( assert_equal, assert_fee_amount, assert_raises_rpc_error, ) from test_framework.wallet import ( - create_child_with_parents, - create_raw_chain, + COIN, DEFAULT_FEE, - make_chain, + MiniWallet, ) + 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 @@ -49,38 +44,38 @@ class RPCPackagesTest(BitcoinTestFramework): assert_equal(shuffled_testres, self.nodes[0].testmempoolaccept(shuffled_package)) def run_test(self): - self.log.info("Generate blocks to create UTXOs") node = self.nodes[0] - self.privkeys = [node.get_deterministic_priv_key().key] - self.address = node.get_deterministic_priv_key().address - self.coins = [] - # The last 100 coinbase transactions are premature - for b in self.generatetoaddress(node, 220, self.address)[:-100]: - coinbase = node.getblock(blockhash=b, verbosity=2)["tx"][0] - self.coins.append({ + + # get an UTXO that requires signature to be spent + deterministic_address = node.get_deterministic_priv_key().address + blockhash = self.generatetoaddress(node, 1, deterministic_address)[0] + coinbase = node.getblock(blockhash=blockhash, verbosity=2)["tx"][0] + coin = { "txid": coinbase["txid"], "amount": coinbase["vout"][0]["value"], "scriptPubKey": coinbase["vout"][0]["scriptPubKey"], - }) + "vout": 0, + "height": 0 + } + + self.wallet = MiniWallet(self.nodes[0]) + self.generate(self.wallet, COINBASE_MATURITY + 100) # blocks generated for inputs + self.log.info("Create some transactions") # Create some transactions that can be reused throughout the test. Never submit these to mempool. self.independent_txns_hex = [] self.independent_txns_testres = [] for _ in range(3): - coin = self.coins.pop() - rawtx = node.createrawtransaction([{"txid": coin["txid"], "vout": 0}], - {self.address : coin["amount"] - Decimal("0.0001")}) - signedtx = node.signrawtransactionwithkey(hexstring=rawtx, privkeys=self.privkeys) - assert signedtx["complete"] - testres = node.testmempoolaccept([signedtx["hex"]]) + tx_hex = self.wallet.create_self_transfer(fee_rate=Decimal("0.0001"))["hex"] + testres = self.nodes[0].testmempoolaccept([tx_hex]) assert testres[0]["allowed"] - self.independent_txns_hex.append(signedtx["hex"]) + self.independent_txns_hex.append(tx_hex) # testmempoolaccept returns a list of length one, avoid creating a 2D list self.independent_txns_testres.append(testres[0]) self.independent_txns_testres_blank = [{ "txid": res["txid"], "wtxid": res["wtxid"]} for res in self.independent_txns_testres] - self.test_independent() + self.test_independent(coin) self.test_chain() self.test_multiple_children() self.test_multiple_parents() @@ -88,14 +83,15 @@ class RPCPackagesTest(BitcoinTestFramework): self.test_rbf() self.test_submitpackage() - def test_independent(self): + def test_independent(self, coin): self.log.info("Test multiple independent transactions in a package") node = self.nodes[0] # For independent transactions, order doesn't matter. self.assert_testres_equal(self.independent_txns_hex, self.independent_txns_testres) self.log.info("Test an otherwise valid package with an extra garbage tx appended") - garbage_tx = node.createrawtransaction([{"txid": "00" * 32, "vout": 5}], {self.address: 1}) + address = node.get_deterministic_priv_key().address + garbage_tx = node.createrawtransaction([{"txid": "00" * 32, "vout": 5}], {address: 1}) tx = tx_from_hex(garbage_tx) # Only the txid and wtxids are returned because validation is incomplete for the independent txns. # Package validation is atomic: if the node cannot find a UTXO for any single tx in the package, @@ -105,9 +101,8 @@ class RPCPackagesTest(BitcoinTestFramework): self.assert_testres_equal(package_bad, testres_bad) self.log.info("Check testmempoolaccept tells us when some transactions completed validation successfully") - coin = self.coins.pop() tx_bad_sig_hex = node.createrawtransaction([{"txid": coin["txid"], "vout": 0}], - {self.address : coin["amount"] - Decimal("0.0001")}) + {address : coin["amount"] - Decimal("0.0001")}) tx_bad_sig = tx_from_hex(tx_bad_sig_hex) testres_bad_sig = node.testmempoolaccept(self.independent_txns_hex + [tx_bad_sig_hex]) # By the time the signature for the last transaction is checked, all the other transactions @@ -120,23 +115,22 @@ class RPCPackagesTest(BitcoinTestFramework): }]) self.log.info("Check testmempoolaccept reports txns in packages that exceed max feerate") - coin = self.coins.pop() - tx_high_fee_raw = node.createrawtransaction([{"txid": coin["txid"], "vout": 0}], - {self.address : coin["amount"] - Decimal("0.999")}) - tx_high_fee_signed = node.signrawtransactionwithkey(hexstring=tx_high_fee_raw, privkeys=self.privkeys) - assert tx_high_fee_signed["complete"] - tx_high_fee = tx_from_hex(tx_high_fee_signed["hex"]) - testres_high_fee = node.testmempoolaccept([tx_high_fee_signed["hex"]]) + tx_high_fee = self.wallet.create_self_transfer(fee=Decimal("0.999")) + testres_high_fee = node.testmempoolaccept([tx_high_fee["hex"]]) assert_equal(testres_high_fee, [ - {"txid": tx_high_fee.rehash(), "wtxid": tx_high_fee.getwtxid(), "allowed": False, "reject-reason": "max-fee-exceeded"} + {"txid": tx_high_fee["txid"], "wtxid": tx_high_fee["wtxid"], "allowed": False, "reject-reason": "max-fee-exceeded"} ]) - package_high_fee = [tx_high_fee_signed["hex"]] + self.independent_txns_hex + package_high_fee = [tx_high_fee["hex"]] + self.independent_txns_hex testres_package_high_fee = node.testmempoolaccept(package_high_fee) assert_equal(testres_package_high_fee, testres_high_fee + self.independent_txns_testres_blank) def test_chain(self): node = self.nodes[0] - (chain_hex, chain_txns) = create_raw_chain(node, self.coins.pop(), self.address, self.privkeys) + + chain = self.wallet.create_self_transfer_chain(chain_length=25) + chain_hex = [t["hex"] for t in chain] + chain_txns = [t["tx"] for t in chain] + self.log.info("Check that testmempoolaccept requires packages to be sorted by dependency") assert_equal(node.testmempoolaccept(rawtxs=chain_hex[::-1]), [{"txid": tx.rehash(), "wtxid": tx.getwtxid(), "package-error": "package-not-sorted"} for tx in chain_txns[::-1]]) @@ -158,78 +152,57 @@ class RPCPackagesTest(BitcoinTestFramework): def test_multiple_children(self): node = self.nodes[0] - self.log.info("Testmempoolaccept a package in which a transaction has two children within the package") - first_coin = self.coins.pop() - value = (first_coin["amount"] - Decimal("0.0002")) / 2 # Deduct reasonable fee and make 2 outputs - inputs = [{"txid": first_coin["txid"], "vout": 0}] - outputs = [{self.address : value}, {ADDRESS_BCRT1_P2WSH_OP_TRUE : value}] - rawtx = node.createrawtransaction(inputs, outputs) - - parent_signed = node.signrawtransactionwithkey(hexstring=rawtx, privkeys=self.privkeys) - assert parent_signed["complete"] - parent_tx = tx_from_hex(parent_signed["hex"]) - parent_txid = parent_tx.rehash() - assert node.testmempoolaccept([parent_signed["hex"]])[0]["allowed"] - parent_locking_script_a = parent_tx.vout[0].scriptPubKey.hex() - child_value = value - Decimal("0.0001") + parent_tx = self.wallet.create_self_transfer_multi(num_outputs=2) + assert node.testmempoolaccept([parent_tx["hex"]])[0]["allowed"] # Child A - (_, tx_child_a_hex, _, _) = make_chain(node, self.address, self.privkeys, parent_txid, child_value, 0, parent_locking_script_a) - assert not node.testmempoolaccept([tx_child_a_hex])[0]["allowed"] + child_a_tx = self.wallet.create_self_transfer(utxo_to_spend=parent_tx["new_utxos"][0]) + assert not node.testmempoolaccept([child_a_tx["hex"]])[0]["allowed"] # Child B - rawtx_b = node.createrawtransaction([{"txid": parent_txid, "vout": 1}], {self.address : child_value}) - tx_child_b = tx_from_hex(rawtx_b) - tx_child_b.wit.vtxinwit = [CTxInWitness()] - tx_child_b.wit.vtxinwit[0].scriptWitness.stack = [CScript([OP_TRUE])] - tx_child_b_hex = tx_child_b.serialize().hex() - assert not node.testmempoolaccept([tx_child_b_hex])[0]["allowed"] + child_b_tx = self.wallet.create_self_transfer(utxo_to_spend=parent_tx["new_utxos"][1]) + assert not node.testmempoolaccept([child_b_tx["hex"]])[0]["allowed"] self.log.info("Testmempoolaccept with entire package, should work with children in either order") - testres_multiple_ab = node.testmempoolaccept(rawtxs=[parent_signed["hex"], tx_child_a_hex, tx_child_b_hex]) - testres_multiple_ba = node.testmempoolaccept(rawtxs=[parent_signed["hex"], tx_child_b_hex, tx_child_a_hex]) + testres_multiple_ab = node.testmempoolaccept(rawtxs=[parent_tx["hex"], child_a_tx["hex"], child_b_tx["hex"]]) + testres_multiple_ba = node.testmempoolaccept(rawtxs=[parent_tx["hex"], child_b_tx["hex"], child_a_tx["hex"]]) assert all([testres["allowed"] for testres in testres_multiple_ab + testres_multiple_ba]) testres_single = [] # Test accept and then submit each one individually, which should be identical to package testaccept - for rawtx in [parent_signed["hex"], tx_child_a_hex, tx_child_b_hex]: + for rawtx in [parent_tx["hex"], child_a_tx["hex"], child_b_tx["hex"]]: testres = node.testmempoolaccept([rawtx]) testres_single.append(testres[0]) # Submit the transaction now so its child should have no problem validating node.sendrawtransaction(rawtx) assert_equal(testres_single, testres_multiple_ab) - def test_multiple_parents(self): node = self.nodes[0] - self.log.info("Testmempoolaccept a package in which a transaction has multiple parents within the package") + for num_parents in [2, 10, 24]: # Test a package with num_parents parents and 1 child transaction. + parent_coins = [] package_hex = [] - parents_tx = [] - values = [] - parent_locking_scripts = [] + for _ in range(num_parents): - parent_coin = self.coins.pop() - value = parent_coin["amount"] - (tx, txhex, value, parent_locking_script) = make_chain(node, self.address, self.privkeys, parent_coin["txid"], value) - package_hex.append(txhex) - parents_tx.append(tx) - values.append(value) - parent_locking_scripts.append(parent_locking_script) - child_hex = create_child_with_parents(node, self.address, self.privkeys, parents_tx, values, parent_locking_scripts) - # Package accept should work with the parents in any order (as long as parents come before child) + # Package accept should work with the parents in any order (as long as parents come before child) + parent_tx = self.wallet.create_self_transfer() + parent_coins.append(parent_tx["new_utxo"]) + package_hex.append(parent_tx["hex"]) + + child_tx = self.wallet.create_self_transfer_multi(utxos_to_spend=parent_coins, fee_per_output=2000) for _ in range(10): random.shuffle(package_hex) - testres_multiple = node.testmempoolaccept(rawtxs=package_hex + [child_hex]) + testres_multiple = node.testmempoolaccept(rawtxs=package_hex + [child_tx['hex']]) assert all([testres["allowed"] for testres in testres_multiple]) testres_single = [] # Test accept and then submit each one individually, which should be identical to package testaccept - for rawtx in package_hex + [child_hex]: + for rawtx in package_hex + [child_tx["hex"]]: testres_single.append(node.testmempoolaccept([rawtx])[0]) # Submit the transaction now so its child should have no problem validating node.sendrawtransaction(rawtx) @@ -237,77 +210,63 @@ class RPCPackagesTest(BitcoinTestFramework): def test_conflicting(self): node = self.nodes[0] - prevtx = self.coins.pop() - inputs = [{"txid": prevtx["txid"], "vout": 0}] - output1 = {node.get_deterministic_priv_key().address: 50 - 0.00125} - output2 = {ADDRESS_BCRT1_P2WSH_OP_TRUE: 50 - 0.00125} + coin = self.wallet.get_utxo() # tx1 and tx2 share the same inputs - rawtx1 = node.createrawtransaction(inputs, output1) - rawtx2 = node.createrawtransaction(inputs, output2) - signedtx1 = node.signrawtransactionwithkey(hexstring=rawtx1, privkeys=self.privkeys) - signedtx2 = node.signrawtransactionwithkey(hexstring=rawtx2, privkeys=self.privkeys) - tx1 = tx_from_hex(signedtx1["hex"]) - tx2 = tx_from_hex(signedtx2["hex"]) - assert signedtx1["complete"] - assert signedtx2["complete"] + tx1 = self.wallet.create_self_transfer(utxo_to_spend=coin) + tx2 = self.wallet.create_self_transfer(utxo_to_spend=coin) # Ensure tx1 and tx2 are valid by themselves - assert node.testmempoolaccept([signedtx1["hex"]])[0]["allowed"] - assert node.testmempoolaccept([signedtx2["hex"]])[0]["allowed"] + assert node.testmempoolaccept([tx1["hex"]])[0]["allowed"] + assert node.testmempoolaccept([tx2["hex"]])[0]["allowed"] self.log.info("Test duplicate transactions in the same package") - testres = node.testmempoolaccept([signedtx1["hex"], signedtx1["hex"]]) + testres = node.testmempoolaccept([tx1["hex"], tx1["hex"]]) assert_equal(testres, [ - {"txid": tx1.rehash(), "wtxid": tx1.getwtxid(), "package-error": "conflict-in-package"}, - {"txid": tx1.rehash(), "wtxid": tx1.getwtxid(), "package-error": "conflict-in-package"} + {"txid": tx1["txid"], "wtxid": tx1["wtxid"], "package-error": "conflict-in-package"}, + {"txid": tx1["txid"], "wtxid": tx1["wtxid"], "package-error": "conflict-in-package"} ]) self.log.info("Test conflicting transactions in the same package") - testres = node.testmempoolaccept([signedtx1["hex"], signedtx2["hex"]]) + testres = node.testmempoolaccept([tx1["hex"], tx2["hex"]]) assert_equal(testres, [ - {"txid": tx1.rehash(), "wtxid": tx1.getwtxid(), "package-error": "conflict-in-package"}, - {"txid": tx2.rehash(), "wtxid": tx2.getwtxid(), "package-error": "conflict-in-package"} + {"txid": tx1["txid"], "wtxid": tx1["wtxid"], "package-error": "conflict-in-package"}, + {"txid": tx2["txid"], "wtxid": tx2["wtxid"], "package-error": "conflict-in-package"} ]) def test_rbf(self): node = self.nodes[0] - coin = self.coins.pop() - inputs = [{"txid": coin["txid"], "vout": 0, "sequence": MAX_BIP125_RBF_SEQUENCE}] - fee = Decimal('0.00125000') - output = {node.get_deterministic_priv_key().address: 50 - fee} - raw_replaceable_tx = node.createrawtransaction(inputs, output) - signed_replaceable_tx = node.signrawtransactionwithkey(hexstring=raw_replaceable_tx, privkeys=self.privkeys) - testres_replaceable = node.testmempoolaccept([signed_replaceable_tx["hex"]]) - replaceable_tx = tx_from_hex(signed_replaceable_tx["hex"]) - assert_equal(testres_replaceable, [ - {"txid": replaceable_tx.rehash(), "wtxid": replaceable_tx.getwtxid(), - "allowed": True, "vsize": replaceable_tx.get_vsize(), "fees": { "base": fee }} - ]) - # Replacement transaction is identical except has double the fee - replacement_tx = tx_from_hex(signed_replaceable_tx["hex"]) - replacement_tx.vout[0].nValue -= int(fee * COIN) # Doubled fee - signed_replacement_tx = node.signrawtransactionwithkey(replacement_tx.serialize().hex(), self.privkeys) - replacement_tx = tx_from_hex(signed_replacement_tx["hex"]) + coin = self.wallet.get_utxo() + fee = Decimal("0.00125000") + replaceable_tx = self.wallet.create_self_transfer(utxo_to_spend=coin, sequence=MAX_BIP125_RBF_SEQUENCE, fee = fee) + testres_replaceable = node.testmempoolaccept([replaceable_tx["hex"]])[0] + assert_equal(testres_replaceable["txid"], replaceable_tx["txid"]) + assert_equal(testres_replaceable["wtxid"], replaceable_tx["wtxid"]) + assert testres_replaceable["allowed"] + assert_equal(testres_replaceable["vsize"], replaceable_tx["tx"].get_vsize()) + assert_equal(testres_replaceable["fees"]["base"], fee) + assert_fee_amount(fee, replaceable_tx["tx"].get_vsize(), testres_replaceable["fees"]["effective-feerate"]) + assert_equal(testres_replaceable["fees"]["effective-includes"], [replaceable_tx["wtxid"]]) - self.log.info("Test that transactions within a package cannot replace each other") - testres_rbf_conflicting = node.testmempoolaccept([signed_replaceable_tx["hex"], signed_replacement_tx["hex"]]) + # Replacement transaction is identical except has double the fee + replacement_tx = self.wallet.create_self_transfer(utxo_to_spend=coin, sequence=MAX_BIP125_RBF_SEQUENCE, fee = 2 * fee) + testres_rbf_conflicting = node.testmempoolaccept([replaceable_tx["hex"], replacement_tx["hex"]]) assert_equal(testres_rbf_conflicting, [ - {"txid": replaceable_tx.rehash(), "wtxid": replaceable_tx.getwtxid(), "package-error": "conflict-in-package"}, - {"txid": replacement_tx.rehash(), "wtxid": replacement_tx.getwtxid(), "package-error": "conflict-in-package"} + {"txid": replaceable_tx["txid"], "wtxid": replaceable_tx["wtxid"], "package-error": "conflict-in-package"}, + {"txid": replacement_tx["txid"], "wtxid": replacement_tx["wtxid"], "package-error": "conflict-in-package"} ]) self.log.info("Test that packages cannot conflict with mempool transactions, even if a valid BIP125 RBF") - node.sendrawtransaction(signed_replaceable_tx["hex"]) - testres_rbf_single = node.testmempoolaccept([signed_replacement_tx["hex"]]) # This transaction is a valid BIP125 replace-by-fee + self.wallet.sendrawtransaction(from_node=node, tx_hex=replaceable_tx["hex"]) + testres_rbf_single = node.testmempoolaccept([replacement_tx["hex"]]) assert testres_rbf_single[0]["allowed"] testres_rbf_package = self.independent_txns_testres_blank + [{ - "txid": replacement_tx.rehash(), "wtxid": replacement_tx.getwtxid(), "allowed": False, + "txid": replacement_tx["txid"], "wtxid": replacement_tx["wtxid"], "allowed": False, "reject-reason": "bip125-replacement-disallowed" }] - self.assert_testres_equal(self.independent_txns_hex + [signed_replacement_tx["hex"]], testres_rbf_package) + self.assert_testres_equal(self.independent_txns_hex + [replacement_tx["hex"]], testres_rbf_package) def assert_equal_package_results(self, node, testmempoolaccept_result, submitpackage_result): """Assert that a successful submitpackage result is consistent with testmempoolaccept @@ -330,90 +289,78 @@ class RPCPackagesTest(BitcoinTestFramework): def test_submit_child_with_parents(self, num_parents, partial_submit): node = self.nodes[0] peer = node.add_p2p_connection(P2PTxInvStore()) - # Test a package with num_parents parents and 1 child transaction. - package_hex = [] + package_txns = [] - values = [] - scripts = [] + presubmitted_wtxids = set() for _ in range(num_parents): - parent_coin = self.coins.pop() - value = parent_coin["amount"] - (tx, txhex, value, spk) = make_chain(node, self.address, self.privkeys, parent_coin["txid"], value) - package_hex.append(txhex) - package_txns.append(tx) - values.append(value) - scripts.append(spk) + parent_tx = self.wallet.create_self_transfer(fee=DEFAULT_FEE) + package_txns.append(parent_tx) if partial_submit and random.choice([True, False]): - node.sendrawtransaction(txhex) - child_hex = create_child_with_parents(node, self.address, self.privkeys, package_txns, values, scripts) - package_hex.append(child_hex) - package_txns.append(tx_from_hex(child_hex)) + node.sendrawtransaction(parent_tx["hex"]) + presubmitted_wtxids.add(parent_tx["wtxid"]) + child_tx = self.wallet.create_self_transfer_multi(utxos_to_spend=[tx["new_utxo"] for tx in package_txns], fee_per_output=10000) #DEFAULT_FEE + package_txns.append(child_tx) - testmempoolaccept_result = node.testmempoolaccept(rawtxs=package_hex) - submitpackage_result = node.submitpackage(package=package_hex) + testmempoolaccept_result = node.testmempoolaccept(rawtxs=[tx["hex"] for tx in package_txns]) + submitpackage_result = node.submitpackage(package=[tx["hex"] for tx in package_txns]) # Check that each result is present, with the correct size and fees - for i in range(num_parents + 1): - tx = package_txns[i] + for package_txn in package_txns: + tx = package_txn["tx"] + assert tx.getwtxid() in submitpackage_result["tx-results"] wtxid = tx.getwtxid() assert wtxid in submitpackage_result["tx-results"] tx_result = submitpackage_result["tx-results"][wtxid] - assert_equal(tx_result, { - "txid": tx.rehash(), - "vsize": tx.get_vsize(), - "fees": { - "base": DEFAULT_FEE, - } - }) + assert_equal(tx_result["txid"], tx.rehash()) + assert_equal(tx_result["vsize"], tx.get_vsize()) + assert_equal(tx_result["fees"]["base"], DEFAULT_FEE) + if wtxid not in presubmitted_wtxids: + assert_fee_amount(DEFAULT_FEE, tx.get_vsize(), tx_result["fees"]["effective-feerate"]) + assert_equal(tx_result["fees"]["effective-includes"], [wtxid]) # submitpackage result should be consistent with testmempoolaccept and getmempoolentry self.assert_equal_package_results(node, testmempoolaccept_result, submitpackage_result) - # Package feerate is calculated for the remaining transactions after deduplication and - # individual submission. If only 0 or 1 transaction is left, e.g. because all transactions - # had high-feerates or were already in the mempool, no package feerate is provided. - # In this case, since all of the parents have high fees, each is accepted individually. - assert "package-feerate" not in submitpackage_result - # The node should announce each transaction. No guarantees for propagation. - peer.wait_for_broadcast([tx.getwtxid() for tx in package_txns]) + peer.wait_for_broadcast([tx["tx"].getwtxid() for tx in package_txns]) self.generate(node, 1) - def test_submit_cpfp(self): node = self.nodes[0] peer = node.add_p2p_connection(P2PTxInvStore()) - # 2 parent 1 child CPFP. First parent pays high fees, second parent pays 0 fees and is - # fee-bumped by the child. - coin_rich = self.coins.pop() - coin_poor = self.coins.pop() - tx_rich, hex_rich, value_rich, spk_rich = make_chain(node, self.address, self.privkeys, coin_rich["txid"], coin_rich["amount"]) - tx_poor, hex_poor, value_poor, spk_poor = make_chain(node, self.address, self.privkeys, coin_poor["txid"], coin_poor["amount"], fee=0) + # Package with 2 parents and 1 child. One parent pays for itself using modified fees, and + # another has 0 fees but is bumped by child. + tx_poor = self.wallet.create_self_transfer(fee=0, fee_rate=0) + tx_rich = self.wallet.create_self_transfer(fee=0, fee_rate=0) + node.prioritisetransaction(tx_rich["txid"], 0, int(DEFAULT_FEE * COIN)) package_txns = [tx_rich, tx_poor] - hex_child = create_child_with_parents(node, self.address, self.privkeys, package_txns, [value_rich, value_poor], [spk_rich, spk_poor]) - tx_child = tx_from_hex(hex_child) + coins = [tx["new_utxo"] for tx in package_txns] + tx_child = self.wallet.create_self_transfer_multi(utxos_to_spend=coins, fee_per_output=10000) #DEFAULT_FEE package_txns.append(tx_child) - submitpackage_result = node.submitpackage([hex_rich, hex_poor, hex_child]) + submitpackage_result = node.submitpackage([tx["hex"] for tx in package_txns]) - rich_parent_result = submitpackage_result["tx-results"][tx_rich.getwtxid()] - poor_parent_result = submitpackage_result["tx-results"][tx_poor.getwtxid()] - child_result = submitpackage_result["tx-results"][tx_child.getwtxid()] - assert_equal(rich_parent_result["fees"]["base"], DEFAULT_FEE) + rich_parent_result = submitpackage_result["tx-results"][tx_rich["wtxid"]] + poor_parent_result = submitpackage_result["tx-results"][tx_poor["wtxid"]] + child_result = submitpackage_result["tx-results"][tx_child["tx"].getwtxid()] + assert_equal(rich_parent_result["fees"]["base"], 0) assert_equal(poor_parent_result["fees"]["base"], 0) assert_equal(child_result["fees"]["base"], DEFAULT_FEE) - # Package feerate is calculated for the remaining transactions after deduplication and - # individual submission. Since this package had a 0-fee parent, package feerate must have - # been used and returned. - assert "package-feerate" in submitpackage_result - assert_fee_amount(DEFAULT_FEE, rich_parent_result["vsize"] + child_result["vsize"], submitpackage_result["package-feerate"]) + # The "rich" parent does not require CPFP so its effective feerate. + assert_fee_amount(DEFAULT_FEE, tx_rich["tx"].get_vsize(), rich_parent_result["fees"]["effective-feerate"]) + assert_equal(rich_parent_result["fees"]["effective-includes"], [tx_rich["wtxid"]]) + # The "poor" parent and child's effective feerates are the same, composed of the child's fee + # divided by their combined vsize. + assert_fee_amount(DEFAULT_FEE, tx_poor["tx"].get_vsize() + tx_child["tx"].get_vsize(), poor_parent_result["fees"]["effective-feerate"]) + assert_fee_amount(DEFAULT_FEE, tx_poor["tx"].get_vsize() + tx_child["tx"].get_vsize(), child_result["fees"]["effective-feerate"]) + assert_equal([tx_poor["wtxid"], tx_child["tx"].getwtxid()], poor_parent_result["fees"]["effective-includes"]) + assert_equal([tx_poor["wtxid"], tx_child["tx"].getwtxid()], child_result["fees"]["effective-includes"]) # The node will broadcast each transaction, still abiding by its peer's fee filter - peer.wait_for_broadcast([tx.getwtxid() for tx in package_txns]) + peer.wait_for_broadcast([tx["tx"].getwtxid() for tx in package_txns]) self.generate(node, 1) - def test_submitpackage(self): node = self.nodes[0] @@ -427,7 +374,7 @@ class RPCPackagesTest(BitcoinTestFramework): self.log.info("Submitpackage only allows packages of 1 child with its parents") # Chain of 3 transactions has too many generations - chain_hex, _ = create_raw_chain(node, self.coins.pop(), self.address, self.privkeys, 3) + chain_hex = [t["hex"] for t in self.wallet.create_self_transfer_chain(chain_length=25)] assert_raises_rpc_error(-25, "not-child-with-parents", node.submitpackage, chain_hex) diff --git a/test/functional/rpc_psbt.py b/test/functional/rpc_psbt.py index 8243f43736..58a80e37a2 100755 --- a/test/functional/rpc_psbt.py +++ b/test/functional/rpc_psbt.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2018-2021 The Bitcoin Core developers +# Copyright (c) 2018-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 the Partially Signed Transaction RPCs. @@ -36,6 +36,7 @@ from test_framework.util import ( assert_approx, assert_equal, assert_greater_than, + assert_greater_than_or_equal, assert_raises_rpc_error, find_output, find_vout_for_address, @@ -47,8 +48,9 @@ import json import os -# Create one-input, one-output, no-fee transaction: class PSBTTest(BitcoinTestFramework): + def add_options(self, parser): + self.add_wallet_options(parser) def set_test_params(self): self.num_nodes = 3 @@ -105,6 +107,65 @@ class PSBTTest(BitcoinTestFramework): self.connect_nodes(0, 1) self.connect_nodes(0, 2) + def test_input_confs_control(self): + self.nodes[0].createwallet("minconf") + wallet = self.nodes[0].get_wallet_rpc("minconf") + + # Fund the wallet with different chain heights + for _ in range(2): + self.nodes[1].sendmany("", {wallet.getnewaddress():1, wallet.getnewaddress():1}) + self.generate(self.nodes[1], 1) + + unconfirmed_txid = wallet.sendtoaddress(wallet.getnewaddress(), 0.5) + + self.log.info("Crafting PSBT using an unconfirmed input") + target_address = self.nodes[1].getnewaddress() + psbtx1 = wallet.walletcreatefundedpsbt([], {target_address: 0.1}, 0, {'fee_rate': 1, 'maxconf': 0})['psbt'] + + # Make sure we only had the one input + tx1_inputs = self.nodes[0].decodepsbt(psbtx1)['tx']['vin'] + assert_equal(len(tx1_inputs), 1) + + utxo1 = tx1_inputs[0] + assert_equal(unconfirmed_txid, utxo1['txid']) + + signed_tx1 = wallet.walletprocesspsbt(psbtx1)['psbt'] + final_tx1 = wallet.finalizepsbt(signed_tx1)['hex'] + txid1 = self.nodes[0].sendrawtransaction(final_tx1) + + mempool = self.nodes[0].getrawmempool() + assert txid1 in mempool + + self.log.info("Fail to craft a new PSBT that sends more funds with add_inputs = False") + assert_raises_rpc_error(-4, "The preselected coins total amount does not cover the transaction target. Please allow other inputs to be automatically selected or include more coins manually", wallet.walletcreatefundedpsbt, [{'txid': utxo1['txid'], 'vout': utxo1['vout']}], {target_address: 1}, 0, {'add_inputs': False}) + + self.log.info("Fail to craft a new PSBT with minconf above highest one") + assert_raises_rpc_error(-4, "Insufficient funds", wallet.walletcreatefundedpsbt, [{'txid': utxo1['txid'], 'vout': utxo1['vout']}], {target_address: 1}, 0, {'add_inputs': True, 'minconf': 3, 'fee_rate': 10}) + + self.log.info("Fail to broadcast a new PSBT with maxconf 0 due to BIP125 rules to verify it actually chose unconfirmed outputs") + psbt_invalid = wallet.walletcreatefundedpsbt([{'txid': utxo1['txid'], 'vout': utxo1['vout']}], {target_address: 1}, 0, {'add_inputs': True, 'maxconf': 0, 'fee_rate': 10})['psbt'] + signed_invalid = wallet.walletprocesspsbt(psbt_invalid)['psbt'] + final_invalid = wallet.finalizepsbt(signed_invalid)['hex'] + assert_raises_rpc_error(-26, "bad-txns-spends-conflicting-tx", self.nodes[0].sendrawtransaction, final_invalid) + + self.log.info("Craft a replacement adding inputs with highest confs possible") + psbtx2 = wallet.walletcreatefundedpsbt([{'txid': utxo1['txid'], 'vout': utxo1['vout']}], {target_address: 1}, 0, {'add_inputs': True, 'minconf': 2, 'fee_rate': 10})['psbt'] + tx2_inputs = self.nodes[0].decodepsbt(psbtx2)['tx']['vin'] + assert_greater_than_or_equal(len(tx2_inputs), 2) + for vin in tx2_inputs: + if vin['txid'] != unconfirmed_txid: + assert_greater_than_or_equal(self.nodes[0].gettxout(vin['txid'], vin['vout'])['confirmations'], 2) + + signed_tx2 = wallet.walletprocesspsbt(psbtx2)['psbt'] + final_tx2 = wallet.finalizepsbt(signed_tx2)['hex'] + txid2 = self.nodes[0].sendrawtransaction(final_tx2) + + mempool = self.nodes[0].getrawmempool() + assert txid1 not in mempool + assert txid2 in mempool + + wallet.unloadwallet() + def assert_change_type(self, psbtx, expected_type): """Assert that the given PSBT has a change output with the given type.""" @@ -119,7 +180,9 @@ class PSBTTest(BitcoinTestFramework): # If inputs are specified, do not automatically add more: utxo1 = self.nodes[0].listunspent()[0] - assert_raises_rpc_error(-4, "Insufficient funds", self.nodes[0].walletcreatefundedpsbt, [{"txid": utxo1['txid'], "vout": utxo1['vout']}], {self.nodes[2].getnewaddress():90}) + assert_raises_rpc_error(-4, "The preselected coins total amount does not cover the transaction target. " + "Please allow other inputs to be automatically selected or include more coins manually", + self.nodes[0].walletcreatefundedpsbt, [{"txid": utxo1['txid'], "vout": utxo1['vout']}], {self.nodes[2].getnewaddress():90}) psbtx1 = self.nodes[0].walletcreatefundedpsbt([{"txid": utxo1['txid'], "vout": utxo1['vout']}], {self.nodes[2].getnewaddress():90}, 0, {"add_inputs": True})['psbt'] assert_equal(len(self.nodes[0].decodepsbt(psbtx1)['tx']['vin']), 2) @@ -511,6 +574,8 @@ class PSBTTest(BitcoinTestFramework): # TODO: Re-enable this for segwit v1 # self.test_utxo_conversion() + self.test_input_confs_control() + # Test that psbts with p2pkh outputs are created properly p2pkh = self.nodes[0].getnewaddress(address_type='legacy') psbt = self.nodes[1].walletcreatefundedpsbt([], [{p2pkh : 1}], 0, {"includeWatching" : True}, True) diff --git a/test/functional/rpc_rawtransaction.py b/test/functional/rpc_rawtransaction.py index 930aaaa897..cdec4b2a85 100755 --- a/test/functional/rpc_rawtransaction.py +++ b/test/functional/rpc_rawtransaction.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2014-2021 The Bitcoin Core developers +# Copyright (c) 2014-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 the rawtransaction RPCs. @@ -14,8 +14,8 @@ 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 ( MAX_BIP125_RBF_SEQUENCE, CTransaction, @@ -54,8 +54,10 @@ class multidict(dict): class RawTransactionsTest(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 = 3 self.extra_args = [ ["-txindex"], @@ -65,8 +67,6 @@ class RawTransactionsTest(BitcoinTestFramework): # whitelist all peers to speed up tx relay / mempool sync for args in self.extra_args: args.append("-whitelist=noban@127.0.0.1") - self.requires_wallet = self.is_specified_wallet_compiled() - self.supports_cli = False def setup_network(self): @@ -75,17 +75,16 @@ class RawTransactionsTest(BitcoinTestFramework): def run_test(self): self.wallet = MiniWallet(self.nodes[0]) - self.log.info("Prepare some coins for multiple *rawtransaction commands") - self.generate(self.wallet, 10) - 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() self.decoderawtransaction_tests() self.transaction_version_number_tests() - if self.requires_wallet and not self.options.descriptors: + if self.is_specified_wallet_compiled() and not self.options.descriptors: + self.import_deterministic_coinbase_privkeys() self.raw_multisig_transaction_legacy_tests() def getrawtransaction_tests(self): @@ -114,6 +113,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']) @@ -124,13 +124,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'] @@ -143,9 +144,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) @@ -170,6 +172,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/rpc_scanblocks.py b/test/functional/rpc_scanblocks.py index 743cdf89ed..126e95362b 100755 --- a/test/functional/rpc_scanblocks.py +++ b/test/functional/rpc_scanblocks.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2021 The Bitcoin Core developers +# Copyright (c) 2021-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 the scanblocks RPC call.""" @@ -27,7 +27,6 @@ class ScanblocksTest(BitcoinTestFramework): def run_test(self): node = self.nodes[0] wallet = MiniWallet(node) - wallet.rescan_utxos() # send 1.0, mempool only _, spk_1, addr_1 = getnewdestination() @@ -46,7 +45,7 @@ class ScanblocksTest(BitcoinTestFramework): self.wait_until(lambda: all(i["synced"] for i in node.getindexinfo().values())) out = node.scanblocks("start", [f"addr({addr_1})"]) - assert(blockhash in out['relevant_blocks']) + assert blockhash in out['relevant_blocks'] assert_equal(height, out['to_height']) assert_equal(0, out['from_height']) @@ -56,24 +55,30 @@ class ScanblocksTest(BitcoinTestFramework): # make sure the blockhash is not in the filter result if we set the start_height # to the just mined block (unlikely to hit a false positive) - assert(blockhash not in node.scanblocks( - "start", [f"addr({addr_1})"], height_new)['relevant_blocks']) + assert blockhash not in node.scanblocks( + "start", [f"addr({addr_1})"], height_new)['relevant_blocks'] # make sure the blockhash is present when using the first mined block as start_height - assert(blockhash in node.scanblocks( - "start", [f"addr({addr_1})"], height)['relevant_blocks']) + assert blockhash in node.scanblocks( + "start", [f"addr({addr_1})"], height)['relevant_blocks'] + for v in [False, True]: + assert blockhash in node.scanblocks( + action="start", + scanobjects=[f"addr({addr_1})"], + start_height=height, + options={"filter_false_positives": v})['relevant_blocks'] # also test the stop height - assert(blockhash in node.scanblocks( - "start", [f"addr({addr_1})"], height, height)['relevant_blocks']) + assert blockhash in node.scanblocks( + "start", [f"addr({addr_1})"], height, height)['relevant_blocks'] # use the stop_height to exclude the relevant block - assert(blockhash not in node.scanblocks( - "start", [f"addr({addr_1})"], 0, height - 1)['relevant_blocks']) + assert blockhash not in node.scanblocks( + "start", [f"addr({addr_1})"], 0, height - 1)['relevant_blocks'] # make sure the blockhash is present when using the first mined block as start_height - assert(blockhash in node.scanblocks( - "start", [{"desc": f"pkh({parent_key}/*)", "range": [0, 100]}], height)['relevant_blocks']) + assert blockhash in node.scanblocks( + "start", [{"desc": f"pkh({parent_key}/*)", "range": [0, 100]}], height)['relevant_blocks'] # check that false-positives are included in the result now; note that # finding a false-positive at runtime would take too long, hence we simply @@ -89,13 +94,16 @@ class ScanblocksTest(BitcoinTestFramework): false_positive_hash = bip158_basic_element_hash(false_positive_spk, 1, genesis_blockhash) assert_equal(genesis_coinbase_hash, false_positive_hash) - assert(genesis_blockhash in node.scanblocks( - "start", [{"desc": f"raw({genesis_coinbase_spk.hex()})"}], 0, 0)['relevant_blocks']) - assert(genesis_blockhash in node.scanblocks( - "start", [{"desc": f"raw({false_positive_spk.hex()})"}], 0, 0)['relevant_blocks']) + assert genesis_blockhash in node.scanblocks( + "start", [{"desc": f"raw({genesis_coinbase_spk.hex()})"}], 0, 0)['relevant_blocks'] + assert genesis_blockhash in node.scanblocks( + "start", [{"desc": f"raw({false_positive_spk.hex()})"}], 0, 0)['relevant_blocks'] - # TODO: after an "accurate" mode for scanblocks is implemented (e.g. PR #26325) - # check here that it filters out the false-positive + # check that the filter_false_positives option works + assert genesis_blockhash in node.scanblocks( + "start", [{"desc": f"raw({genesis_coinbase_spk.hex()})"}], 0, 0, "basic", {"filter_false_positives": True})['relevant_blocks'] + assert genesis_blockhash not in node.scanblocks( + "start", [{"desc": f"raw({false_positive_spk.hex()})"}], 0, 0, "basic", {"filter_false_positives": True})['relevant_blocks'] # test node with disabled blockfilterindex assert_raises_rpc_error(-1, "Index is not enabled for filtertype basic", diff --git a/test/functional/rpc_scantxoutset.py b/test/functional/rpc_scantxoutset.py index 6eb5b493b9..507a4f48e5 100755 --- a/test/functional/rpc_scantxoutset.py +++ b/test/functional/rpc_scantxoutset.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2018-2021 The Bitcoin Core developers +# Copyright (c) 2018-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 the scantxoutset rpc call.""" @@ -31,7 +31,6 @@ class ScantxoutsetTest(BitcoinTestFramework): def run_test(self): self.wallet = MiniWallet(self.nodes[0]) - self.wallet.rescan_utxos() self.log.info("Test if we find coinbase outputs.") assert_equal(sum(u["coinbase"] for u in self.nodes[0].scantxoutset("start", [self.wallet.get_descriptor()])["unspents"]), 49) diff --git a/test/functional/rpc_signer.py b/test/functional/rpc_signer.py index de17b2b929..4300190387 100755 --- a/test/functional/rpc_signer.py +++ b/test/functional/rpc_signer.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2017-2021 The Bitcoin Core developers +# Copyright (c) 2017-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 external signer. diff --git a/test/functional/rpc_signmessagewithprivkey.py b/test/functional/rpc_signmessagewithprivkey.py index 6635da150f..c5df22157d 100755 --- a/test/functional/rpc_signmessagewithprivkey.py +++ b/test/functional/rpc_signmessagewithprivkey.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2016-2021 The Bitcoin Core developers +# Copyright (c) 2016-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 RPC commands for signing messages with private key.""" diff --git a/test/functional/rpc_txoutproof.py b/test/functional/rpc_txoutproof.py index d04d05962f..60b7ce8d20 100755 --- a/test/functional/rpc_txoutproof.py +++ b/test/functional/rpc_txoutproof.py @@ -4,7 +4,6 @@ # file COPYING or http://www.opensource.org/licenses/mit-license.php. """Test gettxoutproof and verifytxoutproof RPCs.""" -from test_framework.blocktools import COINBASE_MATURITY from test_framework.messages import ( CMerkleBlock, from_hex, @@ -20,7 +19,6 @@ from test_framework.wallet import MiniWallet class MerkleBlockTest(BitcoinTestFramework): def set_test_params(self): self.num_nodes = 2 - self.setup_clean_chain = True self.extra_args = [ [], ["-txindex"], @@ -28,12 +26,9 @@ class MerkleBlockTest(BitcoinTestFramework): def run_test(self): miniwallet = MiniWallet(self.nodes[0]) - # Add enough mature utxos to the wallet, so that all txs spend confirmed coins - self.generate(miniwallet, 5) - self.generate(self.nodes[0], COINBASE_MATURITY) chain_height = self.nodes[1].getblockcount() - assert_equal(chain_height, 105) + assert_equal(chain_height, 200) txid1 = miniwallet.send_self_transfer(from_node=self.nodes[0])['txid'] txid2 = miniwallet.send_self_transfer(from_node=self.nodes[0])['txid'] diff --git a/test/functional/rpc_uptime.py b/test/functional/rpc_uptime.py index 024e8aec1a..cb99e483ec 100755 --- a/test/functional/rpc_uptime.py +++ b/test/functional/rpc_uptime.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2017-2021 The Bitcoin Core developers +# Copyright (c) 2017-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 the RPC call related to the uptime command. diff --git a/test/functional/rpc_users.py b/test/functional/rpc_users.py index 1a35a57802..8cc3ec401e 100755 --- a/test/functional/rpc_users.py +++ b/test/functional/rpc_users.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2020 The Bitcoin Core developers +# Copyright (c) 2015-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 multiple RPC users.""" @@ -53,13 +53,13 @@ class HTTPBasicsTest(BitcoinTestFramework): # Generate RPCAUTH with specified password self.rt2password = "8/F3uMDw4KSEbw96U3CA1C4X05dkHDN2BPFjTgZW4KI=" - p = subprocess.Popen([sys.executable, gen_rpcauth, 'rt2', self.rt2password], stdout=subprocess.PIPE, universal_newlines=True) + p = subprocess.Popen([sys.executable, gen_rpcauth, 'rt2', self.rt2password], stdout=subprocess.PIPE, text=True) lines = p.stdout.read().splitlines() rpcauth2 = lines[1] # Generate RPCAUTH without specifying password self.user = ''.join(SystemRandom().choice(string.ascii_letters + string.digits) for _ in range(10)) - p = subprocess.Popen([sys.executable, gen_rpcauth, self.user], stdout=subprocess.PIPE, universal_newlines=True) + p = subprocess.Popen([sys.executable, gen_rpcauth, self.user], stdout=subprocess.PIPE, text=True) lines = p.stdout.read().splitlines() rpcauth3 = lines[1] self.password = lines[3] diff --git a/test/functional/test-shell.md b/test/functional/test-shell.md index 78737509cb..80f4e88109 100644 --- a/test/functional/test-shell.md +++ b/test/functional/test-shell.md @@ -93,8 +93,10 @@ We now let the first node generate 101 regtest blocks, and direct the coinbase rewards to a wallet address owned by the mining node. ``` +>>> test.nodes[0].createwallet('default') +{'name': 'default', 'warning': 'Empty string given as passphrase, wallet will not be encrypted.'} >>> address = test.nodes[0].getnewaddress() ->>> test.self.generatetoaddress(nodes[0], 101, address) +>>> test.generatetoaddress(test.nodes[0], 101, address) ['2b98dd0044aae6f1cca7f88a0acf366a4bfe053c7f7b00da3c0d115f03d67efb', ... ``` Since the two nodes are both initialized by default to establish an outbound diff --git a/test/functional/test_framework/address.py b/test/functional/test_framework/address.py index 92244b5ed8..959a2a65bd 100644 --- a/test/functional/test_framework/address.py +++ b/test/functional/test_framework/address.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2016-2021 The Bitcoin Core developers +# Copyright (c) 2016-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. """Encode and decode Bitcoin addresses. diff --git a/test/functional/test_framework/authproxy.py b/test/functional/test_framework/authproxy.py index c4ffd1fbf6..dd20b28550 100644 --- a/test/functional/test_framework/authproxy.py +++ b/test/functional/test_framework/authproxy.py @@ -131,10 +131,12 @@ class AuthServiceProxy(): json.dumps(args or argsn, default=EncodeDecimal, ensure_ascii=self.ensure_ascii), )) if args and argsn: - raise ValueError('Cannot handle both named and positional arguments') + params = dict(args=args, **argsn) + else: + params = args or argsn return {'version': '1.1', 'method': self._service_name, - 'params': args or argsn, + 'params': params, 'id': AuthServiceProxy.__id_count} def __call__(self, *args, **argsn): diff --git a/test/functional/test_framework/blocktools.py b/test/functional/test_framework/blocktools.py index 574ea10356..b08cc6a3f9 100644 --- a/test/functional/test_framework/blocktools.py +++ b/test/functional/test_framework/blocktools.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2021 The Bitcoin Core developers +# Copyright (c) 2015-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. """Utilities for manipulating blocks and transactions.""" @@ -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/key.py b/test/functional/test_framework/key.py index 68afc1383d..ad305ce1ef 100644 --- a/test/functional/test_framework/key.py +++ b/test/functional/test_framework/key.py @@ -139,7 +139,7 @@ class EllipticCurve: See https://en.wikibooks.org/wiki/Cryptography/Prime_Curve/Jacobian_Coordinates - Point Addition (with affine point)""" x1, y1, z1 = p1 x2, y2, z2 = p2 - assert(z2 == 1) + assert z2 == 1 # Adding to the point at infinity is a no-op if z1 == 0: return p2 @@ -262,7 +262,7 @@ class ECPubKey(): return self.valid def get_bytes(self): - assert(self.valid) + assert self.valid p = SECP256K1.affine(self.p) if p is None: return None @@ -276,7 +276,7 @@ class ECPubKey(): See https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm for the ECDSA verifier algorithm""" - assert(self.valid) + assert self.valid # Extract r and s from the DER formatted signature. Return false for # any DER encoding errors. @@ -349,7 +349,7 @@ class ECKey(): def set(self, secret, compressed): """Construct a private key object with given 32-byte secret and compressed flag.""" - assert(len(secret) == 32) + assert len(secret) == 32 secret = int.from_bytes(secret, 'big') self.valid = (secret > 0 and secret < SECP256K1_ORDER) if self.valid: @@ -362,7 +362,7 @@ class ECKey(): def get_bytes(self): """Retrieve the 32-byte representation of this key.""" - assert(self.valid) + assert self.valid return self.secret.to_bytes(32, 'big') @property @@ -375,7 +375,7 @@ class ECKey(): def get_pubkey(self): """Compute an ECPubKey object for this secret key.""" - assert(self.valid) + assert self.valid ret = ECPubKey() p = SECP256K1.mul([(SECP256K1_G, self.secret)]) ret.p = p @@ -388,7 +388,7 @@ class ECKey(): See https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm for the ECDSA signer algorithm.""" - assert(self.valid) + assert self.valid z = int.from_bytes(msg, 'big') # Note: no RFC6979 by default, but a simple random nonce (some tests rely on distinct transactions for the same operation) if rfc6979: diff --git a/test/functional/test_framework/messages.py b/test/functional/test_framework/messages.py index 252b49cc6d..8c6f68cacb 100755 --- a/test/functional/test_framework/messages.py +++ b/test/functional/test_framework/messages.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # Copyright (c) 2010 ArtForz -- public domain half-a-node # Copyright (c) 2012 Jeff Garzik -# Copyright (c) 2010-2021 The Bitcoin Core developers +# Copyright (c) 2010-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. """Bitcoin test framework primitive and message structures @@ -1840,29 +1840,23 @@ class msg_cfcheckpt: self.filter_type, self.stop_hash) class msg_sendtxrcncl: - __slots__ = ("initiator", "responder", "version", "salt") + __slots__ = ("version", "salt") msgtype = b"sendtxrcncl" def __init__(self): - self.initiator = False - self.responder = False self.version = 0 self.salt = 0 def deserialize(self, f): - self.initiator = struct.unpack("<?", f.read(1))[0] - self.responder = struct.unpack("<?", f.read(1))[0] self.version = struct.unpack("<I", f.read(4))[0] self.salt = struct.unpack("<Q", f.read(8))[0] def serialize(self): r = b"" - r += struct.pack("<?", self.initiator) - r += struct.pack("<?", self.responder) r += struct.pack("<I", self.version) r += struct.pack("<Q", self.salt) return r def __repr__(self): - return "msg_sendtxrcncl(initiator=%i, responder=%i, version=%lu, salt=%lu)" %\ - (self.initiator, self.responder, self.version, self.salt) + return "msg_sendtxrcncl(version=%lu, salt=%lu)" %\ + (self.version, self.salt) diff --git a/test/functional/test_framework/netutil.py b/test/functional/test_framework/netutil.py index b64f66e69b..fcea4b2f68 100644 --- a/test/functional/test_framework/netutil.py +++ b/test/functional/test_framework/netutil.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2014-2021 The Bitcoin Core developers +# Copyright (c) 2014-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. """Linux network utilities. diff --git a/test/functional/test_framework/p2p.py b/test/functional/test_framework/p2p.py index 05b46e630c..59157f4755 100755 --- a/test/functional/test_framework/p2p.py +++ b/test/functional/test_framework/p2p.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # Copyright (c) 2010 ArtForz -- public domain half-a-node # Copyright (c) 2012 Jeff Garzik -# Copyright (c) 2010-2021 The Bitcoin Core developers +# Copyright (c) 2010-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 objects for interacting with a bitcoind node over the p2p protocol. diff --git a/test/functional/test_framework/script.py b/test/functional/test_framework/script.py index 2b70eab4e4..443cae86a1 100644 --- a/test/functional/test_framework/script.py +++ b/test/functional/test_framework/script.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2021 The Bitcoin Core developers +# Copyright (c) 2015-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. """Functionality to build scripts, as well as signature hash functions. @@ -12,7 +12,7 @@ import struct import unittest from typing import List, Dict -from .key import TaggedHash, tweak_add_pubkey +from .key import TaggedHash, tweak_add_pubkey, compute_xonly_pubkey from .messages import ( CTransaction, @@ -597,6 +597,13 @@ class CScript(bytes): lastOpcode = opcode return n + def IsWitnessProgram(self): + """A witness program is any valid CScript that consists of a 1-byte + push opcode followed by a data push between 2 and 40 bytes.""" + return ((4 <= len(self) <= 42) and + (self[0] == OP_0 or (OP_1 <= self[0] <= OP_16)) and + (self[1] + 2 == len(self))) + SIGHASH_DEFAULT = 0 # Taproot-only default, semantics same as SIGHASH_ALL SIGHASH_ALL = 1 @@ -824,10 +831,10 @@ def taproot_tree_helper(scripts): if len(scripts) == 1: # One entry: treat as a leaf script = scripts[0] - assert(not callable(script)) + assert not callable(script) if isinstance(script, list): return taproot_tree_helper(script) - assert(isinstance(script, tuple)) + assert isinstance(script, tuple) version = LEAF_VERSION_TAPSCRIPT name = script[0] code = script[1] @@ -872,7 +879,7 @@ TaprootInfo = namedtuple("TaprootInfo", "scriptPubKey,internal_pubkey,negflag,tw # - merklebranch: the merkle branch to use for this leaf (32*N bytes) TaprootLeafInfo = namedtuple("TaprootLeafInfo", "script,version,merklebranch,leaf_hash") -def taproot_construct(pubkey, scripts=None): +def taproot_construct(pubkey, scripts=None, treat_internal_as_infinity=False): """Construct a tree of Taproot spending conditions pubkey: a 32-byte xonly pubkey for the internal pubkey (bytes) @@ -891,7 +898,10 @@ def taproot_construct(pubkey, scripts=None): ret, h = taproot_tree_helper(scripts) tweak = TaggedHash("TapTweak", pubkey + h) - tweaked, negated = tweak_add_pubkey(pubkey, tweak) + if treat_internal_as_infinity: + tweaked, negated = compute_xonly_pubkey(tweak) + else: + tweaked, negated = tweak_add_pubkey(pubkey, tweak) leaves = dict((name, TaprootLeafInfo(script, version, merklebranch, leaf)) for name, version, script, merklebranch, leaf in ret) return TaprootInfo(CScript([OP_1, tweaked]), pubkey, negated + 0, tweak, leaves, h, tweaked) diff --git a/test/functional/test_framework/script_util.py b/test/functional/test_framework/script_util.py index b114002145..62894cc0f4 100755 --- a/test/functional/test_framework/script_util.py +++ b/test/functional/test_framework/script_util.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2019-2021 The Bitcoin Core developers +# Copyright (c) 2019-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. """Useful Script constants and utils.""" @@ -13,12 +13,13 @@ from test_framework.script import ( OP_EQUAL, OP_EQUALVERIFY, OP_HASH160, + OP_RETURN, hash160, sha256, ) # To prevent a "tx-size-small" policy rule error, a transaction has to have a -# non-witness size of at least 82 bytes (MIN_STANDARD_TX_NONWITNESS_SIZE in +# non-witness size of at least 65 bytes (MIN_STANDARD_TX_NONWITNESS_SIZE in # src/policy/policy.h). Considering a Tx with the smallest possible single # input (blank, empty scriptSig), and with an output omitting the scriptPubKey, # we get to a minimum size of 60 bytes: @@ -28,15 +29,15 @@ from test_framework.script import ( # Output: 8 [Amount] + 1 [scriptPubKeyLen] = 9 bytes # # Hence, the scriptPubKey of the single output has to have a size of at -# least 22 bytes, which corresponds to the size of a P2WPKH scriptPubKey. -# The following script constant consists of a single push of 21 bytes of 'a': -# <PUSH_21> <21-bytes of 'a'> -# resulting in a 22-byte size. It should be used whenever (small) fake -# scriptPubKeys are needed, to guarantee that the minimum transaction size is -# met. -DUMMY_P2WPKH_SCRIPT = CScript([b'a' * 21]) -DUMMY_2_P2WPKH_SCRIPT = CScript([b'b' * 21]) - +# least 5 bytes. +MIN_STANDARD_TX_NONWITNESS_SIZE = 65 +MIN_PADDING = MIN_STANDARD_TX_NONWITNESS_SIZE - 10 - 41 - 9 +assert MIN_PADDING == 5 + +# This script cannot be spent, allowing dust output values under +# standardness checks +DUMMY_MIN_OP_RETURN_SCRIPT = CScript([OP_RETURN] + ([OP_0] * (MIN_PADDING - 1))) +assert len(DUMMY_MIN_OP_RETURN_SCRIPT) == MIN_PADDING def key_to_p2pk_script(key): key = check_key(key) diff --git a/test/functional/test_framework/siphash.py b/test/functional/test_framework/siphash.py index 5ad245cf1b..884dbcab46 100644 --- a/test/functional/test_framework/siphash.py +++ b/test/functional/test_framework/siphash.py @@ -31,7 +31,7 @@ def siphash_round(v0, v1, v2, v3): def siphash(k0, k1, data): - assert(type(data) == bytes) + assert type(data) == bytes v0 = 0x736f6d6570736575 ^ k0 v1 = 0x646f72616e646f6d ^ k1 v2 = 0x6c7967656e657261 ^ k0 @@ -61,5 +61,5 @@ def siphash(k0, k1, data): def siphash256(k0, k1, num): - assert(type(num) == int) + assert type(num) == int return siphash(k0, k1, num.to_bytes(32, 'little')) diff --git a/test/functional/test_framework/test_framework.py b/test/functional/test_framework/test_framework.py index b1164b98fd..823958397d 100755 --- a/test/functional/test_framework/test_framework.py +++ b/test/functional/test_framework/test_framework.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2014-2021 The Bitcoin Core developers +# Copyright (c) 2014-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. """Base class for RPC testing.""" @@ -97,6 +97,7 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass): self.chain: str = 'regtest' self.setup_clean_chain: bool = False self.nodes: List[TestNode] = [] + self.extra_args = None self.network_thread = None self.rpc_timeout = 60 # Wait for up to 60 seconds for the RPC server to respond self.supports_cli = True @@ -113,7 +114,7 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass): self.wallet_names = None # By default the wallet is not required. Set to true by skip_if_no_wallet(). # When False, we ignore wallet_names regardless of what it is. - self.requires_wallet = False + self._requires_wallet = False # Disable ThreadOpenConnections by default, so that adding entries to # addrman will not result in automatic connections to them. self.disable_autoconnect = True @@ -194,12 +195,6 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass): help="set a random seed for deterministically reproducing a previous test run") parser.add_argument('--timeout-factor', dest="timeout_factor", type=float, default=1.0, help='adjust test timeouts by a factor. Setting it to 0 disables all timeouts') - group = parser.add_mutually_exclusive_group() - group.add_argument("--descriptors", action='store_const', const=True, - help="Run test using a descriptor wallet", dest='descriptors') - group.add_argument("--legacy-wallet", action='store_const', const=False, - help="Run test using legacy wallets", dest='descriptors') - self.add_options(parser) # Running TestShell in a Jupyter notebook causes an additional -f argument # To keep TestShell from failing with an "unrecognized argument" error, we add a dummy "-f" argument @@ -212,15 +207,22 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass): config.read_file(open(self.options.configfile)) self.config = config - if self.options.descriptors is None: - # Prefer BDB unless it isn't available - if self.is_bdb_compiled(): - self.options.descriptors = False - elif self.is_sqlite_compiled(): + if "descriptors" not in self.options: + # Wallet is not required by the test at all 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. + # So set it to None to force -disablewallet, because the wallet is not needed. + self.options.descriptors = None + elif self.options.descriptors is None: + # Some wallet is either required or optionally used by the test. + # 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. + # So set it to None, which will also set -disablewallet. self.options.descriptors = None PortSeed.n = self.options.port_seed @@ -407,12 +409,9 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass): def setup_nodes(self): """Override this method to customize test node setup""" - extra_args = [[]] * self.num_nodes - if hasattr(self, "extra_args"): - extra_args = self.extra_args - self.add_nodes(self.num_nodes, extra_args) + self.add_nodes(self.num_nodes, self.extra_args) self.start_nodes() - if self.requires_wallet: + if self._requires_wallet: self.import_deterministic_coinbase_privkeys() if not self.setup_clean_chain: for n in self.nodes: @@ -446,6 +445,21 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass): # Public helper methods. These can be accessed by the subclass test scripts. + def add_wallet_options(self, parser, *, descriptors=True, legacy=True): + kwargs = {} + if descriptors + legacy == 1: + # If only one type can be chosen, set it as default + kwargs["default"] = descriptors + group = parser.add_mutually_exclusive_group( + # If only one type is allowed, require it to be set in test_runner.py + required=os.getenv("REQUIRE_WALLET_TYPE_SET") == "1" and "default" in kwargs) + if descriptors: + group.add_argument("--descriptors", action='store_const', const=True, **kwargs, + help="Run test using a descriptor wallet", dest='descriptors') + if legacy: + group.add_argument("--legacy-wallet", action='store_const', const=False, **kwargs, + help="Run test using legacy wallets", dest='descriptors') + def add_nodes(self, num_nodes: int, extra_args=None, *, rpchost=None, binary=None, binary_cli=None, versions=None): """Instantiate TestNode objects. @@ -594,6 +608,10 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass): self.wait_until(lambda: sum(peer['version'] != 0 for peer in to_connection.getpeerinfo()) == to_num_peers) self.wait_until(lambda: sum(peer['bytesrecv_per_msg'].pop('verack', 0) == 24 for peer in from_connection.getpeerinfo()) == from_num_peers) self.wait_until(lambda: sum(peer['bytesrecv_per_msg'].pop('verack', 0) == 24 for peer in to_connection.getpeerinfo()) == to_num_peers) + # The message bytes are counted before processing the message, so make + # sure it was fully processed by waiting for a ping. + self.wait_until(lambda: sum(peer["bytesrecv_per_msg"].pop("pong", 0) >= 32 for peer in from_connection.getpeerinfo()) == from_num_peers) + self.wait_until(lambda: sum(peer["bytesrecv_per_msg"].pop("pong", 0) >= 32 for peer in to_connection.getpeerinfo()) == to_num_peers) def disconnect_nodes(self, a, b): def disconnect_nodes_helper(node_a, node_b): @@ -836,6 +854,13 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass): except ImportError: raise SkipTest("python3-zmq module not available.") + def skip_if_no_py_sqlite3(self): + """Attempt to import the sqlite3 package and skip the test if the import fails.""" + try: + import sqlite3 # noqa + except ImportError: + raise SkipTest("sqlite3 module not available.") + def skip_if_no_python_bcc(self): """Attempt to import the bcc package and skip the tests if the import fails.""" try: @@ -866,7 +891,7 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass): def skip_if_no_wallet(self): """Skip the running test if wallet has not been compiled.""" - self.requires_wallet = True + self._requires_wallet = True if not self.is_wallet_compiled(): raise SkipTest("wallet has not been compiled.") if self.options.descriptors: diff --git a/test/functional/test_framework/test_node.py b/test/functional/test_framework/test_node.py index 0075fa0996..f3d81ed7da 100755 --- a/test/functional/test_framework/test_node.py +++ b/test/functional/test_framework/test_node.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2017-2021 The Bitcoin Core developers +# Copyright (c) 2017-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. """Class for bitcoind node under test""" @@ -105,6 +105,9 @@ class TestNode(): "-debugexclude=rand", "-uacomment=testnode%d" % i, ] + if self.descriptors is None: + self.args.append("-disablewallet") + if use_valgrind: default_suppressions_file = os.path.join( os.path.dirname(os.path.realpath(__file__)), @@ -334,7 +337,7 @@ class TestNode(): return self.log.debug("Stopping node") try: - # Do not use wait argument when testing older nodes, e.g. in feature_backwards_compatibility.py + # Do not use wait argument when testing older nodes, e.g. in wallet_backwards_compatibility.py if self.version_is_at_least(180000): self.stop(wait=wait) else: @@ -656,7 +659,8 @@ class TestNode(): return len([peer for peer in self.getpeerinfo() if peer['subver'] == P2P_SUBVERSION]) def disconnect_p2ps(self): - """Close all p2p connections to the node.""" + """Close all p2p connections to the node. + Use only after each p2p has sent a version message to ensure the wait works.""" for p in self.p2ps: p.peer_disconnect() del self.p2ps[:] @@ -719,7 +723,6 @@ class TestNodeCLI(): """Run bitcoin-cli command. Deserializes returned string as python object.""" pos_args = [arg_to_cli(arg) for arg in args] named_args = [str(key) + "=" + arg_to_cli(value) for (key, value) in kwargs.items()] - assert not (pos_args and named_args), "Cannot use positional arguments and named arguments in the same bitcoin-cli call" p_args = [self.binary, "-datadir=" + self.datadir] + self.options if named_args: p_args += ["-named"] @@ -727,7 +730,7 @@ class TestNodeCLI(): p_args += [command] p_args += pos_args + named_args self.log.debug("Running bitcoin-cli {}".format(p_args[2:])) - process = subprocess.Popen(p_args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True) + process = subprocess.Popen(p_args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) cli_stdout, cli_stderr = process.communicate(input=self.input) returncode = process.poll() if returncode: diff --git a/test/functional/test_framework/test_shell.py b/test/functional/test_framework/test_shell.py index 26df128f1f..09ccec28a1 100644 --- a/test/functional/test_framework/test_shell.py +++ b/test/functional/test_framework/test_shell.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2019 The Bitcoin Core developers +# Copyright (c) 2019-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. @@ -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/util.py b/test/functional/test_framework/util.py index bfc835f272..9048a915b2 100644 --- a/test/functional/test_framework/util.py +++ b/test/functional/test_framework/util.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2014-2021 The Bitcoin Core developers +# Copyright (c) 2014-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. """Helpful routines for regression testing.""" @@ -488,28 +488,6 @@ def find_output(node, txid, amount, *, blockhash=None): raise RuntimeError("find_output txid %s : %s not found" % (txid, str(amount))) -def chain_transaction(node, parent_txids, vouts, value, fee, num_outputs): - """Build and send a transaction that spends the given inputs (specified - by lists of parent_txid:vout each), with the desired total value and fee, - equally divided up to the desired number of outputs. - - Returns a tuple with the txid and the amount sent per output. - """ - send_value = satoshi_round((value - fee)/num_outputs) - inputs = [] - for (txid, vout) in zip(parent_txids, vouts): - inputs.append({'txid' : txid, 'vout' : vout}) - outputs = {} - for _ in range(num_outputs): - outputs[node.getnewaddress()] = send_value - rawtx = node.createrawtransaction(inputs, outputs, 0, True) - signedtx = node.signrawtransactionwithwallet(rawtx) - txid = node.sendrawtransaction(signedtx['hex']) - fulltx = node.getrawtransaction(txid, 1) - assert len(fulltx['vout']) == num_outputs # make sure we didn't generate a change output - return (txid, send_value) - - # Create large OP_RETURN txouts that can be appended to a transaction # to make it large (helper for constructing large transactions). The # total serialized size of the txouts is about 66k vbytes. diff --git a/test/functional/test_framework/wallet.py b/test/functional/test_framework/wallet.py index 374fda5c23..f3253630c4 100644 --- a/test/functional/test_framework/wallet.py +++ b/test/functional/test_framework/wallet.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2020-2021 The Bitcoin Core developers +# Copyright (c) 2020-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. """A limited-functionality wallet, which may replace a real wallet in tests""" @@ -32,7 +32,6 @@ from test_framework.messages import ( CTxIn, CTxInWitness, CTxOut, - tx_from_hex, ) from test_framework.script import ( CScript, @@ -56,6 +55,7 @@ from test_framework.util import ( assert_equal, assert_greater_than_or_equal, ) +from test_framework.blocktools import COINBASE_MATURITY DEFAULT_FEE = Decimal("0.0001") @@ -101,8 +101,15 @@ class MiniWallet: self._address, self._internal_key = create_deterministic_address_bcrt1_p2tr_op_true() self._scriptPubKey = bytes.fromhex(self._test_node.validateaddress(self._address)['scriptPubKey']) - def _create_utxo(self, *, txid, vout, value, height): - return {"txid": txid, "vout": vout, "value": value, "height": height} + # When the pre-mined test framework chain is used, it contains coinbase + # outputs to the MiniWallet's default address in blocks 76-100 + # (see method BitcoinTestFramework._initialize_chain()) + # The MiniWallet needs to rescan_utxos() in order to account + # for those mature UTXOs, so that all txs spend confirmed coins + self.rescan_utxos() + + def _create_utxo(self, *, txid, vout, value, height, coinbase, confirmations): + return {"txid": txid, "vout": vout, "value": value, "height": height, "coinbase": coinbase, "confirmations": confirmations} def _bulk_tx(self, tx, target_weight): """Pad a transaction with extra outputs until it reaches a target weight (or higher). @@ -119,13 +126,25 @@ class MiniWallet: def get_balance(self): return sum(u['value'] for u in self._utxos) - def rescan_utxos(self): + def rescan_utxos(self, *, include_mempool=True): """Drop all utxos and rescan the utxo set""" self._utxos = [] res = self._test_node.scantxoutset(action="start", scanobjects=[self.get_descriptor()]) assert_equal(True, res['success']) for utxo in res['unspents']: - self._utxos.append(self._create_utxo(txid=utxo["txid"], vout=utxo["vout"], value=utxo["amount"], height=utxo["height"])) + self._utxos.append( + self._create_utxo(txid=utxo["txid"], + vout=utxo["vout"], + value=utxo["amount"], + height=utxo["height"], + coinbase=utxo["coinbase"], + confirmations=res["height"] - utxo["height"] + 1)) + if include_mempool: + mempool = self._test_node.getrawmempool(verbose=True) + # Sort tx by ancestor count. See BlockAssembler::SortForBlock in src/node/miner.cpp + sorted_mempool = sorted(mempool.items(), key=lambda item: (item[1]["ancestorcount"], int(item[0], 16))) + for txid, _ in sorted_mempool: + self.scan_tx(self._test_node.getrawtransaction(txid=txid, verbose=True)) def scan_tx(self, tx): """Scan the tx and adjust the internal list of owned utxos""" @@ -140,23 +159,35 @@ class MiniWallet: pass for out in tx['vout']: if out['scriptPubKey']['hex'] == self._scriptPubKey.hex(): - self._utxos.append(self._create_utxo(txid=tx["txid"], vout=out["n"], value=out["value"], height=0)) + self._utxos.append(self._create_utxo(txid=tx["txid"], vout=out["n"], value=out["value"], height=0, coinbase=False, confirmations=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) - (sighash, err) = LegacySignatureHash(CScript(self._scriptPubKey), tx, 0, SIGHASH_ALL) - assert err is None - # for exact fee calculation, create only signatures with fixed size by default (>49.89% probability): - # 65 bytes: high-R val (33 bytes) + low-S val (32 bytes) - # with the DER header/skeleton data of 6 bytes added, this leads to a target size of 71 bytes - der_sig = b'' - while not len(der_sig) == 71: - der_sig = self._priv_key.sign_ecdsa(sighash) - if not fixed_length: - break - tx.vin[0].scriptSig = CScript([der_sig + bytes(bytearray([SIGHASH_ALL]))]) - tx.rehash() + if self._mode == MiniWalletMode.RAW_P2PK: + (sighash, err) = LegacySignatureHash(CScript(self._scriptPubKey), tx, 0, SIGHASH_ALL) + assert err is None + # for exact fee calculation, create only signatures with fixed size by default (>49.89% probability): + # 65 bytes: high-R val (33 bytes) + low-S val (32 bytes) + # with the DER header/skeleton data of 6 bytes added, this leads to a target size of 71 bytes + der_sig = b'' + while not len(der_sig) == 71: + der_sig = self._priv_key.sign_ecdsa(sighash) + if not fixed_length: + break + tx.vin[0].scriptSig = CScript([der_sig + bytes(bytearray([SIGHASH_ALL]))]) + tx.rehash() + elif self._mode == MiniWalletMode.RAW_OP_TRUE: + for i in tx.vin: + i.scriptSig = CScript([OP_NOP] * 43) # pad to identical size + elif self._mode == MiniWalletMode.ADDRESS_OP_TRUE: + tx.wit.vtxinwit = [CTxInWitness()] * len(tx.vin) + for i in tx.wit.vtxinwit: + i.scriptWitness.stack = [CScript([OP_TRUE]), bytes([LEAF_VERSION_TAPSCRIPT]) + self._internal_key] + else: + assert False def generate(self, num_blocks, **kwargs): """Generate blocks with coinbase outputs to the internal address, and call rescan_utxos""" @@ -201,9 +232,13 @@ class MiniWallet: else: return self._utxos[index] - def get_utxos(self, *, mark_as_spent=True): + def get_utxos(self, *, include_immature_coinbase=False, mark_as_spent=True): """Returns the list of all utxos and optionally mark them as spent""" - utxos = deepcopy(self._utxos) + if not include_immature_coinbase: + utxo_filter = filter(lambda utxo: not utxo['coinbase'] or COINBASE_MATURITY <= utxo['confirmations'], self._utxos) + else: + utxo_filter = self._utxos + utxos = deepcopy(list(utxo_filter)) if mark_as_spent: self._utxos = [] return utxos @@ -245,6 +280,7 @@ class MiniWallet: utxos_to_spend: Optional[List[dict]] = None, num_outputs=1, amount_per_output=0, + locktime=0, sequence=0, fee_per_output=1000, target_weight=0 @@ -257,27 +293,22 @@ class MiniWallet: utxos_to_spend = utxos_to_spend or [self.get_utxo()] sequence = [sequence] * len(utxos_to_spend) if type(sequence) is int else sequence assert_equal(len(utxos_to_spend), len(sequence)) - # create simple tx template (1 input, 1 output) - tx = self.create_self_transfer( - fee_rate=0, - utxo_to_spend=utxos_to_spend[0])["tx"] - - # duplicate inputs, witnesses and outputs - tx.vin = [deepcopy(tx.vin[0]) for _ in range(len(utxos_to_spend))] - for txin, seq in zip(tx.vin, sequence): - txin.nSequence = seq - tx.wit.vtxinwit = [deepcopy(tx.wit.vtxinwit[0]) for _ in range(len(utxos_to_spend))] - tx.vout = [deepcopy(tx.vout[0]) for _ in range(num_outputs)] - - # adapt input prevouts - for i, utxo in enumerate(utxos_to_spend): - tx.vin[i] = CTxIn(COutPoint(int(utxo['txid'], 16), utxo['vout'])) - - # adapt output amounts (use fixed fee per output) + + # calculate output amount inputs_value_total = sum([int(COIN * utxo['value']) for utxo in utxos_to_spend]) outputs_value_total = inputs_value_total - fee_per_output * num_outputs - for o in tx.vout: - o.nValue = amount_per_output or (outputs_value_total // num_outputs) + amount_per_output = amount_per_output or (outputs_value_total // num_outputs) + assert amount_per_output > 0 + outputs_value_total = amount_per_output * num_outputs + fee = Decimal(inputs_value_total - outputs_value_total) / COIN + + # create tx + tx = CTransaction() + tx.vin = [CTxIn(COutPoint(int(utxo_to_spend['txid'], 16), utxo_to_spend['vout']), nSequence=seq) for utxo_to_spend, seq in zip(utxos_to_spend, sequence)] + tx.vout = [CTxOut(amount_per_output, bytearray(self._scriptPubKey)) for _ in range(num_outputs)] + tx.nLockTime = locktime + + self.sign_tx(tx) if target_weight: self._bulk_tx(tx, target_weight) @@ -289,8 +320,12 @@ class MiniWallet: vout=i, value=Decimal(tx.vout[i].nValue) / COIN, height=0, + coinbase=False, + confirmations=0, ) for i in range(len(tx.vout))], + "fee": fee, "txid": txid, + "wtxid": tx.getwtxid(), "hex": tx.serialize().hex(), "tx": tx, } @@ -300,6 +335,7 @@ class MiniWallet: utxo_to_spend = utxo_to_spend or self.get_utxo() assert fee_rate >= 0 assert fee >= 0 + # calculate fee if self._mode in (MiniWalletMode.RAW_OP_TRUE, MiniWalletMode.ADDRESS_OP_TRUE): vsize = Decimal(104) # anyone-can-spend elif self._mode == MiniWalletMode.RAW_P2PK: @@ -307,47 +343,45 @@ class MiniWallet: else: assert False send_value = utxo_to_spend["value"] - (fee or (fee_rate * vsize / 1000)) - assert send_value > 0 - - tx = CTransaction() - tx.vin = [CTxIn(COutPoint(int(utxo_to_spend['txid'], 16), utxo_to_spend['vout']), nSequence=sequence)] - tx.vout = [CTxOut(int(COIN * send_value), bytearray(self._scriptPubKey))] - tx.nLockTime = locktime - if self._mode == MiniWalletMode.RAW_P2PK: - self.sign_tx(tx) - elif self._mode == MiniWalletMode.RAW_OP_TRUE: - tx.vin[0].scriptSig = CScript([OP_NOP] * 43) # pad to identical size - elif self._mode == MiniWalletMode.ADDRESS_OP_TRUE: - tx.wit.vtxinwit = [CTxInWitness()] - tx.wit.vtxinwit[0].scriptWitness.stack = [CScript([OP_TRUE]), bytes([LEAF_VERSION_TAPSCRIPT]) + self._internal_key] - else: - assert False - assert_equal(tx.get_vsize(), vsize) + # create tx + tx = self.create_self_transfer_multi(utxos_to_spend=[utxo_to_spend], locktime=locktime, sequence=sequence, amount_per_output=int(COIN * send_value), target_weight=target_weight) + if not target_weight: + assert_equal(tx["tx"].get_vsize(), vsize) + tx["new_utxo"] = tx.pop("new_utxos")[0] - if target_weight: - self._bulk_tx(tx, target_weight) - - tx_hex = tx.serialize().hex() - new_utxo = self._create_utxo(txid=tx.rehash(), vout=0, value=send_value, height=0) - - return {"txid": new_utxo["txid"], "wtxid": tx.getwtxid(), "hex": tx_hex, "tx": tx, "new_utxo": new_utxo} + return tx def sendrawtransaction(self, *, from_node, tx_hex, maxfeerate=0, **kwargs): txid = from_node.sendrawtransaction(hexstring=tx_hex, maxfeerate=maxfeerate, **kwargs) self.scan_tx(from_node.decoderawtransaction(tx_hex)) return txid - def send_self_transfer_chain(self, *, from_node, chain_length, utxo_to_spend=None): - """Create and send a "chain" of chain_length transactions. The nth transaction in + def create_self_transfer_chain(self, *, chain_length, utxo_to_spend=None): + """ + Create a "chain" of chain_length transactions. The nth transaction in the chain is a child of the n-1th transaction and parent of the n+1th transaction. - - Returns the chaintip (nth) utxo """ chaintip_utxo = utxo_to_spend or self.get_utxo() + chain = [] + for _ in range(chain_length): - chaintip_utxo = self.send_self_transfer(utxo_to_spend=chaintip_utxo, from_node=from_node)["new_utxo"] - return chaintip_utxo + tx = self.create_self_transfer(utxo_to_spend=chaintip_utxo) + chaintip_utxo = tx["new_utxo"] + chain.append(tx) + + return chain + + def send_self_transfer_chain(self, *, from_node, **kwargs): + """Create and send a "chain" of chain_length transactions. The nth transaction in + the chain is a child of the n-1th transaction and parent of the n+1th transaction. + + Returns a list of objects for each tx (see create_self_transfer_multi). + """ + chain = self.create_self_transfer_chain(**kwargs) + for t in chain: + self.sendrawtransaction(from_node=from_node, tx_hex=t["hex"]) + return chain def getnewdestination(address_type='bech32m'): @@ -388,56 +422,3 @@ def address_to_scriptpubkey(address): # TODO: also support other address formats else: assert False - - -def make_chain(node, address, privkeys, parent_txid, parent_value, n=0, parent_locking_script=None, fee=DEFAULT_FEE): - """Build a transaction that spends parent_txid.vout[n] and produces one output with - amount = parent_value with a fee deducted. - Return tuple (CTransaction object, raw hex, nValue, scriptPubKey of the output created). - """ - inputs = [{"txid": parent_txid, "vout": n}] - my_value = parent_value - fee - outputs = {address : my_value} - rawtx = node.createrawtransaction(inputs, outputs) - prevtxs = [{ - "txid": parent_txid, - "vout": n, - "scriptPubKey": parent_locking_script, - "amount": parent_value, - }] if parent_locking_script else None - signedtx = node.signrawtransactionwithkey(hexstring=rawtx, privkeys=privkeys, prevtxs=prevtxs) - assert signedtx["complete"] - tx = tx_from_hex(signedtx["hex"]) - return (tx, signedtx["hex"], my_value, tx.vout[0].scriptPubKey.hex()) - -def create_child_with_parents(node, address, privkeys, parents_tx, values, locking_scripts, fee=DEFAULT_FEE): - """Creates a transaction that spends the first output of each parent in parents_tx.""" - num_parents = len(parents_tx) - total_value = sum(values) - inputs = [{"txid": tx.rehash(), "vout": 0} for tx in parents_tx] - outputs = {address : total_value - fee} - rawtx_child = node.createrawtransaction(inputs, outputs) - prevtxs = [] - for i in range(num_parents): - prevtxs.append({"txid": parents_tx[i].rehash(), "vout": 0, "scriptPubKey": locking_scripts[i], "amount": values[i]}) - signedtx_child = node.signrawtransactionwithkey(hexstring=rawtx_child, privkeys=privkeys, prevtxs=prevtxs) - assert signedtx_child["complete"] - return signedtx_child["hex"] - -def create_raw_chain(node, first_coin, address, privkeys, chain_length=25): - """Helper function: create a "chain" of chain_length transactions. The nth transaction in the - chain is a child of the n-1th transaction and parent of the n+1th transaction. - """ - parent_locking_script = None - txid = first_coin["txid"] - chain_hex = [] - chain_txns = [] - value = first_coin["amount"] - - for _ in range(chain_length): - (tx, txhex, value, parent_locking_script) = make_chain(node, address, privkeys, txid, value, 0, parent_locking_script) - txid = tx.rehash() - chain_hex.append(txhex) - chain_txns.append(tx) - - return (chain_hex, chain_txns) diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py index e2c13a6705..569af0ee9b 100755 --- a/test/functional/test_runner.py +++ b/test/functional/test_runner.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2014-2021 The Bitcoin Core developers +# Copyright (c) 2014-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. """Run regression test suite. @@ -27,6 +27,8 @@ import re import logging import unittest +os.environ["REQUIRE_WALLET_TYPE_SET"] = "1" + # Formatting. Default colors to empty strings. DEFAULT, BOLD, GREEN, RED = ("", ""), ("", ""), ("", ""), ("", "") try: @@ -83,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', - 'rpc_fundrawtransaction.py --legacy-wallet', - 'rpc_fundrawtransaction.py --descriptors', - 'p2p_compactblocks.py', - 'p2p_compactblocks_blocksonly.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', + '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', @@ -155,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', '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', @@ -178,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', @@ -187,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', @@ -208,8 +223,7 @@ BASE_SCRIPTS = [ 'wallet_keypool.py --legacy-wallet', 'wallet_keypool.py --descriptors', 'wallet_descriptor.py --descriptors', - 'wallet_miniscript.py', - 'feature_maxtipage.py', + 'wallet_miniscript.py --descriptors', 'p2p_nobloomfilter_messages.py', 'p2p_filter.py', 'rpc_setban.py', @@ -217,15 +231,13 @@ 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', + 'wallet_multisig_descriptor_psbt.py --descriptors', 'wallet_txn_doublespend.py --descriptors', - 'feature_backwards_compatibility.py --legacy-wallet', - 'feature_backwards_compatibility.py --descriptors', + 'wallet_backwards_compatibility.py --legacy-wallet', + 'wallet_backwards_compatibility.py --descriptors', 'wallet_txn_clone.py --mineblock', 'feature_notifications.py', 'rpc_getblockfilter.py', @@ -235,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', @@ -252,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', @@ -294,8 +299,7 @@ BASE_SCRIPTS = [ 'wallet_sendall.py --legacy-wallet', 'wallet_sendall.py --descriptors', 'wallet_create_tx.py --descriptors', - 'wallet_taproot.py', - 'wallet_inactive_hdchains.py', + 'wallet_inactive_hdchains.py --legacy-wallet', 'p2p_fingerprint.py', 'feature_uacomment.py', 'feature_init.py', @@ -307,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', @@ -315,9 +318,11 @@ BASE_SCRIPTS = [ 'mempool_unbroadcast.py', 'mempool_compatibility.py', 'mempool_accept_wtxid.py', + 'mempool_dust.py', 'rpc_deriveaddresses.py', 'rpc_deriveaddresses.py --usecli', 'p2p_ping.py', + 'p2p_tx_privacy.py', 'rpc_scanblocks.py', 'p2p_sendtxrcncl.py', 'rpc_scantxoutset.py', @@ -580,7 +585,7 @@ def run_tests(*, test_list, src_dir, build_dir, tmpdir, jobs=1, enable_coverage= combined_logs_args = [sys.executable, os.path.join(tests_dir, 'combine_logs.py'), testdir] if BOLD[0]: combined_logs_args += ['--color'] - combined_logs, _ = subprocess.Popen(combined_logs_args, universal_newlines=True, stdout=subprocess.PIPE).communicate() + combined_logs, _ = subprocess.Popen(combined_logs_args, text=True, stdout=subprocess.PIPE).communicate() print("\n".join(deque(combined_logs.splitlines(), combined_logs_len))) if failfast: @@ -665,7 +670,7 @@ class TestHandler: self.jobs.append((test, time.time(), subprocess.Popen([sys.executable, self.tests_dir + test_argv[0]] + test_argv[1:] + self.flags + portseed_arg + tmpdir_arg, - universal_newlines=True, + text=True, stdout=log_stdout, stderr=log_stderr), testdir, diff --git a/test/functional/tool_signet_miner.py b/test/functional/tool_signet_miner.py index e6fc9072ab..1ad2a579bf 100755 --- a/test/functional/tool_signet_miner.py +++ b/test/functional/tool_signet_miner.py @@ -20,6 +20,9 @@ CHALLENGE_PRIVATE_KEY = (42).to_bytes(32, 'big') class SignetMinerTest(BitcoinTestFramework): + def add_options(self, parser): + self.add_wallet_options(parser) + def set_test_params(self): self.chain = "signet" self.setup_clean_chain = True diff --git a/test/functional/tool_wallet.py b/test/functional/tool_wallet.py index 1e5ce513cb..a888f93b03 100755 --- a/test/functional/tool_wallet.py +++ b/test/functional/tool_wallet.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2018-2021 The Bitcoin Core developers +# Copyright (c) 2018-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 bitcoin-wallet.""" @@ -19,6 +19,9 @@ BUFFER_SIZE = 16 * 1024 class ToolWalletTest(BitcoinTestFramework): + def add_options(self, parser): + self.add_wallet_options(parser) + def set_test_params(self): self.num_nodes = 1 self.setup_clean_chain = True @@ -34,7 +37,7 @@ class ToolWalletTest(BitcoinTestFramework): if not self.options.descriptors and 'create' in args: default_args.append('-legacy') - return subprocess.Popen([binary] + default_args + list(args), stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True) + return subprocess.Popen([binary] + default_args + list(args), stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) def assert_raises_tool_error(self, error, *args): p = self.bitcoin_wallet_process(*args) diff --git a/test/functional/wallet_abandonconflict.py b/test/functional/wallet_abandonconflict.py index d7850b41ac..934f44588d 100755 --- a/test/functional/wallet_abandonconflict.py +++ b/test/functional/wallet_abandonconflict.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2014-2021 The Bitcoin Core developers +# Copyright (c) 2014-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 the abandontransaction RPC. @@ -21,6 +21,9 @@ from test_framework.util import ( class AbandonConflictTest(BitcoinTestFramework): + def add_options(self, parser): + self.add_wallet_options(parser) + def set_test_params(self): self.num_nodes = 2 self.extra_args = [["-minrelaytxfee=0.00001"], []] diff --git a/test/functional/wallet_address_types.py b/test/functional/wallet_address_types.py index 5b836f693f..ebeb5620e5 100755 --- a/test/functional/wallet_address_types.py +++ b/test/functional/wallet_address_types.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2017-2021 The Bitcoin Core developers +# Copyright (c) 2017-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 that the wallet can send and receive using all combinations of address types. @@ -66,6 +66,9 @@ from test_framework.util import ( ) class AddressTypeTest(BitcoinTestFramework): + def add_options(self, parser): + self.add_wallet_options(parser) + def set_test_params(self): self.num_nodes = 6 self.extra_args = [ diff --git a/test/functional/wallet_avoid_mixing_output_types.py b/test/functional/wallet_avoid_mixing_output_types.py index cad9d02808..861765f452 100755 --- a/test/functional/wallet_avoid_mixing_output_types.py +++ b/test/functional/wallet_avoid_mixing_output_types.py @@ -106,6 +106,9 @@ def generate_payment_values(n, m): class AddressInputTypeGrouping(BitcoinTestFramework): + def add_options(self, parser): + self.add_wallet_options(parser, legacy=False) + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 2 diff --git a/test/functional/wallet_avoidreuse.py b/test/functional/wallet_avoidreuse.py index f663666f57..5601d81227 100755 --- a/test/functional/wallet_avoidreuse.py +++ b/test/functional/wallet_avoidreuse.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2018-2021 The Bitcoin Core developers +# Copyright (c) 2018-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 the avoid_reuse and setwalletflag features.""" @@ -63,6 +63,8 @@ def assert_balances(node, mine, margin=0.001): assert_approx(got[k], v, margin) class AvoidReuseTest(BitcoinTestFramework): + def add_options(self, parser): + self.add_wallet_options(parser) def set_test_params(self): self.num_nodes = 2 @@ -118,6 +120,8 @@ class AvoidReuseTest(BitcoinTestFramework): assert_raises_rpc_error(-8, "Wallet flag is already set to false", self.nodes[0].setwalletflag, 'avoid_reuse', False) assert_raises_rpc_error(-8, "Wallet flag is already set to true", self.nodes[1].setwalletflag, 'avoid_reuse', True) + assert_raises_rpc_error(-8, "Unknown wallet flag: abc", self.nodes[0].setwalletflag, 'abc', True) + # Create a wallet with avoid reuse, and test that disabling it afterwards persists self.nodes[1].createwallet(wallet_name="avoid_reuse_persist", avoid_reuse=True) w = self.nodes[1].get_wallet_rpc("avoid_reuse_persist") @@ -191,7 +195,7 @@ class AvoidReuseTest(BitcoinTestFramework): # getbalances should show no used, 10 btc trusted assert_balances(self.nodes[1], mine={"used": 0, "trusted": 10}) # node 0 should not show a used entry, as it does not enable avoid_reuse - assert("used" not in self.nodes[0].getbalances()["mine"]) + assert "used" not in self.nodes[0].getbalances()["mine"] self.nodes[1].sendtoaddress(retaddr, 5) self.generate(self.nodes[0], 1) diff --git a/test/functional/wallet_backup.py b/test/functional/wallet_backup.py index 292fe3a310..4ad25d964e 100755 --- a/test/functional/wallet_backup.py +++ b/test/functional/wallet_backup.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2014-2021 The Bitcoin Core developers +# Copyright (c) 2014-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 the wallet backup features. @@ -44,6 +44,9 @@ from test_framework.util import ( class WalletBackupTest(BitcoinTestFramework): + def add_options(self, parser): + self.add_wallet_options(parser) + def set_test_params(self): self.num_nodes = 4 self.setup_clean_chain = True diff --git a/test/functional/feature_backwards_compatibility.py b/test/functional/wallet_backwards_compatibility.py index 59a12193fd..f55a3758ce 100755 --- a/test/functional/feature_backwards_compatibility.py +++ b/test/functional/wallet_backwards_compatibility.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2018-2021 The Bitcoin Core developers +# Copyright (c) 2018-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. """Backwards compatibility functional test @@ -7,10 +7,6 @@ Test various backwards compatibility scenarios. Requires previous releases binaries, see test/README.md. -v0.15.2 is not required by this test, but it is used in wallet_upgradewallet.py. -Due to a hardfork in regtest, it can't be used to sync nodes. - - Due to RPC changes introduced in various versions the below tests won't work for older versions without some patches or workarounds. @@ -32,6 +28,9 @@ from test_framework.util import ( class BackwardsCompatibilityTest(BitcoinTestFramework): + def add_options(self, parser): + self.add_wallet_options(parser) + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 10 @@ -194,18 +193,18 @@ class BackwardsCompatibilityTest(BitcoinTestFramework): assert_equal(txs[1]["txid"], tx1_id) assert_equal(txs[2]["walletconflicts"], [tx1_id]) assert_equal(txs[1]["replaced_by_txid"], tx2_id) - assert not(txs[1]["abandoned"]) + assert not txs[1]["abandoned"] assert_equal(txs[1]["confirmations"], -1) assert_equal(txs[2]["blockindex"], 1) assert txs[3]["abandoned"] assert_equal(txs[4]["walletconflicts"], [tx3_id]) assert_equal(txs[3]["replaced_by_txid"], tx4_id) - assert not(hasattr(txs[3], "blockindex")) + assert not hasattr(txs[3], "blockindex") elif wallet_name == "w2": - assert(info['private_keys_enabled'] == False) + assert info['private_keys_enabled'] == False assert info['keypoolsize'] == 0 else: - assert(info['private_keys_enabled'] == True) + assert info['private_keys_enabled'] == True assert info['keypoolsize'] == 0 else: for node in legacy_nodes: @@ -271,6 +270,7 @@ class BackwardsCompatibilityTest(BitcoinTestFramework): assert_equal(info["desc"], descsum_create(descriptor)) # Now copy that same wallet back to 0.16 to make sure no automatic upgrade breaks it + node_master.unloadwallet("u1_v16") os.remove(os.path.join(node_v16_wallets_dir, "wallets/u1_v16")) shutil.copyfile( os.path.join(node_master_wallets_dir, "u1_v16"), diff --git a/test/functional/wallet_balance.py b/test/functional/wallet_balance.py index 60da22ca26..9ed2caefb7 100755 --- a/test/functional/wallet_balance.py +++ b/test/functional/wallet_balance.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2018-2021 The Bitcoin Core developers +# Copyright (c) 2018-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 the wallet balance RPC methods.""" @@ -46,6 +46,9 @@ def create_transactions(node, address, amt, fees): return txs class WalletTest(BitcoinTestFramework): + def add_options(self, parser): + self.add_wallet_options(parser) + def set_test_params(self): self.num_nodes = 2 self.setup_clean_chain = True diff --git a/test/functional/wallet_basic.py b/test/functional/wallet_basic.py index 20c577ceb3..52022a2eee 100755 --- a/test/functional/wallet_basic.py +++ b/test/functional/wallet_basic.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2014-2021 The Bitcoin Core developers +# Copyright (c) 2014-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 the wallet.""" @@ -23,6 +23,9 @@ OUT_OF_RANGE = "Amount out of range" class WalletTest(BitcoinTestFramework): + def add_options(self, parser): + self.add_wallet_options(parser) + def set_test_params(self): self.num_nodes = 4 self.extra_args = [[ @@ -728,6 +731,45 @@ class WalletTest(BitcoinTestFramework): assert_equal(coin_b["parent_descs"][0], multi_b) self.nodes[0].unloadwallet("wo") + self.log.info("Test -spendzeroconfchange") + self.restart_node(0, ["-spendzeroconfchange=0"]) + + # create new wallet and fund it with a confirmed UTXO + self.nodes[0].createwallet(wallet_name="zeroconf", load_on_startup=True) + zeroconf_wallet = self.nodes[0].get_wallet_rpc("zeroconf") + default_wallet = self.nodes[0].get_wallet_rpc(self.default_wallet_name) + default_wallet.sendtoaddress(zeroconf_wallet.getnewaddress(), Decimal('1.0')) + self.generate(self.nodes[0], 1, sync_fun=self.no_op) + utxos = zeroconf_wallet.listunspent(minconf=0) + assert_equal(len(utxos), 1) + assert_equal(utxos[0]['confirmations'], 1) + + # spend confirmed UTXO to ourselves + zeroconf_wallet.sendall(recipients=[zeroconf_wallet.getnewaddress()]) + utxos = zeroconf_wallet.listunspent(minconf=0) + assert_equal(len(utxos), 1) + assert_equal(utxos[0]['confirmations'], 0) + # accounts for untrusted pending balance + bal = zeroconf_wallet.getbalances() + assert_equal(bal['mine']['trusted'], 0) + assert_equal(bal['mine']['untrusted_pending'], utxos[0]['amount']) + + # spending an unconfirmed UTXO sent to ourselves should fail + assert_raises_rpc_error(-6, "Insufficient funds", zeroconf_wallet.sendtoaddress, zeroconf_wallet.getnewaddress(), Decimal('0.5')) + + # check that it works again with -spendzeroconfchange set (=default) + self.restart_node(0, ["-spendzeroconfchange=1"]) + zeroconf_wallet = self.nodes[0].get_wallet_rpc("zeroconf") + utxos = zeroconf_wallet.listunspent(minconf=0) + assert_equal(len(utxos), 1) + assert_equal(utxos[0]['confirmations'], 0) + # accounts for trusted balance + bal = zeroconf_wallet.getbalances() + assert_equal(bal['mine']['trusted'], utxos[0]['amount']) + assert_equal(bal['mine']['untrusted_pending'], 0) + + zeroconf_wallet.sendtoaddress(zeroconf_wallet.getnewaddress(), Decimal('0.5')) + if __name__ == '__main__': WalletTest().main() diff --git a/test/functional/wallet_bumpfee.py b/test/functional/wallet_bumpfee.py index 2ee3e00a7b..a2ae997ecb 100755 --- a/test/functional/wallet_bumpfee.py +++ b/test/functional/wallet_bumpfee.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2016-2021 The Bitcoin Core developers +# Copyright (c) 2016-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 the bumpfee RPC. @@ -17,10 +17,6 @@ from decimal import Decimal from test_framework.blocktools import ( COINBASE_MATURITY, - add_witness_commitment, - create_block, - create_coinbase, - send_to_witness, ) from test_framework.messages import ( MAX_BIP125_RBF_SEQUENCE, @@ -46,6 +42,9 @@ TOO_HIGH = 100000 class BumpFeeTest(BitcoinTestFramework): + def add_options(self, parser): + self.add_wallet_options(parser) + def set_test_params(self): self.num_nodes = 2 self.setup_clean_chain = True @@ -89,6 +88,7 @@ class BumpFeeTest(BitcoinTestFramework): test_nonrbf_bumpfee_fails(self, peer_node, dest_address) test_notmine_bumpfee(self, rbf_node, peer_node, dest_address) test_bumpfee_with_descendant_fails(self, rbf_node, rbf_node_address, dest_address) + test_bumpfee_with_abandoned_descendant_succeeds(self, rbf_node, rbf_node_address, dest_address) test_dust_to_fee(self, rbf_node, dest_address) test_watchonly_psbt(self, peer_node, rbf_node, dest_address) test_rebumping(self, rbf_node, dest_address) @@ -200,16 +200,8 @@ def test_segwit_bumpfee_succeeds(self, rbf_node, dest_address): # Create a transaction with segwit output, then create an RBF transaction # which spends it, and make sure bumpfee can be called on it. - segwit_in = next(u for u in rbf_node.listunspent() if u["amount"] == Decimal("0.001")) - segwit_out = rbf_node.getaddressinfo(rbf_node.getnewaddress(address_type='bech32')) - segwitid = send_to_witness( - use_p2wsh=False, - node=rbf_node, - utxo=segwit_in, - pubkey=segwit_out["pubkey"], - encode_p2sh=False, - amount=Decimal("0.0009"), - sign=True) + segwit_out = rbf_node.getnewaddress(address_type='bech32') + segwitid = rbf_node.send({segwit_out: "0.0009"}, options={"change_position": 1})["txid"] rbfraw = rbf_node.createrawtransaction([{ 'txid': segwitid, @@ -295,6 +287,35 @@ def test_bumpfee_with_descendant_fails(self, rbf_node, rbf_node_address, dest_ad self.clear_mempool() +def test_bumpfee_with_abandoned_descendant_succeeds(self, rbf_node, rbf_node_address, dest_address): + self.log.info('Test that fee can be bumped when it has abandoned descendant') + # parent is send-to-self, so we don't have to check which output is change when creating the child tx + parent_id = spend_one_input(rbf_node, rbf_node_address) + # Submit child transaction with low fee + child_id = rbf_node.send(outputs={dest_address: 0.00020000}, + options={"inputs": [{"txid": parent_id, "vout": 0}], "fee_rate": 2})["txid"] + assert child_id in rbf_node.getrawmempool() + + # Restart the node with higher min relay fee so the descendant tx is no longer in mempool so that we can abandon it + self.restart_node(1, ['-minrelaytxfee=0.00005'] + self.extra_args[1]) + rbf_node.walletpassphrase(WALLET_PASSPHRASE, WALLET_PASSPHRASE_TIMEOUT) + self.connect_nodes(1, 0) + assert parent_id in rbf_node.getrawmempool() + assert child_id not in rbf_node.getrawmempool() + # Should still raise an error even if not in mempool + assert_raises_rpc_error(-8, "Transaction has descendants in the wallet", rbf_node.bumpfee, parent_id) + # Now abandon the child transaction and bump the original + rbf_node.abandontransaction(child_id) + bumped_result = rbf_node.bumpfee(parent_id, {"fee_rate": HIGH}) + assert bumped_result['txid'] in rbf_node.getrawmempool() + assert parent_id not in rbf_node.getrawmempool() + # Cleanup + self.restart_node(1, self.extra_args[1]) + rbf_node.walletpassphrase(WALLET_PASSPHRASE, WALLET_PASSPHRASE_TIMEOUT) + self.connect_nodes(1, 0) + self.clear_mempool() + + def test_small_output_with_feerate_succeeds(self, rbf_node, dest_address): self.log.info('Testing small output with feerate bump succeeds') @@ -541,10 +562,10 @@ def test_unconfirmed_not_spendable(self, rbf_node, rbf_node_address): # then invalidate the block so the rbf tx will be put back in the mempool. # This makes it possible to check whether the rbf tx outputs are # spendable before the rbf tx is confirmed. - block = submit_block_with_tx(rbf_node, rbftx) + block = self.generateblock(rbf_node, output="raw(51)", transactions=[rbftx]) # Can not abandon conflicted tx assert_raises_rpc_error(-5, 'Transaction not eligible for abandonment', lambda: rbf_node.abandontransaction(txid=bumpid)) - rbf_node.invalidateblock(block.hash) + rbf_node.invalidateblock(block["hash"]) # Call abandon to make sure the wallet doesn't attempt to resubmit # the bump tx and hope the wallet does not rebroadcast before we call. rbf_node.abandontransaction(bumpid) @@ -566,7 +587,7 @@ def test_unconfirmed_not_spendable(self, rbf_node, rbf_node_address): def test_bumpfee_metadata(self, rbf_node, dest_address): self.log.info('Test that bumped txn metadata persists to new txn record') - assert(rbf_node.getbalance() < 49) + assert rbf_node.getbalance() < 49 self.generatetoaddress(rbf_node, 101, rbf_node.getnewaddress()) rbfid = rbf_node.sendtoaddress(dest_address, 49, "comment value", "to value") bumped_tx = rbf_node.bumpfee(rbfid) @@ -619,17 +640,6 @@ def spend_one_input(node, dest_address, change_size=Decimal("0.00049000")): return txid -def submit_block_with_tx(node, tx): - tip = node.getbestblockhash() - height = node.getblockcount() + 1 - block_time = node.getblockheader(tip)["mediantime"] + 1 - block = create_block(int(tip, 16), create_coinbase(height), block_time, txlist=[tx]) - add_witness_commitment(block) - block.solve() - node.submitblock(block.serialize().hex()) - return block - - def test_no_more_inputs_fails(self, rbf_node, dest_address): self.log.info('Test that bumpfee fails when there are no available confirmed outputs') # feerate rbf requires confirmed outputs when change output doesn't exist or is insufficient diff --git a/test/functional/wallet_coinbase_category.py b/test/functional/wallet_coinbase_category.py index c2a8e612cf..c2cb0bf3b0 100755 --- a/test/functional/wallet_coinbase_category.py +++ b/test/functional/wallet_coinbase_category.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2014-2021 The Bitcoin Core developers +# Copyright (c) 2014-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 coinbase transactions return the correct categories. @@ -13,6 +13,9 @@ from test_framework.util import ( ) class CoinbaseCategoryTest(BitcoinTestFramework): + def add_options(self, parser): + self.add_wallet_options(parser) + def set_test_params(self): self.num_nodes = 1 self.setup_clean_chain = True diff --git a/test/functional/wallet_create_tx.py b/test/functional/wallet_create_tx.py index a213a261ef..2d9bb38fcc 100755 --- a/test/functional/wallet_create_tx.py +++ b/test/functional/wallet_create_tx.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2018-2021 The Bitcoin Core developers +# Copyright (c) 2018-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. @@ -14,6 +14,9 @@ from test_framework.blocktools import ( class CreateTxWalletTest(BitcoinTestFramework): + def add_options(self, parser): + self.add_wallet_options(parser) + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 1 diff --git a/test/functional/wallet_createwallet.py b/test/functional/wallet_createwallet.py index 12480d4d1e..22c491441b 100755 --- a/test/functional/wallet_createwallet.py +++ b/test/functional/wallet_createwallet.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2018-2021 The Bitcoin Core developers +# Copyright (c) 2018-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 createwallet arguments. @@ -16,6 +16,9 @@ from test_framework.util import ( from test_framework.wallet_util import bytes_to_wif, generate_wif_key class CreateWalletTest(BitcoinTestFramework): + def add_options(self, parser): + self.add_wallet_options(parser) + def set_test_params(self): self.num_nodes = 1 diff --git a/test/functional/wallet_crosschain.py b/test/functional/wallet_crosschain.py index c0047542ed..6f93ad4e3b 100755 --- a/test/functional/wallet_crosschain.py +++ b/test/functional/wallet_crosschain.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2020 The Bitcoin Core developers +# Copyright (c) 2020-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. @@ -9,6 +9,9 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.util import assert_raises_rpc_error class WalletCrossChain(BitcoinTestFramework): + def add_options(self, parser): + self.add_wallet_options(parser) + def set_test_params(self): self.num_nodes = 2 self.setup_clean_chain = True @@ -33,20 +36,28 @@ class WalletCrossChain(BitcoinTestFramework): self.log.info("Creating wallets") node0_wallet = os.path.join(self.nodes[0].datadir, 'node0_wallet') + node0_wallet_backup = os.path.join(self.nodes[0].datadir, 'node0_wallet.bak') self.nodes[0].createwallet(node0_wallet) + self.nodes[0].backupwallet(node0_wallet_backup) self.nodes[0].unloadwallet(node0_wallet) node1_wallet = os.path.join(self.nodes[1].datadir, 'node1_wallet') + node1_wallet_backup = os.path.join(self.nodes[0].datadir, 'node1_wallet.bak') self.nodes[1].createwallet(node1_wallet) + self.nodes[1].backupwallet(node1_wallet_backup) self.nodes[1].unloadwallet(node1_wallet) - self.log.info("Loading wallets into nodes with a different genesis blocks") + self.log.info("Loading/restoring wallets into nodes with a different genesis block") if self.options.descriptors: assert_raises_rpc_error(-18, 'Wallet file verification failed.', self.nodes[0].loadwallet, node1_wallet) assert_raises_rpc_error(-18, 'Wallet file verification failed.', self.nodes[1].loadwallet, node0_wallet) + assert_raises_rpc_error(-18, 'Wallet file verification failed.', self.nodes[0].restorewallet, 'w', node1_wallet_backup) + assert_raises_rpc_error(-18, 'Wallet file verification failed.', self.nodes[1].restorewallet, 'w', node0_wallet_backup) else: assert_raises_rpc_error(-4, 'Wallet files should not be reused across chains.', self.nodes[0].loadwallet, node1_wallet) assert_raises_rpc_error(-4, 'Wallet files should not be reused across chains.', self.nodes[1].loadwallet, node0_wallet) + assert_raises_rpc_error(-4, 'Wallet files should not be reused across chains.', self.nodes[0].restorewallet, 'w', node1_wallet_backup) + assert_raises_rpc_error(-4, 'Wallet files should not be reused across chains.', self.nodes[1].restorewallet, 'w', node0_wallet_backup) if not self.options.descriptors: self.log.info("Override cross-chain wallet load protection") diff --git a/test/functional/wallet_descriptor.py b/test/functional/wallet_descriptor.py index e7cfa56c46..b0e93df36a 100755 --- a/test/functional/wallet_descriptor.py +++ b/test/functional/wallet_descriptor.py @@ -1,8 +1,14 @@ #!/usr/bin/env python3 -# Copyright (c) 2019-2021 The Bitcoin Core developers +# Copyright (c) 2019-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 descriptor wallet function.""" +import os + +try: + import sqlite3 +except ImportError: + pass from test_framework.blocktools import COINBASE_MATURITY from test_framework.test_framework import BitcoinTestFramework @@ -13,6 +19,9 @@ from test_framework.util import ( class WalletDescriptorTest(BitcoinTestFramework): + def add_options(self, parser): + self.add_wallet_options(parser, legacy=False) + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 1 @@ -22,6 +31,7 @@ class WalletDescriptorTest(BitcoinTestFramework): def skip_test_if_missing_module(self): self.skip_if_no_wallet() self.skip_if_no_sqlite() + self.skip_if_no_py_sqlite3() def run_test(self): if self.is_bdb_compiled(): @@ -104,7 +114,7 @@ class WalletDescriptorTest(BitcoinTestFramework): # Make sure things are disabled self.log.info("Test disabled RPCs") assert_raises_rpc_error(-4, "Only legacy wallets are supported by this command", recv_wrpc.rpc.importprivkey, "cVpF924EspNh8KjYsfhgY96mmxvT6DgdWiTYMtMjuM74hJaU5psW") - assert_raises_rpc_error(-4, "Only legacy wallets are supported by this command", recv_wrpc.rpc.importpubkey, send_wrpc.getaddressinfo(send_wrpc.getnewaddress())) + assert_raises_rpc_error(-4, "Only legacy wallets are supported by this command", recv_wrpc.rpc.importpubkey, send_wrpc.getaddressinfo(send_wrpc.getnewaddress())["pubkey"]) assert_raises_rpc_error(-4, "Only legacy wallets are supported by this command", recv_wrpc.rpc.importaddress, recv_wrpc.getnewaddress()) assert_raises_rpc_error(-4, "Only legacy wallets are supported by this command", recv_wrpc.rpc.importmulti, []) assert_raises_rpc_error(-4, "Only legacy wallets are supported by this command", recv_wrpc.rpc.addmultisigaddress, 1, [recv_wrpc.getnewaddress()]) @@ -221,5 +231,15 @@ class WalletDescriptorTest(BitcoinTestFramework): imp_addr = imp_rpc.getnewaddress(address_type=addr_type) assert_equal(exp_addr, imp_addr) + self.log.info("Test that loading descriptor wallet containing legacy key types throws error") + self.nodes[0].createwallet(wallet_name="crashme", descriptors=True) + self.nodes[0].unloadwallet("crashme") + wallet_db = os.path.join(self.nodes[0].datadir, self.chain, "wallets", "crashme", self.wallet_data_filename) + with sqlite3.connect(wallet_db) as conn: + # add "cscript" entry: key type is uint160 (20 bytes), value type is CScript (zero-length here) + conn.execute('INSERT INTO main VALUES(?, ?)', (b'\x07cscript' + b'\x00'*20, b'\x00')) + assert_raises_rpc_error(-4, "Unexpected legacy entry in descriptor wallet found.", self.nodes[0].loadwallet, "crashme") + + if __name__ == '__main__': WalletDescriptorTest().main () diff --git a/test/functional/wallet_disable.py b/test/functional/wallet_disable.py index 74cddf2738..9c73f7dead 100755 --- a/test/functional/wallet_disable.py +++ b/test/functional/wallet_disable.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2021 The Bitcoin Core developers +# Copyright (c) 2015-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 a node with the -disablewallet option. diff --git a/test/functional/wallet_dump.py b/test/functional/wallet_dump.py index 9f0d666270..cf20ff1239 100755 --- a/test/functional/wallet_dump.py +++ b/test/functional/wallet_dump.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2016-2021 The Bitcoin Core developers +# Copyright (c) 2016-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 the dumpwallet RPC.""" @@ -93,6 +93,9 @@ def read_dump(file_name, addrs, script_addrs, hd_master_addr_old): class WalletDumpTest(BitcoinTestFramework): + def add_options(self, parser): + self.add_wallet_options(parser, descriptors=False) + def set_test_params(self): self.num_nodes = 1 self.extra_args = [["-keypool=90", "-addresstype=legacy"]] diff --git a/test/functional/wallet_encryption.py b/test/functional/wallet_encryption.py index 37c1c4bff3..885c52cf2e 100755 --- a/test/functional/wallet_encryption.py +++ b/test/functional/wallet_encryption.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2016-2020 The Bitcoin Core developers +# Copyright (c) 2016-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 encryption""" @@ -14,6 +14,9 @@ from test_framework.util import ( class WalletEncryptionTest(BitcoinTestFramework): + def add_options(self, parser): + self.add_wallet_options(parser) + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 1 diff --git a/test/functional/wallet_fallbackfee.py b/test/functional/wallet_fallbackfee.py index acd92097ff..f0740b72fd 100755 --- a/test/functional/wallet_fallbackfee.py +++ b/test/functional/wallet_fallbackfee.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2017-2021 The Bitcoin Core developers +# Copyright (c) 2017-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 replace-by-fee capabilities in conjunction with the fallbackfee.""" @@ -9,6 +9,9 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.util import assert_raises_rpc_error class WalletRBFTest(BitcoinTestFramework): + def add_options(self, parser): + self.add_wallet_options(parser) + def set_test_params(self): self.num_nodes = 1 self.setup_clean_chain = True diff --git a/test/functional/wallet_fast_rescan.py b/test/functional/wallet_fast_rescan.py index 3b8ae8eb92..1ab24f1a96 100755 --- a/test/functional/wallet_fast_rescan.py +++ b/test/functional/wallet_fast_rescan.py @@ -21,6 +21,9 @@ NUM_BLOCKS = 6 # number of blocks to mine class WalletFastRescanTest(BitcoinTestFramework): + def add_options(self, parser): + self.add_wallet_options(parser, legacy=False) + def set_test_params(self): self.num_nodes = 1 self.extra_args = [[f'-keypool={KEYPOOL_SIZE}', '-blockfilterindex=1']] @@ -37,7 +40,6 @@ class WalletFastRescanTest(BitcoinTestFramework): def run_test(self): node = self.nodes[0] wallet = MiniWallet(node) - wallet.rescan_utxos() self.log.info("Create descriptor wallet with backup") WALLET_BACKUP_FILENAME = os.path.join(node.datadir, 'wallet.bak') diff --git a/test/functional/rpc_fundrawtransaction.py b/test/functional/wallet_fundrawtransaction.py index 1152995ac9..29ddb77b41 100755 --- a/test/functional/rpc_fundrawtransaction.py +++ b/test/functional/wallet_fundrawtransaction.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2014-2021 The Bitcoin Core developers +# Copyright (c) 2014-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 the fundrawtransaction RPC.""" @@ -27,6 +27,8 @@ from test_framework.util import ( ) from test_framework.wallet_util import bytes_to_wif +ERR_NOT_ENOUGH_PRESET_INPUTS = "The preselected coins total amount does not cover the transaction target. " \ + "Please allow other inputs to be automatically selected or include more coins manually" def get_unspent(listunspent, amount): for utx in listunspent: @@ -35,6 +37,9 @@ def get_unspent(listunspent, amount): raise AssertionError('Could not find unspent with amount={}'.format(amount)) class RawTransactionsTest(BitcoinTestFramework): + def add_options(self, parser): + self.add_wallet_options(parser) + def set_test_params(self): self.num_nodes = 4 self.setup_clean_chain = True @@ -107,6 +112,7 @@ class RawTransactionsTest(BitcoinTestFramework): self.generate(self.nodes[0], 121) self.test_add_inputs_default_value() + self.test_preset_inputs_selection() self.test_weight_calculation() self.test_change_position() self.test_simple() @@ -142,6 +148,7 @@ class RawTransactionsTest(BitcoinTestFramework): self.test_external_inputs() self.test_22670() self.test_feerate_rounding() + self.test_input_confs_control() def test_change_position(self): """Ensure setting changePosition in fundraw with an exact match is handled properly.""" @@ -324,7 +331,7 @@ class RawTransactionsTest(BitcoinTestFramework): assert_equal("00", dec_tx['vin'][0]['scriptSig']['hex']) # Should fail without add_inputs: - assert_raises_rpc_error(-4, "Insufficient funds", self.nodes[2].fundrawtransaction, rawtx, {"add_inputs": False}) + assert_raises_rpc_error(-4, ERR_NOT_ENOUGH_PRESET_INPUTS, self.nodes[2].fundrawtransaction, rawtx, {"add_inputs": False}) # add_inputs is enabled by default rawtxfund = self.nodes[2].fundrawtransaction(rawtx) @@ -356,7 +363,7 @@ class RawTransactionsTest(BitcoinTestFramework): assert_equal(utx['txid'], dec_tx['vin'][0]['txid']) # Should fail without add_inputs: - assert_raises_rpc_error(-4, "Insufficient funds", self.nodes[2].fundrawtransaction, rawtx, {"add_inputs": False}) + assert_raises_rpc_error(-4, ERR_NOT_ENOUGH_PRESET_INPUTS, self.nodes[2].fundrawtransaction, rawtx, {"add_inputs": False}) rawtxfund = self.nodes[2].fundrawtransaction(rawtx, {"add_inputs": True}) dec_tx = self.nodes[2].decoderawtransaction(rawtxfund['hex']) @@ -390,7 +397,7 @@ class RawTransactionsTest(BitcoinTestFramework): assert_equal(utx['txid'], dec_tx['vin'][0]['txid']) # Should fail without add_inputs: - assert_raises_rpc_error(-4, "Insufficient funds", self.nodes[2].fundrawtransaction, rawtx, {"add_inputs": False}) + assert_raises_rpc_error(-4, ERR_NOT_ENOUGH_PRESET_INPUTS, self.nodes[2].fundrawtransaction, rawtx, {"add_inputs": False}) rawtxfund = self.nodes[2].fundrawtransaction(rawtx, {"add_inputs": True}) dec_tx = self.nodes[2].decoderawtransaction(rawtxfund['hex']) @@ -979,11 +986,15 @@ class RawTransactionsTest(BitcoinTestFramework): # are selected, the transaction will end up being too large, so it # shouldn't use BnB and instead fall back to Knapsack but that behavior # is not implemented yet. For now we just check that we get an error. + # First, force the wallet to bulk-generate the addresses we'll need. + recipient.keypoolrefill(1500) for _ in range(1500): outputs[recipient.getnewaddress()] = 0.1 wallet.sendmany("", outputs) self.generate(self.nodes[0], 10) - assert_raises_rpc_error(-4, "Transaction too large", recipient.fundrawtransaction, rawtx) + assert_raises_rpc_error(-4, "The inputs size exceeds the maximum weight. " + "Please try sending a smaller amount or manually consolidating your wallet's UTXOs", + recipient.fundrawtransaction, rawtx) self.nodes[0].unloadwallet("large") def test_external_inputs(self): @@ -1124,7 +1135,7 @@ class RawTransactionsTest(BitcoinTestFramework): } ] } - assert_raises_rpc_error(-4, "Insufficient funds", wallet.send, outputs=[{addr1: 8}], options=options) + assert_raises_rpc_error(-4, ERR_NOT_ENOUGH_PRESET_INPUTS, wallet.send, outputs=[{addr1: 8}], options=options) # Case (3), Explicit add_inputs=true and preset inputs (with preset inputs not-covering the target amount) options["add_inputs"] = True @@ -1152,7 +1163,7 @@ class RawTransactionsTest(BitcoinTestFramework): # 6. Explicit add_inputs=false, no preset inputs: options = {"add_inputs": False} - assert_raises_rpc_error(-4, "Insufficient funds", wallet.send, outputs=[{addr1: 3}], options=options) + assert_raises_rpc_error(-4, ERR_NOT_ENOUGH_PRESET_INPUTS, wallet.send, outputs=[{addr1: 3}], options=options) ################################################ @@ -1169,7 +1180,7 @@ class RawTransactionsTest(BitcoinTestFramework): "vout": 1 # change position was hardcoded to index 0 }] outputs = {self.nodes[1].getnewaddress(): 8} - assert_raises_rpc_error(-4, "Insufficient funds", wallet.walletcreatefundedpsbt, inputs=inputs, outputs=outputs) + assert_raises_rpc_error(-4, ERR_NOT_ENOUGH_PRESET_INPUTS, wallet.walletcreatefundedpsbt, inputs=inputs, outputs=outputs) # Case (3), Explicit add_inputs=true and preset inputs (with preset inputs not-covering the target amount) options["add_inputs"] = True @@ -1196,10 +1207,54 @@ class RawTransactionsTest(BitcoinTestFramework): # Case (6). Explicit add_inputs=false, no preset inputs: options = {"add_inputs": False} - assert_raises_rpc_error(-4, "Insufficient funds", wallet.walletcreatefundedpsbt, inputs=[], outputs=outputs, options=options) + assert_raises_rpc_error(-4, ERR_NOT_ENOUGH_PRESET_INPUTS, wallet.walletcreatefundedpsbt, inputs=[], outputs=outputs, options=options) self.nodes[2].unloadwallet("test_preset_inputs") + def test_preset_inputs_selection(self): + self.log.info('Test wallet preset inputs are not double-counted or reused in coin selection') + + # Create and fund the wallet with 4 UTXO of 5 BTC each (20 BTC total) + self.nodes[2].createwallet("test_preset_inputs_selection") + wallet = self.nodes[2].get_wallet_rpc("test_preset_inputs_selection") + outputs = {} + for _ in range(4): + outputs[wallet.getnewaddress(address_type="bech32")] = 5 + self.nodes[0].sendmany("", outputs) + self.generate(self.nodes[0], 1) + + # Select the preset inputs + coins = wallet.listunspent() + preset_inputs = [coins[0], coins[1], coins[2]] + + # Now let's create the tx creation options + options = { + "inputs": preset_inputs, + "add_inputs": True, # automatically add coins from the wallet to fulfill the target + "subtract_fee_from_outputs": [0], # deduct fee from first output + "add_to_wallet": False + } + + # Attempt to send 29 BTC from a wallet that only has 20 BTC. The wallet should exclude + # the preset inputs from the pool of available coins, realize that there is not enough + # money to fund the 29 BTC payment, and fail with "Insufficient funds". + # + # Even with SFFO, the wallet can only afford to send 20 BTC. + # If the wallet does not properly exclude preset inputs from the pool of available coins + # prior to coin selection, it may create a transaction that does not fund the full payment + # amount or, through SFFO, incorrectly reduce the recipient's amount by the difference + # between the original target and the wrongly counted inputs (in this case 9 BTC) + # so that the recipient's amount is no longer equal to the user's selected target of 29 BTC. + + # First case, use 'subtract_fee_from_outputs = true' + assert_raises_rpc_error(-4, "Insufficient funds", wallet.send, outputs=[{wallet.getnewaddress(address_type="bech32"): 29}], options=options) + + # Second case, don't use 'subtract_fee_from_outputs' + del options["subtract_fee_from_outputs"] + assert_raises_rpc_error(-4, "Insufficient funds", wallet.send, outputs=[{wallet.getnewaddress(address_type="bech32"): 29}], options=options) + + self.nodes[2].unloadwallet("test_preset_inputs_selection") + def test_weight_calculation(self): self.log.info("Test weight calculation with external inputs") @@ -1349,6 +1404,66 @@ class RawTransactionsTest(BitcoinTestFramework): rawtx = w.createrawtransaction(inputs=[], outputs=[{self.nodes[0].getnewaddress(address_type="bech32"): 1 - 0.00000202}]) assert_raises_rpc_error(-4, "Insufficient funds", w.fundrawtransaction, rawtx, {"fee_rate": 1.85}) + def test_input_confs_control(self): + self.nodes[0].createwallet("minconf") + wallet = self.nodes[0].get_wallet_rpc("minconf") + + # Fund the wallet with different chain heights + for _ in range(2): + self.nodes[2].sendmany("", {wallet.getnewaddress():1, wallet.getnewaddress():1}) + self.generate(self.nodes[2], 1) + + unconfirmed_txid = wallet.sendtoaddress(wallet.getnewaddress(), 0.5) + + self.log.info("Crafting TX using an unconfirmed input") + target_address = self.nodes[2].getnewaddress() + raw_tx1 = wallet.createrawtransaction([], {target_address: 0.1}, 0, True) + funded_tx1 = wallet.fundrawtransaction(raw_tx1, {'fee_rate': 1, 'maxconf': 0})['hex'] + + # Make sure we only had the one input + tx1_inputs = self.nodes[0].decoderawtransaction(funded_tx1)['vin'] + assert_equal(len(tx1_inputs), 1) + + utxo1 = tx1_inputs[0] + assert unconfirmed_txid == utxo1['txid'] + + final_tx1 = wallet.signrawtransactionwithwallet(funded_tx1)['hex'] + txid1 = self.nodes[0].sendrawtransaction(final_tx1) + + mempool = self.nodes[0].getrawmempool() + assert txid1 in mempool + + self.log.info("Fail to craft a new TX with minconf above highest one") + # Create a replacement tx to 'final_tx1' that has 1 BTC target instead of 0.1. + raw_tx2 = wallet.createrawtransaction([{'txid': utxo1['txid'], 'vout': utxo1['vout']}], {target_address: 1}) + assert_raises_rpc_error(-4, "Insufficient funds", wallet.fundrawtransaction, raw_tx2, {'add_inputs': True, 'minconf': 3, 'fee_rate': 10}) + + self.log.info("Fail to broadcast a new TX with maxconf 0 due to BIP125 rules to verify it actually chose unconfirmed outputs") + # Now fund 'raw_tx2' to fulfill the total target (1 BTC) by using all the wallet unconfirmed outputs. + # As it was created with the first unconfirmed output, 'raw_tx2' only has 0.1 BTC covered (need to fund 0.9 BTC more). + # So, the selection process, to cover the amount, will pick up the 'final_tx1' output as well, which is an output of the tx that this + # new tx is replacing!. So, once we send it to the mempool, it will return a "bad-txns-spends-conflicting-tx" + # because the input will no longer exist once the first tx gets replaced by this new one). + funded_invalid = wallet.fundrawtransaction(raw_tx2, {'add_inputs': True, 'maxconf': 0, 'fee_rate': 10})['hex'] + final_invalid = wallet.signrawtransactionwithwallet(funded_invalid)['hex'] + assert_raises_rpc_error(-26, "bad-txns-spends-conflicting-tx", self.nodes[0].sendrawtransaction, final_invalid) + + self.log.info("Craft a replacement adding inputs with highest depth possible") + funded_tx2 = wallet.fundrawtransaction(raw_tx2, {'add_inputs': True, 'minconf': 2, 'fee_rate': 10})['hex'] + tx2_inputs = self.nodes[0].decoderawtransaction(funded_tx2)['vin'] + assert_greater_than_or_equal(len(tx2_inputs), 2) + for vin in tx2_inputs: + if vin['txid'] != unconfirmed_txid: + assert_greater_than_or_equal(self.nodes[0].gettxout(vin['txid'], vin['vout'])['confirmations'], 2) + + final_tx2 = wallet.signrawtransactionwithwallet(funded_tx2)['hex'] + txid2 = self.nodes[0].sendrawtransaction(final_tx2) + + mempool = self.nodes[0].getrawmempool() + assert txid1 not in mempool + assert txid2 in mempool + + wallet.unloadwallet() if __name__ == '__main__': RawTransactionsTest().main() diff --git a/test/functional/wallet_groups.py b/test/functional/wallet_groups.py index e5e4cf03bf..83c1826a41 100755 --- a/test/functional/wallet_groups.py +++ b/test/functional/wallet_groups.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2018-2021 The Bitcoin Core developers +# Copyright (c) 2018-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 group functionality.""" @@ -16,6 +16,9 @@ from test_framework.util import ( class WalletGroupTest(BitcoinTestFramework): + def add_options(self, parser): + self.add_wallet_options(parser) + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 5 diff --git a/test/functional/wallet_hd.py b/test/functional/wallet_hd.py index 220c856498..0f79df6e5d 100755 --- a/test/functional/wallet_hd.py +++ b/test/functional/wallet_hd.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2016-2021 The Bitcoin Core developers +# Copyright (c) 2016-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 Hierarchical Deterministic wallet function.""" @@ -16,6 +16,9 @@ from test_framework.util import ( class WalletHDTest(BitcoinTestFramework): + def add_options(self, parser): + self.add_wallet_options(parser) + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 2 diff --git a/test/functional/wallet_implicitsegwit.py b/test/functional/wallet_implicitsegwit.py index a8583e2879..baa9bafb00 100755 --- a/test/functional/wallet_implicitsegwit.py +++ b/test/functional/wallet_implicitsegwit.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2019 The Bitcoin Core developers +# Copyright (c) 2019-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 the wallet implicit segwit feature.""" @@ -36,9 +36,12 @@ def check_implicit_transactions(implicit_keys, implicit_node): pubkey = implicit_keys[a] for b in address_types: b_address = key_to_address(pubkey, b) - assert(('receive', b_address) in tuple((tx['category'], tx['address']) for tx in txs)) + assert ('receive', b_address) in tuple((tx['category'], tx['address']) for tx in txs) class ImplicitSegwitTest(BitcoinTestFramework): + def add_options(self, parser): + self.add_wallet_options(parser, descriptors=False) + def set_test_params(self): self.num_nodes = 2 self.supports_cli = False diff --git a/test/functional/wallet_import_rescan.py b/test/functional/wallet_import_rescan.py index 085ad51c79..211e939a39 100755 --- a/test/functional/wallet_import_rescan.py +++ b/test/functional/wallet_import_rescan.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2014-2021 The Bitcoin Core developers +# Copyright (c) 2014-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 RPCs. @@ -147,6 +147,9 @@ def get_rand_amount(): class ImportRescanTest(BitcoinTestFramework): + def add_options(self, parser): + self.add_wallet_options(parser, descriptors=False) + def set_test_params(self): self.num_nodes = 2 + len(IMPORT_NODES) self.supports_cli = False @@ -176,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, @@ -185,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(), []) @@ -214,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_import_with_label.py b/test/functional/wallet_import_with_label.py index 6a9d2e8290..0a1fc31ebc 100755 --- a/test/functional/wallet_import_with_label.py +++ b/test/functional/wallet_import_with_label.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2018-2020 The Bitcoin Core developers +# Copyright (c) 2018-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 the behavior of RPC importprivkey on set and unset labels of @@ -15,6 +15,9 @@ from test_framework.wallet_util import test_address class ImportWithLabel(BitcoinTestFramework): + def add_options(self, parser): + self.add_wallet_options(parser, descriptors=False) + def set_test_params(self): self.num_nodes = 2 self.setup_clean_chain = True diff --git a/test/functional/wallet_importdescriptors.py b/test/functional/wallet_importdescriptors.py index 9744009af8..ca0209b61d 100755 --- a/test/functional/wallet_importdescriptors.py +++ b/test/functional/wallet_importdescriptors.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2019-2021 The Bitcoin Core developers +# Copyright (c) 2019-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 the importdescriptors RPC. @@ -15,7 +15,6 @@ variants. - `test_address()` is called to call getaddressinfo for an address on node1 and test the values returned.""" -from test_framework.address import key_to_p2pkh from test_framework.blocktools import COINBASE_MATURITY from test_framework.test_framework import BitcoinTestFramework from test_framework.descriptors import descsum_create @@ -30,6 +29,9 @@ from test_framework.wallet_util import ( ) class ImportDescriptorsTest(BitcoinTestFramework): + def add_options(self, parser): + self.add_wallet_options(parser, legacy=False) + def set_test_params(self): self.num_nodes = 2 self.extra_args = [["-addresstype=legacy"], @@ -117,12 +119,11 @@ class ImportDescriptorsTest(BitcoinTestFramework): self.log.info("Internal addresses should be detected as such") key = get_generate_key() - addr = key_to_p2pkh(key.pubkey) self.test_importdesc({"desc": descsum_create("pkh(" + key.pubkey + ")"), "timestamp": "now", "internal": True}, success=True) - info = w1.getaddressinfo(addr) + info = w1.getaddressinfo(key.p2pkh_addr) assert_equal(info["ismine"], True) assert_equal(info["ischange"], True) @@ -484,8 +485,8 @@ class ImportDescriptorsTest(BitcoinTestFramework): assert_equal(addr, 'bcrt1qp8s25ckjl7gr6x2q3dx3tn2pytwp05upkjztk6ey857tt50r5aeqn6mvr9') # Derived at m/84'/0'/0'/1 change_addr = wmulti_pub.getrawchangeaddress('bech32') assert_equal(change_addr, 'bcrt1qzxl0qz2t88kljdnkzg4n4gapr6kte26390gttrg79x66nt4p04fssj53nl') - assert(send_txid in self.nodes[0].getrawmempool(True)) - assert(send_txid in (x['txid'] for x in wmulti_pub.listunspent(0))) + assert send_txid in self.nodes[0].getrawmempool(True) + assert send_txid in (x['txid'] for x in wmulti_pub.listunspent(0)) assert_equal(wmulti_pub.getwalletinfo()['keypoolsize'], 999) # generate some utxos for next tests diff --git a/test/functional/wallet_importmulti.py b/test/functional/wallet_importmulti.py index 62a1a3341d..31013f6323 100755 --- a/test/functional/wallet_importmulti.py +++ b/test/functional/wallet_importmulti.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2014-2021 The Bitcoin Core developers +# Copyright (c) 2014-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 the importmulti RPC. @@ -35,6 +35,9 @@ from test_framework.wallet_util import ( class ImportMultiTest(BitcoinTestFramework): + def add_options(self, parser): + self.add_wallet_options(parser, descriptors=False) + def set_test_params(self): self.num_nodes = 2 self.extra_args = [["-addresstype=legacy"], ["-addresstype=legacy"]] diff --git a/test/functional/wallet_importprunedfunds.py b/test/functional/wallet_importprunedfunds.py index 2a4d0981c7..77b407579f 100755 --- a/test/functional/wallet_importprunedfunds.py +++ b/test/functional/wallet_importprunedfunds.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2014-2021 The Bitcoin Core developers +# Copyright (c) 2014-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 the importprunedfunds and removeprunedfunds RPCs.""" @@ -21,6 +21,9 @@ from test_framework.wallet_util import bytes_to_wif class ImportPrunedFundsTest(BitcoinTestFramework): + def add_options(self, parser): + self.add_wallet_options(parser) + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 2 diff --git a/test/functional/wallet_inactive_hdchains.py b/test/functional/wallet_inactive_hdchains.py index e1dad00876..c0b3fea1c0 100755 --- a/test/functional/wallet_inactive_hdchains.py +++ b/test/functional/wallet_inactive_hdchains.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2021 The Bitcoin Core developers +# Copyright (c) 2021-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. """ @@ -17,6 +17,9 @@ from test_framework.wallet_util import ( class InactiveHDChainsTest(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 diff --git a/test/functional/wallet_keypool.py b/test/functional/wallet_keypool.py index 54c47511a9..bd97851153 100755 --- a/test/functional/wallet_keypool.py +++ b/test/functional/wallet_keypool.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2014-2021 The Bitcoin Core developers +# Copyright (c) 2014-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 the wallet keypool and interaction with wallet encryption/locking.""" @@ -11,6 +11,9 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.util import assert_equal, assert_raises_rpc_error class KeyPoolTest(BitcoinTestFramework): + def add_options(self, parser): + self.add_wallet_options(parser) + def set_test_params(self): self.num_nodes = 1 @@ -201,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_keypool_topup.py b/test/functional/wallet_keypool_topup.py index 4c965b7160..18c3ae1f7c 100755 --- a/test/functional/wallet_keypool_topup.py +++ b/test/functional/wallet_keypool_topup.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2017-2021 The Bitcoin Core developers +# Copyright (c) 2017-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 HD Wallet keypool restore function. @@ -21,6 +21,9 @@ from test_framework.util import ( class KeypoolRestoreTest(BitcoinTestFramework): + def add_options(self, parser): + self.add_wallet_options(parser) + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 4 diff --git a/test/functional/wallet_labels.py b/test/functional/wallet_labels.py index c29b02e661..a39700f73a 100755 --- a/test/functional/wallet_labels.py +++ b/test/functional/wallet_labels.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2016-2021 The Bitcoin Core developers +# Copyright (c) 2016-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 label RPCs. @@ -18,13 +18,54 @@ from test_framework.wallet_util import test_address class WalletLabelsTest(BitcoinTestFramework): + def add_options(self, parser): + self.add_wallet_options(parser) + def set_test_params(self): self.setup_clean_chain = True - self.num_nodes = 1 + self.num_nodes = 2 def skip_test_if_missing_module(self): self.skip_if_no_wallet() + def invalid_label_name_test(self): + node = self.nodes[0] + address = node.getnewaddress() + pubkey = node.getaddressinfo(address)['pubkey'] + rpc_calls = [ + [node.getnewaddress], + [node.setlabel, address], + [node.getaddressesbylabel], + [node.importpubkey, pubkey], + [node.addmultisigaddress, 1, [pubkey]], + [node.getreceivedbylabel], + [node.listsinceblock, node.getblockhash(0), 1, False, True, False], + ] + if self.options.descriptors: + response = node.importdescriptors([{ + 'desc': f'pkh({pubkey})', + 'label': '*', + 'timestamp': 'now', + }]) + else: + rpc_calls.extend([ + [node.importprivkey, node.dumpprivkey(address)], + [node.importaddress, address], + ]) + + response = node.importmulti([{ + 'scriptPubKey': {'address': address}, + 'label': '*', + 'timestamp': 'now', + }]) + + assert_equal(response[0]['success'], False) + assert_equal(response[0]['error']['code'], -11) + assert_equal(response[0]['error']['message'], "Invalid label name") + + for rpc_call in rpc_calls: + assert_raises_rpc_error(-11, "Invalid label name", *rpc_call, "*") + def run_test(self): # Check that there's no UTXO on the node node = self.nodes[0] @@ -80,8 +121,14 @@ class WalletLabelsTest(BitcoinTestFramework): label.add_receive_address(address) label.verify(node) + # Check listlabels when passing 'purpose' + node2_addr = self.nodes[1].getnewaddress() + node.setlabel(node2_addr, "node2_addr") + assert_equal(node.listlabels(purpose="send"), ["node2_addr"]) + assert_equal(node.listlabels(purpose="receive"), sorted(['coinbase'] + [label.name for label in labels])) + # Check all labels are returned by listlabels. - assert_equal(node.listlabels(), sorted(['coinbase'] + [label.name for label in labels])) + assert_equal(node.listlabels(), sorted(['coinbase'] + [label.name for label in labels] + ["node2_addr"])) # Send a transaction to each label. for label in labels: @@ -135,6 +182,8 @@ class WalletLabelsTest(BitcoinTestFramework): # in the label. This is a no-op. change_label(node, labels[2].addresses[0], labels[2], labels[2]) + self.invalid_label_name_test() + if self.options.descriptors: # This is a descriptor wallet test because of segwit v1+ addresses self.log.info('Check watchonly labels') diff --git a/test/functional/wallet_listdescriptors.py b/test/functional/wallet_listdescriptors.py index d5372f5aee..fb2156bda1 100755 --- a/test/functional/wallet_listdescriptors.py +++ b/test/functional/wallet_listdescriptors.py @@ -1,11 +1,14 @@ #!/usr/bin/env python3 -# Copyright (c) 2014-2021 The Bitcoin Core developers +# Copyright (c) 2014-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 the listdescriptors RPC.""" +from test_framework.blocktools import ( + TIME_GENESIS_BLOCK, +) from test_framework.descriptors import ( - descsum_create + descsum_create, ) from test_framework.test_framework import BitcoinTestFramework from test_framework.util import ( @@ -15,6 +18,9 @@ from test_framework.util import ( class ListDescriptorsTest(BitcoinTestFramework): + def add_options(self, parser): + self.add_wallet_options(parser, legacy=False) + def set_test_params(self): self.num_nodes = 1 @@ -63,13 +69,13 @@ class ListDescriptorsTest(BitcoinTestFramework): wallet = node.get_wallet_rpc('w2') wallet.importdescriptors([{ 'desc': descsum_create('wpkh(' + xprv + hardened_path + '/0/*)'), - 'timestamp': 1296688602, + 'timestamp': TIME_GENESIS_BLOCK, }]) expected = { 'wallet_name': 'w2', 'descriptors': [ {'desc': descsum_create('wpkh([80002067' + hardened_path + ']' + xpub_acc + '/0/*)'), - 'timestamp': 1296688602, + 'timestamp': TIME_GENESIS_BLOCK, 'active': False, 'range': [0, 0], 'next': 0}, @@ -83,7 +89,7 @@ class ListDescriptorsTest(BitcoinTestFramework): 'wallet_name': 'w2', 'descriptors': [ {'desc': descsum_create('wpkh(' + xprv + hardened_path + '/0/*)'), - 'timestamp': 1296688602, + 'timestamp': TIME_GENESIS_BLOCK, 'active': False, 'range': [0, 0], 'next': 0}, @@ -105,7 +111,7 @@ class ListDescriptorsTest(BitcoinTestFramework): watch_only_wallet = node.get_wallet_rpc('watch-only') watch_only_wallet.importdescriptors([{ 'desc': descsum_create('wpkh(' + xpub_acc + ')'), - 'timestamp': 1296688602, + 'timestamp': TIME_GENESIS_BLOCK, }]) assert_raises_rpc_error(-4, 'Can\'t get descriptor string', watch_only_wallet.listdescriptors, True) @@ -114,14 +120,14 @@ class ListDescriptorsTest(BitcoinTestFramework): wallet = node.get_wallet_rpc('w4') wallet.importdescriptors([{ 'desc': descsum_create('combo(' + node.get_deterministic_priv_key().key + ')'), - 'timestamp': 1296688602, + 'timestamp': TIME_GENESIS_BLOCK, }]) expected = { 'wallet_name': 'w4', 'descriptors': [ {'active': False, 'desc': 'combo(0227d85ba011276cf25b51df6a188b75e604b38770a462b2d0e9fb2fc839ef5d3f)#np574htj', - 'timestamp': 1296688602}, + 'timestamp': TIME_GENESIS_BLOCK}, ] } assert_equal(expected, wallet.listdescriptors()) diff --git a/test/functional/wallet_listreceivedby.py b/test/functional/wallet_listreceivedby.py index f1d7de2f27..8ec21484d1 100755 --- a/test/functional/wallet_listreceivedby.py +++ b/test/functional/wallet_listreceivedby.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2014-2021 The Bitcoin Core developers +# Copyright (c) 2014-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 the listreceivedbyaddress, listreceivedbylabel, getreceivedybaddress, and getreceivedbylabel RPCs.""" @@ -16,6 +16,9 @@ from test_framework.wallet_util import test_address class ReceivedByTest(BitcoinTestFramework): + def add_options(self, parser): + self.add_wallet_options(parser) + def set_test_params(self): self.num_nodes = 2 # whitelist peers to speed up tx relay / mempool sync diff --git a/test/functional/wallet_listsinceblock.py b/test/functional/wallet_listsinceblock.py index aff408ceb1..bfca344fd1 100755 --- a/test/functional/wallet_listsinceblock.py +++ b/test/functional/wallet_listsinceblock.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2017-2021 The Bitcoin Core developers +# Copyright (c) 2017-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 the listsinceblock RPC.""" @@ -20,6 +20,9 @@ from test_framework.wallet_util import bytes_to_wif from decimal import Decimal class ListSinceBlockTest(BitcoinTestFramework): + def add_options(self, parser): + self.add_wallet_options(parser) + def set_test_params(self): self.num_nodes = 4 self.setup_clean_chain = True @@ -46,6 +49,7 @@ class ListSinceBlockTest(BitcoinTestFramework): self.test_desc() self.test_send_to_self() self.test_op_return() + self.test_label() def test_no_blockhash(self): self.log.info("Test no blockhash") @@ -462,6 +466,20 @@ class ListSinceBlockTest(BitcoinTestFramework): assert 'address' not in op_ret_tx + def test_label(self): + self.log.info('Test passing "label" argument fetches incoming transactions having the specified label') + new_addr = self.nodes[1].getnewaddress(label="new_addr", address_type="bech32") + + self.nodes[2].sendtoaddress(address=new_addr, amount="0.001") + self.generate(self.nodes[2], 1) + + for label in ["new_addr", ""]: + new_addr_transactions = self.nodes[1].listsinceblock(label=label)["transactions"] + assert_equal(len(new_addr_transactions), 1) + assert_equal(new_addr_transactions[0]["label"], label) + if label == "new_addr": + assert_equal(new_addr_transactions[0]["address"], new_addr) + if __name__ == '__main__': ListSinceBlockTest().main() diff --git a/test/functional/wallet_listtransactions.py b/test/functional/wallet_listtransactions.py index 9bb06774a5..a44c129c87 100755 --- a/test/functional/wallet_listtransactions.py +++ b/test/functional/wallet_listtransactions.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2014-2021 The Bitcoin Core developers +# Copyright (c) 2014-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 the listtransactions API.""" @@ -21,6 +21,9 @@ from test_framework.util import ( class ListTransactionsTest(BitcoinTestFramework): + def add_options(self, parser): + self.add_wallet_options(parser) + def set_test_params(self): self.num_nodes = 3 # This test isn't testing txn relay/timing, so set whitelist on the diff --git a/test/functional/wallet_migration.py b/test/functional/wallet_migration.py index 3c1cb6ac32..688ac98617 100755 --- a/test/functional/wallet_migration.py +++ b/test/functional/wallet_migration.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2020 The Bitcoin Core developers +# Copyright (c) 2020-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 Migrating a wallet from legacy to descriptor.""" @@ -19,6 +19,9 @@ from test_framework.wallet_util import ( class WalletMigrationTest(BitcoinTestFramework): + def add_options(self, parser): + self.add_wallet_options(parser) + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 1 @@ -37,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): @@ -184,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"] @@ -253,11 +256,9 @@ 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 + # External address label imports0.setlabel(default.getnewaddress(), "external") # Normal non-watchonly tx @@ -310,16 +311,19 @@ class WalletMigrationTest(BitcoinTestFramework): assert_raises_rpc_error(-5, "Invalid or non-wallet transaction id", watchonly.gettransaction, received_txid) assert_equal(len(watchonly.listtransactions(include_watchonly=True)), 3) + # Check that labels were migrated and persisted to watchonly wallet + self.nodes[0].unloadwallet("imports0_watchonly") + self.nodes[0].loadwallet("imports0_watchonly") + labels = watchonly.listlabels() + assert "external" in labels + assert "imported" in labels + def test_no_privkeys(self): default = self.nodes[0].get_wallet_rpc(self.default_wallet_name) # 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"] @@ -342,11 +346,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") @@ -393,6 +393,15 @@ class WalletMigrationTest(BitcoinTestFramework): assert_equal(bals, wallet.getbalances()) + def test_encrypted(self): + self.log.info("Test migration of an encrypted wallet") + wallet = self.create_legacy_wallet("encrypted") + + wallet.encryptwallet("pass") + + assert_raises_rpc_error(-15, "Error: migratewallet on encrypted wallets is currently unsupported.", wallet.migratewallet) + # TODO: Fix migratewallet so that we can actually migrate encrypted wallets + def run_test(self): self.generate(self.nodes[0], 101) @@ -402,6 +411,7 @@ class WalletMigrationTest(BitcoinTestFramework): self.test_other_watchonly() self.test_no_privkeys() self.test_pk_coinbases() + self.test_encrypted() if __name__ == '__main__': WalletMigrationTest().main() diff --git a/test/functional/wallet_miniscript.py b/test/functional/wallet_miniscript.py index 2252f1e424..cefcaf4dc7 100755 --- a/test/functional/wallet_miniscript.py +++ b/test/functional/wallet_miniscript.py @@ -22,6 +22,9 @@ MINISCRIPTS = [ class WalletMiniscriptTest(BitcoinTestFramework): + def add_options(self, parser): + self.add_wallet_options(parser, legacy=False) + def set_test_params(self): self.num_nodes = 1 diff --git a/test/functional/wallet_multisig_descriptor_psbt.py b/test/functional/wallet_multisig_descriptor_psbt.py index 2b565db137..12069fb00d 100755 --- a/test/functional/wallet_multisig_descriptor_psbt.py +++ b/test/functional/wallet_multisig_descriptor_psbt.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2021 The Bitcoin Core developers +# Copyright (c) 2021-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 a basic M-of-N multisig setup between multiple people using descriptor wallets and PSBTs, as well as a signing flow. @@ -16,6 +16,9 @@ from test_framework.util import ( class WalletMultisigDescriptorPSBTTest(BitcoinTestFramework): + def add_options(self, parser): + self.add_wallet_options(parser, legacy=False) + def set_test_params(self): self.num_nodes = 3 self.setup_clean_chain = True diff --git a/test/functional/wallet_multiwallet.py b/test/functional/wallet_multiwallet.py index 1c890d7207..2faf6cad8b 100755 --- a/test/functional/wallet_multiwallet.py +++ b/test/functional/wallet_multiwallet.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2017-2021 The Bitcoin Core developers +# Copyright (c) 2017-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 multiwallet. @@ -52,6 +52,7 @@ class MultiWalletTest(BitcoinTestFramework): self.skip_if_no_wallet() def add_options(self, parser): + self.add_wallet_options(parser) parser.add_argument( '--data_wallets_dir', default=os.path.join(os.path.dirname(os.path.realpath(__file__)), 'data/wallets/'), @@ -302,12 +303,8 @@ class MultiWalletTest(BitcoinTestFramework): assert_raises_rpc_error(-18, "Wallet file verification failed. Failed to load database path '{}'. Path does not exist.".format(path), self.nodes[0].loadwallet, 'wallets') # Fail to load duplicate wallets - path = os.path.join(self.options.tmpdir, "node0", "regtest", "wallets", "w1", "wallet.dat") - if self.options.descriptors: - assert_raises_rpc_error(-4, f"Wallet file verification failed. SQLiteDatabase: Unable to obtain an exclusive lock on the database, is it being used by another instance of {self.config['environment']['PACKAGE_NAME']}?", self.nodes[0].loadwallet, wallet_names[0]) - else: - assert_raises_rpc_error(-35, "Wallet file verification failed. Refusing to load database. Data file '{}' is already loaded.".format(path), self.nodes[0].loadwallet, wallet_names[0]) - + assert_raises_rpc_error(-35, "Wallet \"w1\" is already loaded.", self.nodes[0].loadwallet, wallet_names[0]) + if not self.options.descriptors: # This tests the default wallet that BDB makes, so SQLite wallet doesn't need to test this # Fail to load duplicate wallets by different ways (directory and filepath) path = os.path.join(self.options.tmpdir, "node0", "regtest", "wallets", "wallet.dat") diff --git a/test/functional/wallet_orphanedreward.py b/test/functional/wallet_orphanedreward.py index 7295db4653..d9f7c14ded 100755 --- a/test/functional/wallet_orphanedreward.py +++ b/test/functional/wallet_orphanedreward.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2020-2021 The Bitcoin Core developers +# Copyright (c) 2020-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 orphaned block rewards in the wallet.""" @@ -8,6 +8,9 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.util import assert_equal class OrphanedBlockRewardTest(BitcoinTestFramework): + def add_options(self, parser): + self.add_wallet_options(parser) + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 2 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_reorgsrestore.py b/test/functional/wallet_reorgsrestore.py index f2bdb114b7..1c79c6816c 100755 --- a/test/functional/wallet_reorgsrestore.py +++ b/test/functional/wallet_reorgsrestore.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2019-2021 The Bitcoin Core developers +# Copyright (c) 2019-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. @@ -23,6 +23,9 @@ from test_framework.util import ( ) class ReorgsRestoreTest(BitcoinTestFramework): + def add_options(self, parser): + self.add_wallet_options(parser) + def set_test_params(self): self.num_nodes = 3 @@ -91,11 +94,11 @@ class ReorgsRestoreTest(BitcoinTestFramework): tx_after_reorg = self.nodes[1].gettransaction(txid) # Check that normal confirmed tx is confirmed again but with different blockhash assert_equal(tx_after_reorg["confirmations"], 2) - assert(tx_before_reorg["blockhash"] != tx_after_reorg["blockhash"]) + assert tx_before_reorg["blockhash"] != tx_after_reorg["blockhash"] conflicted_after_reorg = self.nodes[1].gettransaction(conflicted_txid) # Check that conflicted tx is confirmed again with blockhash different than previously conflicting tx assert_equal(conflicted_after_reorg["confirmations"], 1) - assert(conflicting["blockhash"] != conflicted_after_reorg["blockhash"]) + assert conflicting["blockhash"] != conflicted_after_reorg["blockhash"] if __name__ == '__main__': ReorgsRestoreTest().main() diff --git a/test/functional/wallet_resendwallettransactions.py b/test/functional/wallet_resendwallettransactions.py index b3d02fbfc9..7e4a4002b2 100755 --- a/test/functional/wallet_resendwallettransactions.py +++ b/test/functional/wallet_resendwallettransactions.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2017-2021 The Bitcoin Core developers +# Copyright (c) 2017-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 that the wallet resends transactions periodically.""" @@ -18,6 +18,9 @@ from test_framework.util import ( ) class ResendWalletTransactionsTest(BitcoinTestFramework): + def add_options(self, parser): + self.add_wallet_options(parser) + def set_test_params(self): self.num_nodes = 1 diff --git a/test/functional/wallet_send.py b/test/functional/wallet_send.py index 83b8332c4d..ac3ec06eec 100755 --- a/test/functional/wallet_send.py +++ b/test/functional/wallet_send.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2020-2021 The Bitcoin Core developers +# Copyright (c) 2020-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 the send RPC command.""" @@ -25,6 +25,9 @@ from test_framework.util import ( from test_framework.wallet_util import bytes_to_wif class WalletSendTest(BitcoinTestFramework): + def add_options(self, parser): + self.add_wallet_options(parser) + def set_test_params(self): self.num_nodes = 2 # whitelist all peers to speed up tx relay / mempool sync @@ -42,7 +45,7 @@ class WalletSendTest(BitcoinTestFramework): conf_target=None, estimate_mode=None, fee_rate=None, add_to_wallet=None, psbt=None, inputs=None, add_inputs=None, include_unsafe=None, change_address=None, change_position=None, change_type=None, include_watching=None, locktime=None, lock_unspents=None, replaceable=None, subtract_fee_from_outputs=None, - expect_error=None, solving_data=None): + expect_error=None, solving_data=None, minconf=None): assert (amount is None) != (data is None) from_balance_before = from_wallet.getbalances()["mine"]["trusted"] @@ -103,6 +106,8 @@ class WalletSendTest(BitcoinTestFramework): options["subtract_fee_from_outputs"] = subtract_fee_from_outputs if solving_data is not None: options["solving_data"] = solving_data + if minconf is not None: + options["minconf"] = minconf if len(options.keys()) == 0: options = None @@ -276,11 +281,11 @@ class WalletSendTest(BitcoinTestFramework): self.log.info("Don't broadcast...") res = self.test_send(from_wallet=w0, to_wallet=w1, amount=1, add_to_wallet=False) - assert(res["hex"]) + assert res["hex"] self.log.info("Return PSBT...") res = self.test_send(from_wallet=w0, to_wallet=w1, amount=1, psbt=True) - assert(res["psbt"]) + assert res["psbt"] self.log.info("Create transaction that spends to address, but don't broadcast...") self.test_send(from_wallet=w0, to_wallet=w1, amount=1, add_to_wallet=False) @@ -409,10 +414,12 @@ class WalletSendTest(BitcoinTestFramework): assert res["complete"] utxo1 = w0.listunspent()[0] assert_equal(utxo1["amount"], 50) + ERR_NOT_ENOUGH_PRESET_INPUTS = "The preselected coins total amount does not cover the transaction target. " \ + "Please allow other inputs to be automatically selected or include more coins manually" self.test_send(from_wallet=w0, to_wallet=w1, amount=51, inputs=[utxo1], - expect_error=(-4, "Insufficient funds")) + expect_error=(-4, ERR_NOT_ENOUGH_PRESET_INPUTS)) self.test_send(from_wallet=w0, to_wallet=w1, amount=51, inputs=[utxo1], add_inputs=False, - expect_error=(-4, "Insufficient funds")) + expect_error=(-4, ERR_NOT_ENOUGH_PRESET_INPUTS)) res = self.test_send(from_wallet=w0, to_wallet=w1, amount=51, inputs=[utxo1], add_inputs=True, add_to_wallet=False) assert res["complete"] @@ -482,6 +489,16 @@ class WalletSendTest(BitcoinTestFramework): res = self.test_send(from_wallet=w5, to_wallet=w0, amount=1, include_unsafe=True) assert res["complete"] + self.log.info("Minconf") + self.nodes[1].createwallet(wallet_name="minconfw") + minconfw= self.nodes[1].get_wallet_rpc("minconfw") + self.test_send(from_wallet=w0, to_wallet=minconfw, amount=2) + self.generate(self.nodes[0], 3) + self.test_send(from_wallet=minconfw, to_wallet=w0, amount=1, minconf=4, expect_error=(-4, "Insufficient funds")) + self.test_send(from_wallet=minconfw, to_wallet=w0, amount=1, minconf=-4, expect_error=(-8, "Negative minconf")) + res = self.test_send(from_wallet=minconfw, to_wallet=w0, amount=1, minconf=3) + assert res["complete"] + self.log.info("External outputs") eckey = ECKey() eckey.generate() diff --git a/test/functional/wallet_sendall.py b/test/functional/wallet_sendall.py index 4fe11455b1..f6440f07d7 100755 --- a/test/functional/wallet_sendall.py +++ b/test/functional/wallet_sendall.py @@ -26,6 +26,9 @@ def cleanup(func): class SendallTest(BitcoinTestFramework): # Setup and helpers + def add_options(self, parser): + self.add_wallet_options(parser) + def skip_test_if_missing_module(self): self.skip_if_no_wallet() @@ -282,6 +285,12 @@ class SendallTest(BitcoinTestFramework): fee_rate=100000) @cleanup + def sendall_fails_on_low_fee(self): + self.log.info("Test sendall fails if the transaction fee is lower than the minimum fee rate setting") + assert_raises_rpc_error(-8, "Fee rate (0.999 sat/vB) is lower than the minimum fee rate setting (1.000 sat/vB)", + self.wallet.sendall, recipients=[self.recipient], fee_rate=0.999) + + @cleanup def sendall_watchonly_specific_inputs(self): self.log.info("Test sendall with a subset of UTXO pool in a watchonly wallet") self.add_utxos([17, 4]) @@ -308,9 +317,75 @@ class SendallTest(BitcoinTestFramework): assert_equal(decoded["tx"]["vin"][0]["vout"], utxo["vout"]) assert_equal(decoded["tx"]["vout"][0]["scriptPubKey"]["address"], self.remainder_target) + @cleanup + def sendall_with_minconf(self): + # utxo of 17 bicoin has 6 confirmations, utxo of 4 has 3 + self.add_utxos([17]) + self.generate(self.nodes[0], 2) + self.add_utxos([4]) + self.generate(self.nodes[0], 2) + + self.log.info("Test sendall fails because minconf is negative") + + assert_raises_rpc_error(-8, + "Invalid minconf (minconf cannot be negative): -2", + self.wallet.sendall, + recipients=[self.remainder_target], + options={"minconf": -2}) + self.log.info("Test sendall fails because minconf is used while specific inputs are provided") + + utxo = self.wallet.listunspent()[0] + assert_raises_rpc_error(-8, + "Cannot combine minconf or maxconf with specific inputs.", + self.wallet.sendall, + recipients=[self.remainder_target], + options={"inputs": [utxo], "minconf": 2}) + + self.log.info("Test sendall fails because there are no utxos with enough confirmations specified by minconf") + + assert_raises_rpc_error(-6, + "Total value of UTXO pool too low to pay for transaction. Try using lower feerate or excluding uneconomic UTXOs with 'send_max' option.", + self.wallet.sendall, + recipients=[self.remainder_target], + options={"minconf": 7}) + + self.log.info("Test sendall only spends utxos with a specified number of confirmations when minconf is used") + self.wallet.sendall(recipients=[self.remainder_target], fee_rate=300, options={"minconf": 6}) + + assert_equal(len(self.wallet.listunspent()), 1) + assert_equal(self.wallet.listunspent()[0]['confirmations'], 3) + + # decrease minconf and show the remaining utxo is picked up + self.wallet.sendall(recipients=[self.remainder_target], fee_rate=300, options={"minconf": 3}) + assert_equal(self.wallet.getbalance(), 0) + + @cleanup + def sendall_with_maxconf(self): + # utxo of 17 bicoin has 6 confirmations, utxo of 4 has 3 + self.add_utxos([17]) + self.generate(self.nodes[0], 2) + self.add_utxos([4]) + self.generate(self.nodes[0], 2) + + self.log.info("Test sendall fails because there are no utxos with enough confirmations specified by maxconf") + assert_raises_rpc_error(-6, + "Total value of UTXO pool too low to pay for transaction. Try using lower feerate or excluding uneconomic UTXOs with 'send_max' option.", + self.wallet.sendall, + recipients=[self.remainder_target], + options={"maxconf": 1}) + + self.log.info("Test sendall only spends utxos with a specified number of confirmations when maxconf is used") + self.wallet.sendall(recipients=[self.remainder_target], fee_rate=300, options={"maxconf":4}) + assert_equal(len(self.wallet.listunspent()), 1) + assert_equal(self.wallet.listunspent()[0]['confirmations'], 6) + # This tests needs to be the last one otherwise @cleanup will fail with "Transaction too large" error def sendall_fails_with_transaction_too_large(self): self.log.info("Test that sendall fails if resulting transaction is too large") + + # Force the wallet to bulk-generate the addresses we'll need + self.wallet.keypoolrefill(1600) + # create many inputs outputs = {self.wallet.getnewaddress(): 0.000025 for _ in range(1600)} self.def_wallet.sendmany(amounts=outputs) @@ -373,9 +448,18 @@ class SendallTest(BitcoinTestFramework): # Sendall fails when providing a fee that is too high self.sendall_fails_on_high_fee() + # Sendall fails when fee rate is lower than minimum + self.sendall_fails_on_low_fee() + # Sendall succeeds with watchonly wallets spending specific UTXOs self.sendall_watchonly_specific_inputs() + # Sendall only uses outputs with at least a give number of confirmations when using minconf + self.sendall_with_minconf() + + # Sendall only uses outputs with less than a given number of confirmation when using minconf + self.sendall_with_maxconf() + # Sendall fails when many inputs result to too large transaction self.sendall_fails_with_transaction_too_large() diff --git a/test/functional/wallet_signer.py b/test/functional/wallet_signer.py index db3a8a2efa..8d25044e43 100755 --- a/test/functional/wallet_signer.py +++ b/test/functional/wallet_signer.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2017-2021 The Bitcoin Core developers +# Copyright (c) 2017-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 external signer. @@ -13,11 +13,15 @@ import platform from test_framework.test_framework import BitcoinTestFramework from test_framework.util import ( assert_equal, + assert_greater_than, assert_raises_rpc_error, ) class WalletSignerTest(BitcoinTestFramework): + def add_options(self, parser): + self.add_wallet_options(parser, legacy=False) + def mock_signer_path(self): path = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'mocks', 'signer.py') if platform.system() == "Windows": @@ -166,11 +170,11 @@ 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"] - assert(mock_wallet.testmempoolaccept([mock_tx])[0]["allowed"]) + assert mock_wallet.testmempoolaccept([mock_tx])[0]["allowed"] # # Create a new wallet and populate with specific public keys, in order # # to work with the mock signed PSBT. @@ -199,22 +203,42 @@ class WalletSignerTest(BitcoinTestFramework): # assert_equal(result[1], {'success': True}) assert_equal(hww.getwalletinfo()["txcount"], 1) - assert(hww.testmempoolaccept([mock_tx])[0]["allowed"]) + assert hww.testmempoolaccept([mock_tx])[0]["allowed"] with open(os.path.join(self.nodes[1].cwd, "mock_psbt"), "w", encoding="utf8") as f: f.write(mock_psbt_signed["psbt"]) 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 res["complete"] assert_equal(res["hex"], mock_tx) self.log.info('Test sendall using hww1') res = hww.sendall(recipients=[{dest:0.5}, hww.getrawchangeaddress()],options={"add_to_wallet": False}) - assert(res["complete"]) + 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/functional/wallet_signmessagewithaddress.py b/test/functional/wallet_signmessagewithaddress.py index 74a8f2eef2..4a4b818bd1 100755 --- a/test/functional/wallet_signmessagewithaddress.py +++ b/test/functional/wallet_signmessagewithaddress.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2016-2021 The Bitcoin Core developers +# Copyright (c) 2016-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 commands for signing and verifying messages.""" @@ -10,6 +10,9 @@ from test_framework.util import ( ) class SignMessagesWithAddressTest(BitcoinTestFramework): + def add_options(self, parser): + self.add_wallet_options(parser) + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 1 diff --git a/test/functional/wallet_signrawtransactionwithwallet.py b/test/functional/wallet_signrawtransactionwithwallet.py index 6b30386b7e..3d2f41cb83 100755 --- a/test/functional/wallet_signrawtransactionwithwallet.py +++ b/test/functional/wallet_signrawtransactionwithwallet.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2021 The Bitcoin Core developers +# Copyright (c) 2015-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 transaction signing using the signrawtransactionwithwallet RPC.""" @@ -34,6 +34,9 @@ from decimal import ( ) class SignRawTransactionWithWalletTest(BitcoinTestFramework): + def add_options(self, parser): + self.add_wallet_options(parser) + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 2 diff --git a/test/functional/wallet_simulaterawtx.py b/test/functional/wallet_simulaterawtx.py index a408b99515..545aad892c 100755 --- a/test/functional/wallet_simulaterawtx.py +++ b/test/functional/wallet_simulaterawtx.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2021 The Bitcoin Core developers +# Copyright (c) 2021-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 simulaterawtransaction. @@ -15,6 +15,9 @@ from test_framework.util import ( ) class SimulateTxTest(BitcoinTestFramework): + def add_options(self, parser): + self.add_wallet_options(parser) + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 1 diff --git a/test/functional/wallet_startup.py b/test/functional/wallet_startup.py index d96c2da686..2cc4e312af 100755 --- a/test/functional/wallet_startup.py +++ b/test/functional/wallet_startup.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2017-2020 The Bitcoin Core developers +# Copyright (c) 2017-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 load on startup. @@ -13,6 +13,9 @@ from test_framework.util import ( class WalletStartupTest(BitcoinTestFramework): + def add_options(self, parser): + self.add_wallet_options(parser) + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 1 diff --git a/test/functional/wallet_taproot.py b/test/functional/wallet_taproot.py index 4c28958982..b52892704f 100755 --- a/test/functional/wallet_taproot.py +++ b/test/functional/wallet_taproot.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2021 The Bitcoin Core developers +# Copyright (c) 2021-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 generation and spending of P2TR addresses.""" @@ -187,6 +187,9 @@ def compute_raw_taproot_address(pubkey): class WalletTaprootTest(BitcoinTestFramework): """Test generation and spending of P2TR address outputs.""" + def add_options(self, parser): + self.add_wallet_options(parser, legacy=False) + def set_test_params(self): self.num_nodes = 2 self.setup_clean_chain = True @@ -241,7 +244,7 @@ class WalletTaprootTest(BitcoinTestFramework): desc_pub = self.make_desc(pattern, privmap, keys, True) assert_equal(self.nodes[0].getdescriptorinfo(desc)['descriptor'], desc_pub) result = addr_gen.importdescriptors([{"desc": desc_pub, "active": True, "timestamp": "now"}]) - assert(result[0]['success']) + assert result[0]['success'] address_type = "bech32m" if "tr" in pattern else "bech32" for i in range(4): addr_g = addr_gen.getnewaddress(address_type=address_type) @@ -257,9 +260,9 @@ class WalletTaprootTest(BitcoinTestFramework): # tr descriptors can be imported result = privs_tr_enabled.importdescriptors([{"desc": desc, "timestamp": "now"}]) - assert(result[0]["success"]) + assert result[0]['success'] result = pubs_tr_enabled.importdescriptors([{"desc": desc_pub, "timestamp": "now"}]) - assert(result[0]["success"]) + assert result[0]["success"] # Cleanup privs_tr_enabled.unloadwallet() @@ -281,9 +284,9 @@ class WalletTaprootTest(BitcoinTestFramework): assert_equal(self.nodes[0].getdescriptorinfo(desc_pay)['descriptor'], desc_pay_pub) assert_equal(self.nodes[0].getdescriptorinfo(desc_change)['descriptor'], desc_change_pub) result = rpc_online.importdescriptors([{"desc": desc_pay, "active": True, "timestamp": "now"}]) - assert(result[0]['success']) + assert result[0]['success'] result = rpc_online.importdescriptors([{"desc": desc_change, "active": True, "timestamp": "now", "internal": True}]) - assert(result[0]['success']) + assert result[0]['success'] address_type = "bech32m" if "tr" in pattern else "bech32" for i in range(4): addr_g = rpc_online.getnewaddress(address_type=address_type) @@ -299,12 +302,12 @@ class WalletTaprootTest(BitcoinTestFramework): # Increase fee_rate to compensate for the wallet's inability to estimate fees for script path spends. res = rpc_online.sendtoaddress(address=self.boring.getnewaddress(), amount=Decimal(ret_amnt) / 100000000, subtractfeefromamount=True, fee_rate=200) self.generatetoaddress(self.nodes[0], 1, self.boring.getnewaddress(), sync_fun=self.no_op) - assert(rpc_online.gettransaction(res)["confirmations"] > 0) + assert rpc_online.gettransaction(res)["confirmations"] > 0 # Cleanup txid = rpc_online.sendall(recipients=[self.boring.getnewaddress()])["txid"] self.generatetoaddress(self.nodes[0], 1, self.boring.getnewaddress(), sync_fun=self.no_op) - assert(rpc_online.gettransaction(txid)["confirmations"] > 0) + assert rpc_online.gettransaction(txid)["confirmations"] > 0 rpc_online.unloadwallet() def do_test_psbt(self, comment, pattern, privmap, treefn, keys_pay, keys_change): @@ -326,16 +329,16 @@ class WalletTaprootTest(BitcoinTestFramework): assert_equal(self.nodes[0].getdescriptorinfo(desc_pay)['descriptor'], desc_pay_pub) assert_equal(self.nodes[0].getdescriptorinfo(desc_change)['descriptor'], desc_change_pub) result = psbt_online.importdescriptors([{"desc": desc_pay_pub, "active": True, "timestamp": "now"}]) - assert(result[0]['success']) + assert result[0]['success'] result = psbt_online.importdescriptors([{"desc": desc_change_pub, "active": True, "timestamp": "now", "internal": True}]) - assert(result[0]['success']) + assert result[0]['success'] result = psbt_offline.importdescriptors([{"desc": desc_pay, "active": True, "timestamp": "now"}]) - assert(result[0]['success']) + assert result[0]['success'] result = psbt_offline.importdescriptors([{"desc": desc_change, "active": True, "timestamp": "now", "internal": True}]) - assert(result[0]['success']) + assert result[0]['success'] for key in keys_pay + keys_change: result = key_only_wallet.importdescriptors([{"desc": descsum_create(f"wpkh({key['xprv']}/*)"), "timestamp":"now"}]) - assert(result[0]["success"]) + assert result[0]["success"] address_type = "bech32m" if "tr" in pattern else "bech32" for i in range(4): addr_g = psbt_online.getnewaddress(address_type=address_type) @@ -372,7 +375,7 @@ class WalletTaprootTest(BitcoinTestFramework): txid = self.nodes[0].sendrawtransaction(rawtx) self.generatetoaddress(self.nodes[0], 1, self.boring.getnewaddress(), sync_fun=self.no_op) - assert(psbt_online.gettransaction(txid)['confirmations'] > 0) + assert psbt_online.gettransaction(txid)['confirmations'] > 0 # Cleanup psbt = psbt_online.sendall(recipients=[self.boring.getnewaddress()], options={"psbt": True})["psbt"] @@ -380,7 +383,7 @@ class WalletTaprootTest(BitcoinTestFramework): rawtx = self.nodes[0].finalizepsbt(res['psbt'])['hex'] txid = self.nodes[0].sendrawtransaction(rawtx) self.generatetoaddress(self.nodes[0], 1, self.boring.getnewaddress(), sync_fun=self.no_op) - assert(psbt_online.gettransaction(txid)['confirmations'] > 0) + assert psbt_online.gettransaction(txid)['confirmations'] > 0 psbt_online.unloadwallet() psbt_offline.unloadwallet() diff --git a/test/functional/wallet_timelock.py b/test/functional/wallet_timelock.py index a71cec6607..57a7c3907a 100755 --- a/test/functional/wallet_timelock.py +++ b/test/functional/wallet_timelock.py @@ -8,6 +8,9 @@ from test_framework.util import assert_equal class WalletLocktimeTest(BitcoinTestFramework): + def add_options(self, parser): + self.add_wallet_options(parser) + def set_test_params(self): self.num_nodes = 1 diff --git a/test/functional/wallet_transactiontime_rescan.py b/test/functional/wallet_transactiontime_rescan.py index 9caa1fa3d0..de9616b4a1 100755 --- a/test/functional/wallet_transactiontime_rescan.py +++ b/test/functional/wallet_transactiontime_rescan.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2018-2021 The Bitcoin Core developers +# Copyright (c) 2018-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 transaction time during old block rescanning @@ -17,6 +17,9 @@ from test_framework.util import ( class TransactionTimeRescanTest(BitcoinTestFramework): + def add_options(self, parser): + self.add_wallet_options(parser) + def set_test_params(self): self.setup_clean_chain = False self.num_nodes = 3 diff --git a/test/functional/wallet_txn_clone.py b/test/functional/wallet_txn_clone.py index 5bdde13aa4..d8ef66d83a 100755 --- a/test/functional/wallet_txn_clone.py +++ b/test/functional/wallet_txn_clone.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2014-2021 The Bitcoin Core developers +# Copyright (c) 2014-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 the wallet accounts properly when there are cloned transactions with malleated scriptsigs.""" @@ -24,6 +24,7 @@ class TxnMallTest(BitcoinTestFramework): self.skip_if_no_wallet() def add_options(self, parser): + self.add_wallet_options(parser) parser.add_argument("--mineblock", dest="mine_block", default=False, action="store_true", help="Test double-spend of 1-confirmed transaction") parser.add_argument("--segwit", dest="segwit", default=False, action="store_true", diff --git a/test/functional/wallet_txn_doublespend.py b/test/functional/wallet_txn_doublespend.py index 206187fb61..38ebfe0d7a 100755 --- a/test/functional/wallet_txn_doublespend.py +++ b/test/functional/wallet_txn_doublespend.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2014-2021 The Bitcoin Core developers +# Copyright (c) 2014-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 the wallet accounts properly when there is a double-spend conflict.""" @@ -22,6 +22,7 @@ class TxnMallTest(BitcoinTestFramework): self.skip_if_no_wallet() def add_options(self, parser): + self.add_wallet_options(parser) parser.add_argument("--mineblock", dest="mine_block", default=False, action="store_true", help="Test double-spend of 1-confirmed transaction") diff --git a/test/functional/wallet_upgradewallet.py b/test/functional/wallet_upgradewallet.py index c452e1eafd..4495a7d778 100755 --- a/test/functional/wallet_upgradewallet.py +++ b/test/functional/wallet_upgradewallet.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2018-2021 The Bitcoin Core developers +# Copyright (c) 2018-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. """upgradewallet RPC functional test @@ -45,6 +45,9 @@ def deser_keymeta(f): return ver, create_time, kp_str, seed_id, fpr, path_len, path, has_key_orig class UpgradeWalletTest(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 = 3 diff --git a/test/functional/wallet_watchonly.py b/test/functional/wallet_watchonly.py index 69c32ba54c..dd4514318c 100755 --- a/test/functional/wallet_watchonly.py +++ b/test/functional/wallet_watchonly.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2018-2021 The Bitcoin Core developers +# Copyright (c) 2018-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 createwallet watchonly arguments. @@ -14,6 +14,9 @@ from test_framework.util import ( class CreateWalletWatchonlyTest(BitcoinTestFramework): + def add_options(self, parser): + self.add_wallet_options(parser) + def set_test_params(self): self.num_nodes = 1 diff --git a/test/fuzz/test_runner.py b/test/fuzz/test_runner.py index e2eab2a0fe..af21e7b956 100755 --- a/test/fuzz/test_runner.py +++ b/test/fuzz/test_runner.py @@ -143,7 +143,7 @@ def main(): timeout=20, check=True, stderr=subprocess.PIPE, - universal_newlines=True, + text=True, ).stderr if "libFuzzer" not in help_output: logging.error("Must be built with libFuzzer") @@ -200,7 +200,7 @@ def generate_corpus(*, fuzz_pool, src_dir, build_dir, corpus_dir, targets): env=get_fuzz_env(target=t, source_dir=src_dir), check=True, stderr=subprocess.PIPE, - universal_newlines=True, + text=True, ).stderr)) futures = [] @@ -241,7 +241,7 @@ def merge_inputs(*, fuzz_pool, corpus, test_list, src_dir, build_dir, merge_dir) env=get_fuzz_env(target=t, source_dir=src_dir), check=True, stderr=subprocess.PIPE, - universal_newlines=True, + text=True, ).stderr logging.debug(output) @@ -270,7 +270,7 @@ def run_once(*, fuzz_pool, corpus, test_list, src_dir, build_dir, use_valgrind): args, env=get_fuzz_env(target=t, source_dir=src_dir), stderr=subprocess.PIPE, - universal_newlines=True, + text=True, ) output += result.stderr return output, result @@ -299,7 +299,7 @@ def parse_test_list(*, fuzz_bin): }, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, - universal_newlines=True, + text=True, ).stdout.splitlines() return test_list_all diff --git a/test/get_previous_releases.py b/test/get_previous_releases.py index c983dce619..7f5f15655c 100755 --- a/test/get_previous_releases.py +++ b/test/get_previous_releases.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (c) 2018-2021 The Bitcoin Core developers +# Copyright (c) 2018-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. # @@ -136,7 +136,7 @@ def download_binary(tag, args) -> int: tarballHash = hasher.hexdigest() if tarballHash not in SHA256_SUMS or SHA256_SUMS[tarballHash]['tarball'] != tarball: - if tarball in SHA256_SUMS.values(): + if tarball in [v['tarball'] for v in SHA256_SUMS.values()]: print("Checksum did not match") return 1 @@ -148,10 +148,39 @@ def download_binary(tag, args) -> int: ret = subprocess.run(['tar', '-zxf', tarball, '-C', tag, '--strip-components=1', 'bitcoin-{tag}'.format(tag=tag[1:])]).returncode - if ret: + if ret != 0: + print(f"Failed to extract the {tag} tarball") return ret Path(tarball).unlink() + + if tag >= "v23" and platform == "arm64-apple-darwin": + # Starting with v23 there are arm64 binaries for ARM (e.g. M1, M2) macs, but they have to be signed to run + binary_path = f'{os.getcwd()}/{tag}/bin/' + + for arm_binary in os.listdir(binary_path): + # Is it already signed? + ret = subprocess.run( + ['codesign', '-v', binary_path + arm_binary], + stderr=subprocess.DEVNULL, # Suppress expected stderr output + ).returncode + if ret == 1: + # Have to self-sign the binary + ret = subprocess.run( + ['codesign', '-s', '-', binary_path + arm_binary] + ).returncode + if ret != 0: + print(f"Failed to self-sign {tag} {arm_binary} arm64 binary") + return 1 + + # Confirm success + ret = subprocess.run( + ['codesign', '-v', binary_path + arm_binary] + ).returncode + if ret != 0: + print(f"Failed to verify the self-signed {tag} {arm_binary} arm64 binary") + return 1 + return 0 @@ -260,11 +289,10 @@ if __name__ == '__main__': help='download release binary.') parser.add_argument('-t', '--target-dir', action='store', help='target directory.', default='releases') - parser.add_argument('tags', nargs='*', default=set( - [v['tag'] for v in SHA256_SUMS.values()] - ), + all_tags = sorted([*set([v['tag'] for v in SHA256_SUMS.values()])]) + parser.add_argument('tags', nargs='*', default=all_tags, help='release tags. e.g.: v0.18.1 v0.20.0rc2 ' - '(if not specified, the full list needed for' + '(if not specified, the full list needed for ' 'backwards compatibility tests will be used)' ) args = parser.parse_args() diff --git a/test/lint/README.md b/test/lint/README.md index 8d592c3282..704922d7ab 100644 --- a/test/lint/README.md +++ b/test/lint/README.md @@ -1,5 +1,23 @@ This folder contains lint scripts. +Running locally +=============== + +To run linters locally with the same versions as the CI environment, use the included +Dockerfile: + +```sh +cd ./ci/lint +docker build -t bitcoin-linter . + +cd /root/of/bitcoin/repo +docker run --rm -v $(pwd):/bitcoin -it bitcoin-linter +``` + +After building the container once, you can simply run the last command any time you +want to lint. + + check-doc.py ============ Check for missing documentation of command line options. diff --git a/test/lint/all-lint.py b/test/lint/all-lint.py index 34a7b9742a..c7889796c6 100755 --- a/test/lint/all-lint.py +++ b/test/lint/all-lint.py @@ -10,11 +10,12 @@ from glob import glob from pathlib import Path from subprocess import run +from sys import executable exit_code = 0 mod_path = Path(__file__).parent for lint in glob(f"{mod_path}/lint-*.py"): - result = run([lint]) + result = run([executable, lint]) if result.returncode != 0: print(f"^---- failure generated from {lint.split('/')[-1]}") exit_code |= result.returncode diff --git a/test/lint/check-doc.py b/test/lint/check-doc.py index feaebc68e9..d22dd9d996 100755 --- a/test/lint/check-doc.py +++ b/test/lint/check-doc.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2020 The Bitcoin Core developers +# Copyright (c) 2015-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. @@ -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/commit-script-check.sh b/test/lint/commit-script-check.sh index 9449b393f1..55c9528dea 100755 --- a/test/lint/commit-script-check.sh +++ b/test/lint/commit-script-check.sh @@ -1,5 +1,5 @@ #!/bin/sh -# Copyright (c) 2017-2021 The Bitcoin Core developers +# Copyright (c) 2017-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. diff --git a/test/lint/lint-assertions.py b/test/lint/lint-assertions.py index 195ff33d11..e7eecebce5 100755 --- a/test/lint/lint-assertions.py +++ b/test/lint/lint-assertions.py @@ -12,7 +12,7 @@ import subprocess def git_grep(params: [], error_msg: ""): try: - output = subprocess.check_output(["git", "grep", *params], universal_newlines=True, encoding="utf8") + output = subprocess.check_output(["git", "grep", *params], text=True, encoding="utf8") print(error_msg) print(output) return 1 diff --git a/test/lint/lint-circular-dependencies.py b/test/lint/lint-circular-dependencies.py index b69bbe7cd0..cf6a5f81f1 100755 --- a/test/lint/lint-circular-dependencies.py +++ b/test/lint/lint-circular-dependencies.py @@ -38,14 +38,14 @@ def main(): os.chdir(CODE_DIR) files = subprocess.check_output( ['git', 'ls-files', '--', '*.h', '*.cpp'], - universal_newlines=True, + text=True, ).splitlines() command = [sys.executable, "../contrib/devtools/circular-dependencies.py", *files] dependencies_output = subprocess.run( command, stdout=subprocess.PIPE, - universal_newlines=True, + text=True, ) for dependency_str in dependencies_output.stdout.rstrip().split("\n"): diff --git a/test/lint/lint-files.py b/test/lint/lint-files.py index 123bee2cbc..f2b5db681b 100755 --- a/test/lint/lint-files.py +++ b/test/lint/lint-files.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2021 The Bitcoin Core developers +# Copyright (c) 2021-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. diff --git a/test/lint/lint-git-commit-check.py b/test/lint/lint-git-commit-check.py index a1d03370e8..5897a17e70 100755 --- a/test/lint/lint-git-commit-check.py +++ b/test/lint/lint-git-commit-check.py @@ -42,15 +42,17 @@ def main(): commit_range = "HEAD~" + args.prev_commits + "...HEAD" else: # This assumes that the target branch of the pull request will be master. - merge_base = check_output(["git", "merge-base", "HEAD", "master"], universal_newlines=True, encoding="utf8").rstrip("\n") + merge_base = check_output(["git", "merge-base", "HEAD", "master"], text=True, encoding="utf8").rstrip("\n") commit_range = merge_base + "..HEAD" else: commit_range = os.getenv("COMMIT_RANGE") + if commit_range == "SKIP_EMPTY_NOT_A_PR": + sys.exit(0) - commit_hashes = check_output(["git", "log", commit_range, "--format=%H"], universal_newlines=True, encoding="utf8").splitlines() + commit_hashes = check_output(["git", "log", commit_range, "--format=%H"], text=True, encoding="utf8").splitlines() for hash in commit_hashes: - commit_info = check_output(["git", "log", "--format=%B", "-n", "1", hash], universal_newlines=True, encoding="utf8").splitlines() + commit_info = check_output(["git", "log", "--format=%B", "-n", "1", hash], text=True, encoding="utf8").splitlines() if len(commit_info) >= 2: if commit_info[1]: print(f"The subject line of commit hash {hash} is followed by a non-empty line. Subject lines should always be followed by a blank line.") diff --git a/test/lint/lint-includes.py b/test/lint/lint-includes.py index b3fa4b9303..459030bb0b 100755 --- a/test/lint/lint-includes.py +++ b/test/lint/lint-includes.py @@ -35,13 +35,13 @@ EXPECTED_BOOST_INCLUDES = ["boost/date_time/posix_time/posix_time.hpp", def get_toplevel(): - return check_output(["git", "rev-parse", "--show-toplevel"], universal_newlines=True, encoding="utf8").rstrip("\n") + return check_output(["git", "rev-parse", "--show-toplevel"], text=True, encoding="utf8").rstrip("\n") def list_files_by_suffix(suffixes): exclude_args = [":(exclude)" + dir for dir in EXCLUDED_DIRS] - files_list = check_output(["git", "ls-files", "src"] + exclude_args, universal_newlines=True, encoding="utf8").splitlines() + files_list = check_output(["git", "ls-files", "src"] + exclude_args, text=True, encoding="utf8").splitlines() return [file for file in files_list if file.endswith(suffixes)] @@ -63,7 +63,7 @@ def find_included_cpps(): included_cpps = list() try: - included_cpps = check_output(["git", "grep", "-E", r"^#include [<\"][^>\"]+\.cpp[>\"]", "--", "*.cpp", "*.h"], universal_newlines=True, encoding="utf8").splitlines() + included_cpps = check_output(["git", "grep", "-E", r"^#include [<\"][^>\"]+\.cpp[>\"]", "--", "*.cpp", "*.h"], text=True, encoding="utf8").splitlines() except CalledProcessError as e: if e.returncode > 1: raise e @@ -77,7 +77,7 @@ def find_extra_boosts(): exclusion_set = set() try: - included_boosts = check_output(["git", "grep", "-E", r"^#include <boost/", "--", "*.cpp", "*.h"], universal_newlines=True, encoding="utf8").splitlines() + included_boosts = check_output(["git", "grep", "-E", r"^#include <boost/", "--", "*.cpp", "*.h"], text=True, encoding="utf8").splitlines() except CalledProcessError as e: if e.returncode > 1: raise e @@ -100,7 +100,7 @@ def find_quote_syntax_inclusions(): quote_syntax_inclusions = list() try: - quote_syntax_inclusions = check_output(["git", "grep", r"^#include \"", "--", "*.cpp", "*.h"] + exclude_args, universal_newlines=True, encoding="utf8").splitlines() + quote_syntax_inclusions = check_output(["git", "grep", r"^#include \"", "--", "*.cpp", "*.h"] + exclude_args, text=True, encoding="utf8").splitlines() except CalledProcessError as e: if e.returncode > 1: raise e @@ -143,13 +143,13 @@ def main(): if extra_boosts: for boost in extra_boosts: print(f"A new Boost dependency in the form of \"{boost}\" appears to have been introduced:") - print(check_output(["git", "grep", boost, "--", "*.cpp", "*.h"], universal_newlines=True, encoding="utf8")) + print(check_output(["git", "grep", boost, "--", "*.cpp", "*.h"], text=True, encoding="utf8")) exit_code = 1 # Check if Boost dependencies are no longer used for expected_boost in EXPECTED_BOOST_INCLUDES: try: - check_output(["git", "grep", "-q", r"^#include <%s>" % expected_boost, "--", "*.cpp", "*.h"], universal_newlines=True, encoding="utf8") + check_output(["git", "grep", "-q", r"^#include <%s>" % expected_boost, "--", "*.cpp", "*.h"], text=True, encoding="utf8") except CalledProcessError as e: if e.returncode > 1: raise e diff --git a/test/lint/lint-locale-dependence.py b/test/lint/lint-locale-dependence.py index ce7444cd1a..c5cb34b20a 100755 --- a/test/lint/lint-locale-dependence.py +++ b/test/lint/lint-locale-dependence.py @@ -223,7 +223,7 @@ def find_locale_dependent_function_uses(): git_grep_output = list() try: - git_grep_output = check_output(git_grep_command, universal_newlines=True, encoding="utf8").splitlines() + git_grep_output = check_output(git_grep_command, text=True, encoding="utf8").splitlines() except CalledProcessError as e: if e.returncode > 1: raise e diff --git a/test/lint/lint-logs.py b/test/lint/lint-logs.py index aaf697467d..de04a1aeca 100755 --- a/test/lint/lint-logs.py +++ b/test/lint/lint-logs.py @@ -16,7 +16,7 @@ from subprocess import check_output def main(): - logs_list = check_output(["git", "grep", "--extended-regexp", r"(LogPrintLevel|LogPrintfCategory|LogPrintf?)\(", "--", "*.cpp"], universal_newlines=True, encoding="utf8").splitlines() + logs_list = check_output(["git", "grep", "--extended-regexp", r"(LogPrintLevel|LogPrintfCategory|LogPrintf?)\(", "--", "*.cpp"], text=True, encoding="utf8").splitlines() unterminated_logs = [line for line in logs_list if not re.search(r'(\\n"|/\* Continued \*/)', line)] diff --git a/test/lint/lint-python-mutable-default-parameters.py b/test/lint/lint-python-mutable-default-parameters.py index 7991e3630b..820595ea34 100755 --- a/test/lint/lint-python-mutable-default-parameters.py +++ b/test/lint/lint-python-mutable-default-parameters.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (c) 2019 The Bitcoin Core developers +# Copyright (c) 2019-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. @@ -21,7 +21,7 @@ def main(): "--", "*.py", ] - output = subprocess.run(command, stdout=subprocess.PIPE, universal_newlines=True) + output = subprocess.run(command, stdout=subprocess.PIPE, text=True) if len(output.stdout) > 0: error_msg = ( "A mutable list or dict seems to be used as default parameter value:\n\n" diff --git a/test/lint/lint-python-utf8-encoding.py b/test/lint/lint-python-utf8-encoding.py index 62fdc34d50..364da63468 100755 --- a/test/lint/lint-python-utf8-encoding.py +++ b/test/lint/lint-python-utf8-encoding.py @@ -23,7 +23,7 @@ def check_fileopens(): fileopens = list() try: - fileopens = check_output(["git", "grep", r" open(", "--", "*.py"] + get_exclude_args(), universal_newlines=True, encoding="utf8").splitlines() + fileopens = check_output(["git", "grep", r" open(", "--", "*.py"] + get_exclude_args(), text=True, encoding="utf8").splitlines() except CalledProcessError as e: if e.returncode > 1: raise e @@ -37,12 +37,12 @@ def check_checked_outputs(): checked_outputs = list() try: - checked_outputs = check_output(["git", "grep", "check_output(", "--", "*.py"] + get_exclude_args(), universal_newlines=True, encoding="utf8").splitlines() + checked_outputs = check_output(["git", "grep", "check_output(", "--", "*.py"] + get_exclude_args(), text=True, encoding="utf8").splitlines() except CalledProcessError as e: if e.returncode > 1: raise e - filtered_checked_outputs = [checked_output for checked_output in checked_outputs if re.search(r"universal_newlines=True", checked_output) and not re.search(r"encoding=.(ascii|utf8|utf-8).", checked_output)] + filtered_checked_outputs = [checked_output for checked_output in checked_outputs if re.search(r"text=True", checked_output) and not re.search(r"encoding=.(ascii|utf8|utf-8).", checked_output)] return filtered_checked_outputs diff --git a/test/lint/lint-shell.py b/test/lint/lint-shell.py index ed95024ef5..1646bf0d3e 100755 --- a/test/lint/lint-shell.py +++ b/test/lint/lint-shell.py @@ -25,7 +25,7 @@ def check_shellcheck_install(): sys.exit(0) def get_files(command): - output = subprocess.run(command, stdout=subprocess.PIPE, universal_newlines=True) + output = subprocess.run(command, stdout=subprocess.PIPE, text=True) files = output.stdout.split('\n') # remove whitespace element diff --git a/test/lint/lint-spelling.py b/test/lint/lint-spelling.py index 14d7d13a75..ac0bddeaa6 100755 --- a/test/lint/lint-spelling.py +++ b/test/lint/lint-spelling.py @@ -12,7 +12,7 @@ Note: Will exit successfully regardless of spelling errors. from subprocess import check_output, STDOUT, CalledProcessError IGNORE_WORDS_FILE = 'test/lint/spelling.ignore-words.txt' -FILES_ARGS = ['git', 'ls-files', '--', ":(exclude)build-aux/m4/", ":(exclude)contrib/seeds/*.txt", ":(exclude)depends/", ":(exclude)doc/release-notes/", ":(exclude)src/leveldb/", ":(exclude)src/crc32c/", ":(exclude)src/qt/locale/", ":(exclude)src/qt/*.qrc", ":(exclude)src/secp256k1/", ":(exclude)src/minisketch/", ":(exclude)contrib/builder-keys/keys.txt", ":(exclude)contrib/guix/patches"] +FILES_ARGS = ['git', 'ls-files', '--', ":(exclude)build-aux/m4/", ":(exclude)contrib/seeds/*.txt", ":(exclude)depends/", ":(exclude)doc/release-notes/", ":(exclude)src/leveldb/", ":(exclude)src/crc32c/", ":(exclude)src/qt/locale/", ":(exclude)src/qt/*.qrc", ":(exclude)src/secp256k1/", ":(exclude)src/minisketch/", ":(exclude)contrib/guix/patches"] def check_codespell_install(): diff --git a/test/lint/lint-submodule.py b/test/lint/lint-submodule.py index 89d4c80f55..4d2fbf088f 100755 --- a/test/lint/lint-submodule.py +++ b/test/lint/lint-submodule.py @@ -13,7 +13,7 @@ import sys def main(): submodules_list = subprocess.check_output(['git', 'submodule', 'status', '--recursive'], - universal_newlines = True, encoding = 'utf8').rstrip('\n') + text = True, encoding = 'utf8').rstrip('\n') if submodules_list: print("These submodules were found, delete them:\n", submodules_list) sys.exit(1) diff --git a/test/lint/lint-tests.py b/test/lint/lint-tests.py index 849ddcb961..1eeb7bb014 100755 --- a/test/lint/lint-tests.py +++ b/test/lint/lint-tests.py @@ -23,7 +23,7 @@ def grep_boost_fixture_test_suite(): "src/test/**.cpp", "src/wallet/test/**.cpp", ] - return subprocess.check_output(command, universal_newlines=True, encoding="utf8") + return subprocess.check_output(command, text=True, encoding="utf8") def check_matching_test_names(test_suite_list): diff --git a/test/lint/lint-whitespace.py b/test/lint/lint-whitespace.py index 3fb5b80013..f5e4a776d0 100755 --- a/test/lint/lint-whitespace.py +++ b/test/lint/lint-whitespace.py @@ -80,7 +80,7 @@ def get_diff(commit_range, check_only_code): else: what_files = ["."] - diff = check_output(["git", "diff", "-U0", commit_range, "--"] + what_files + exclude_args, universal_newlines=True, encoding="utf8") + diff = check_output(["git", "diff", "-U0", commit_range, "--"] + what_files + exclude_args, text=True, encoding="utf8") return diff @@ -93,10 +93,12 @@ def main(): commit_range = "HEAD~" + args.prev_commits + "...HEAD" else: # This assumes that the target branch of the pull request will be master. - merge_base = check_output(["git", "merge-base", "HEAD", "master"], universal_newlines=True, encoding="utf8").rstrip("\n") + merge_base = check_output(["git", "merge-base", "HEAD", "master"], text=True, encoding="utf8").rstrip("\n") commit_range = merge_base + "..HEAD" else: commit_range = os.getenv("COMMIT_RANGE") + if commit_range == "SKIP_EMPTY_NOT_A_PR": + sys.exit(0) whitespace_selection = [] tab_selection = [] diff --git a/test/lint/run-lint-format-strings.py b/test/lint/run-lint-format-strings.py index b814446125..57eefb00f2 100755 --- a/test/lint/run-lint-format-strings.py +++ b/test/lint/run-lint-format-strings.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (c) 2018-2019 The Bitcoin Core developers +# Copyright (c) 2018-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. # 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 diff --git a/test/util/test_runner.py b/test/util/test_runner.py index 03db05c563..ea3626fa65 100755 --- a/test/util/test_runner.py +++ b/test/util/test_runner.py @@ -107,7 +107,7 @@ def bctest(testDir, testObj, buildenv): raise Exception # Run the test - proc = subprocess.Popen(execrun, stdin=stdinCfg, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True) + proc = subprocess.Popen(execrun, stdin=stdinCfg, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) try: outs = proc.communicate(input=inputData) except OSError: |