diff options
Diffstat (limited to 'test/functional')
101 files changed, 1052 insertions, 559 deletions
diff --git a/test/functional/README.md b/test/functional/README.md index 2764acbf18..2d04413eb2 100644 --- a/test/functional/README.md +++ b/test/functional/README.md @@ -23,7 +23,7 @@ don't have test cases for. - The oldest supported Python version is specified in [doc/dependencies.md](/doc/dependencies.md). Consider using [pyenv](https://github.com/pyenv/pyenv), which checks [.python-version](/.python-version), to prevent accidentally introducing modern syntax from an unsupported Python version. - The Travis linter also checks this, but [possibly not in all cases](https://github.com/bitcoin/bitcoin/pull/14884#discussion_r239585126). + The CI linter job also checks this, but [possibly not in all cases](https://github.com/bitcoin/bitcoin/pull/14884#discussion_r239585126). - See [the python lint script](/test/lint/lint-python.sh) that checks for violations that could lead to bugs and issues in the test code. - Use [type hints](https://docs.python.org/3/library/typing.html) in your code to improve code readability diff --git a/test/functional/example_test.py b/test/functional/example_test.py index c28bb7115f..97f24e1b6e 100755 --- a/test/functional/example_test.py +++ b/test/functional/example_test.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2017-2019 The Bitcoin Core developers +# Copyright (c) 2017-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. """An example functional test diff --git a/test/functional/feature_bip68_sequence.py b/test/functional/feature_bip68_sequence.py index 60492350ee..6c5857c5ce 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-2019 The Bitcoin Core developers +# Copyright (c) 2014-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 BIP68 implementation.""" diff --git a/test/functional/feature_cltv.py b/test/functional/feature_cltv.py index aad255c4a9..b7c2887ee8 100755 --- a/test/functional/feature_cltv.py +++ b/test/functional/feature_cltv.py @@ -126,8 +126,13 @@ class BIP65Test(BitcoinTestFramework): # First we show that this tx is valid except for CLTV by getting it # rejected from the mempool for exactly that reason. assert_equal( - [{'txid': spendtx.hash, 'allowed': False, 'reject-reason': 'non-mandatory-script-verify-flag (Negative locktime)'}], - self.nodes[0].testmempoolaccept(rawtxs=[spendtx.serialize().hex()], maxfeerate=0) + [{ + 'txid': spendtx.hash, + 'wtxid': spendtx.getwtxid(), + 'allowed': False, + 'reject-reason': 'non-mandatory-script-verify-flag (Negative locktime)', + }], + self.nodes[0].testmempoolaccept(rawtxs=[spendtx.serialize().hex()], maxfeerate=0), ) # Now we verify that a block with this transaction is also invalid. diff --git a/test/functional/feature_config_args.py b/test/functional/feature_config_args.py index 3e28dae4b3..2445b6d977 100755 --- a/test/functional/feature_config_args.py +++ b/test/functional/feature_config_args.py @@ -7,6 +7,7 @@ import os from test_framework.test_framework import BitcoinTestFramework +from test_framework import util class ConfArgsTest(BitcoinTestFramework): @@ -42,10 +43,11 @@ class ConfArgsTest(BitcoinTestFramework): conf.write("wallet=foo\n") self.nodes[0].assert_start_raises_init_error(expected_msg='Error: Config setting for -wallet only applied on %s network when in [%s] section.' % (self.chain, self.chain)) + main_conf_file_path = os.path.join(self.options.tmpdir, 'node0', 'bitcoin_main.conf') + util.write_config(main_conf_file_path, n=0, chain='', extra_config='includeconf={}\n'.format(inc_conf_file_path)) with open(inc_conf_file_path, 'w', encoding='utf-8') as conf: - conf.write('regtest=0\n') # mainnet conf.write('acceptnonstdtxn=1\n') - self.nodes[0].assert_start_raises_init_error(expected_msg='Error: acceptnonstdtxn is not currently supported for main chain') + self.nodes[0].assert_start_raises_init_error(extra_args=["-conf={}".format(main_conf_file_path)], expected_msg='Error: acceptnonstdtxn is not currently supported for main chain') with open(inc_conf_file_path, 'w', encoding='utf-8') as conf: conf.write('nono\n') diff --git a/test/functional/feature_dbcrash.py b/test/functional/feature_dbcrash.py index 7a2e35c095..f9ece244fb 100755 --- a/test/functional/feature_dbcrash.py +++ b/test/functional/feature_dbcrash.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2017-2019 The Bitcoin Core developers +# Copyright (c) 2017-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 recovery from a crash during chainstate writing. diff --git a/test/functional/feature_dersig.py b/test/functional/feature_dersig.py index 3f7efdbded..3b430139b1 100755 --- a/test/functional/feature_dersig.py +++ b/test/functional/feature_dersig.py @@ -112,8 +112,13 @@ class BIP66Test(BitcoinTestFramework): # First we show that this tx is valid except for DERSIG by getting it # rejected from the mempool for exactly that reason. assert_equal( - [{'txid': spendtx.hash, 'allowed': False, 'reject-reason': 'non-mandatory-script-verify-flag (Non-canonical DER signature)'}], - self.nodes[0].testmempoolaccept(rawtxs=[spendtx.serialize().hex()], maxfeerate=0) + [{ + 'txid': spendtx.hash, + 'wtxid': spendtx.getwtxid(), + 'allowed': False, + 'reject-reason': 'non-mandatory-script-verify-flag (Non-canonical DER signature)', + }], + self.nodes[0].testmempoolaccept(rawtxs=[spendtx.serialize().hex()], maxfeerate=0), ) # Now we verify that a block with this transaction is also invalid. diff --git a/test/functional/feature_fee_estimation.py b/test/functional/feature_fee_estimation.py index 8a8a0c7614..8f522aee66 100755 --- a/test/functional/feature_fee_estimation.py +++ b/test/functional/feature_fee_estimation.py @@ -13,6 +13,7 @@ from test_framework.util import ( assert_equal, assert_greater_than, assert_greater_than_or_equal, + assert_raises_rpc_error, satoshi_round, ) @@ -262,6 +263,11 @@ class EstimateFeeTest(BitcoinTestFramework): self.log.info("Final estimates after emptying mempools") check_estimates(self.nodes[1], self.fees_per_kb) + self.log.info("Testing that fee estimation is disabled in blocksonly.") + self.restart_node(0, ["-blocksonly"]) + assert_raises_rpc_error(-32603, "Fee estimation disabled", + self.nodes[0].estimatesmartfee, 2) + if __name__ == '__main__': EstimateFeeTest().main() diff --git a/test/functional/feature_help.py b/test/functional/feature_help.py index 9f18413255..babe9bfc80 100755 --- a/test/functional/feature_help.py +++ b/test/functional/feature_help.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2018-2019 The Bitcoin Core developers +# Copyright (c) 2018-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. """Verify that starting bitcoin with -h works as expected.""" diff --git a/test/functional/feature_minchainwork.py b/test/functional/feature_minchainwork.py index abf87e8f0c..803feb509a 100755 --- a/test/functional/feature_minchainwork.py +++ b/test/functional/feature_minchainwork.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2017-2018 The Bitcoin Core developers +# Copyright (c) 2017-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 logic for setting nMinimumChainWork on command line. diff --git a/test/functional/feature_notifications.py b/test/functional/feature_notifications.py index 47bc8dbb49..f2313bac13 100755 --- a/test/functional/feature_notifications.py +++ b/test/functional/feature_notifications.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2014-2019 The Bitcoin Core developers +# Copyright (c) 2014-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 the -alertnotify, -blocknotify and -walletnotify options.""" diff --git a/test/functional/feature_nulldummy.py b/test/functional/feature_nulldummy.py index b0eac7056b..bdbfa5aed1 100755 --- a/test/functional/feature_nulldummy.py +++ b/test/functional/feature_nulldummy.py @@ -60,7 +60,7 @@ class NULLDUMMYTest(BitcoinTestFramework): self.wit_address = w0.getnewaddress(address_type='p2sh-segwit') self.wit_ms_address = wmulti.addmultisigaddress(1, [self.pubkey], '', 'p2sh-segwit')['address'] if not self.options.descriptors: - # Legacy wallets need to import these so that they are watched by the wallet. This is unnecssary (and does not need to be tested) for descriptor wallets + # Legacy wallets need to import these so that they are watched by the wallet. This is unnecessary (and does not need to be tested) for descriptor wallets wmulti.importaddress(self.ms_address) wmulti.importaddress(self.wit_ms_address) diff --git a/test/functional/feature_proxy.py b/test/functional/feature_proxy.py index 05b658ed87..cd5eff9184 100755 --- a/test/functional/feature_proxy.py +++ b/test/functional/feature_proxy.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2019 The Bitcoin Core developers +# Copyright (c) 2015-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 bitcoind with different proxy configuration. diff --git a/test/functional/feature_rbf.py b/test/functional/feature_rbf.py index 1b531ad51d..c6f55c62b4 100755 --- a/test/functional/feature_rbf.py +++ b/test/functional/feature_rbf.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2014-2019 The Bitcoin Core developers +# Copyright (c) 2014-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 the RBF code.""" diff --git a/test/functional/feature_reindex.py b/test/functional/feature_reindex.py index 31cea8d1b7..68585b7475 100755 --- a/test/functional/feature_reindex.py +++ b/test/functional/feature_reindex.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2014-2018 The Bitcoin Core developers +# Copyright (c) 2014-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 running bitcoind with -reindex and -reindex-chainstate options. diff --git a/test/functional/feature_shutdown.py b/test/functional/feature_shutdown.py index a76e0f1b50..291df4c518 100755 --- a/test/functional/feature_shutdown.py +++ b/test/functional/feature_shutdown.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2018-2019 The Bitcoin Core developers +# Copyright (c) 2018-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 bitcoind shutdown.""" diff --git a/test/functional/feature_taproot.py b/test/functional/feature_taproot.py index 0f0fe8a34a..6ee2b72c11 100755 --- a/test/functional/feature_taproot.py +++ b/test/functional/feature_taproot.py @@ -444,6 +444,8 @@ def make_spender(comment, *, tap=None, witv0=False, script=None, pkh=None, p2sh= * standard: whether the (valid version of) spending is expected to be standard * err_msg: a string with an expected error message for failure (or None, if not cared about) * sigops_weight: the pre-taproot sigops weight consumed by a successful spend + * need_vin_vout_mismatch: whether this test requires being tested in a transaction input that has no corresponding + transaction output. """ conf = dict() @@ -1442,6 +1444,10 @@ class TaprootTest(BitcoinTestFramework): self.nodes[1].generate(101) self.test_spenders(self.nodes[1], spenders_taproot_active(), input_counts=[1, 2, 2, 2, 2, 3]) + # Re-connect nodes in case they have been disconnected + self.disconnect_nodes(0, 1) + self.connect_nodes(0, 1) + # Transfer value of the largest 500 coins to pre-taproot node. addr = self.nodes[0].getnewaddress() diff --git a/test/functional/feature_versionbits_warning.py b/test/functional/feature_versionbits_warning.py index 2e4f4796b0..062a7affb5 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-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 version bits warning system. diff --git a/test/functional/interface_zmq.py b/test/functional/interface_zmq.py index d675ae174c..946bfa51d4 100755 --- a/test/functional/interface_zmq.py +++ b/test/functional/interface_zmq.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2019 The Bitcoin Core developers +# Copyright (c) 2015-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 the ZMQ notification interface.""" @@ -80,34 +80,43 @@ class ZMQTest (BitcoinTestFramework): self.log.debug("Destroying ZMQ context") self.ctx.destroy(linger=None) + # Restart node with the specified zmq notifications enabled, subscribe to + # all of them and return the corresponding ZMQSubscriber objects. + def setup_zmq_test(self, services, recv_timeout=60, connect_nodes=False): + subscribers = [] + for topic, address in services: + socket = self.ctx.socket(zmq.SUB) + socket.set(zmq.RCVTIMEO, recv_timeout*1000) + subscribers.append(ZMQSubscriber(socket, topic.encode())) + + self.restart_node(0, ["-zmqpub%s=%s" % (topic, address) for topic, address in services]) + + if connect_nodes: + self.connect_nodes(0, 1) + + for i, sub in enumerate(subscribers): + sub.socket.connect(services[i][1]) + + # Relax so that the subscribers are ready before publishing zmq messages + sleep(0.2) + + return subscribers + def test_basic(self): # 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' - sockets = [] - subs = [] - services = [b"hashblock", b"hashtx", b"rawblock", b"rawtx"] - for service in services: - sockets.append(self.ctx.socket(zmq.SUB)) - sockets[-1].set(zmq.RCVTIMEO, 60000) - subs.append(ZMQSubscriber(sockets[-1], service)) - - # Subscribe to all available topics. + subs = self.setup_zmq_test( + [(topic, address) for topic in ["hashblock", "hashtx", "rawblock", "rawtx"]], + connect_nodes=True) + hashblock = subs[0] hashtx = subs[1] rawblock = subs[2] rawtx = subs[3] - self.restart_node(0, ["-zmqpub%s=%s" % (sub.topic.decode(), address) for sub in [hashblock, hashtx, rawblock, rawtx]]) - self.connect_nodes(0, 1) - for socket in sockets: - socket.connect(address) - - # Relax so that the subscriber is ready before publishing zmq messages - sleep(0.2) - num_blocks = 5 self.log.info("Generate %(n)d blocks (and %(n)d coinbase txes)" % {"n": num_blocks}) genhashes = self.nodes[0].generatetoaddress(num_blocks, ADDRESS_BCRT1_UNSPENDABLE) @@ -174,25 +183,10 @@ class ZMQTest (BitcoinTestFramework): address = 'tcp://127.0.0.1:28333' - services = [b"hashblock", b"hashtx"] - sockets = [] - subs = [] - for service in services: - sockets.append(self.ctx.socket(zmq.SUB)) - # 2 second timeout to check end of notifications - sockets[-1].set(zmq.RCVTIMEO, 2000) - subs.append(ZMQSubscriber(sockets[-1], service)) - - # Subscribe to all available topics. - hashblock = subs[0] - hashtx = subs[1] - # Should only notify the tip if a reorg occurs - self.restart_node(0, ["-zmqpub%s=%s" % (sub.topic.decode(), address) for sub in [hashblock, hashtx]]) - for socket in sockets: - socket.connect(address) - # Relax so that the subscriber is ready before publishing zmq messages - sleep(0.2) + hashblock, hashtx = self.setup_zmq_test( + [(topic, address) for topic in ["hashblock", "hashtx"]], + recv_timeout=2) # 2 second timeout to check end of notifications # Generate 1 block in nodes[0] with 1 mempool tx and receive all notifications payment_txid = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 1.0) @@ -240,15 +234,7 @@ class ZMQTest (BitcoinTestFramework): <32-byte hash>A<8-byte LE uint> : Transactionhash added mempool """ self.log.info("Testing 'sequence' publisher") - address = 'tcp://127.0.0.1:28333' - socket = self.ctx.socket(zmq.SUB) - socket.set(zmq.RCVTIMEO, 60000) - seq = ZMQSubscriber(socket, b'sequence') - - self.restart_node(0, ['-zmqpub%s=%s' % (seq.topic.decode(), address)]) - socket.connect(address) - # Relax so that the subscriber is ready before publishing zmq messages - sleep(0.2) + [seq] = self.setup_zmq_test([("sequence", "tcp://127.0.0.1:28333")]) # Mempool sequence number starts at 1 seq_num = 1 @@ -399,16 +385,7 @@ class ZMQTest (BitcoinTestFramework): return self.log.info("Testing 'mempool sync' usage of sequence notifier") - address = 'tcp://127.0.0.1:28333' - socket = self.ctx.socket(zmq.SUB) - socket.set(zmq.RCVTIMEO, 60000) - seq = ZMQSubscriber(socket, b'sequence') - - self.restart_node(0, ['-zmqpub%s=%s' % (seq.topic.decode(), address)]) - self.connect_nodes(0, 1) - socket.connect(address) - # Relax so that the subscriber is ready before publishing zmq messages - sleep(0.2) + [seq] = self.setup_zmq_test([("sequence", "tcp://127.0.0.1:28333")], connect_nodes=True) # In-memory counter, should always start at 1 next_mempool_seq = self.nodes[0].getrawmempool(mempool_sequence=True)["mempool_sequence"] @@ -508,26 +485,17 @@ class ZMQTest (BitcoinTestFramework): def test_multiple_interfaces(self): # Set up two subscribers with different addresses - subscribers = [] - for i in range(2): - address = 'tcp://127.0.0.1:%d' % (28334 + i) - socket = self.ctx.socket(zmq.SUB) - socket.set(zmq.RCVTIMEO, 60000) - hashblock = ZMQSubscriber(socket, b"hashblock") - socket.connect(address) - subscribers.append({'address': address, 'hashblock': hashblock}) - - self.restart_node(0, ['-zmqpub%s=%s' % (subscriber['hashblock'].topic.decode(), subscriber['address']) for subscriber in subscribers]) - - # Relax so that the subscriber is ready before publishing zmq messages - sleep(0.2) + subscribers = self.setup_zmq_test([ + ("hashblock", "tcp://127.0.0.1:28334"), + ("hashblock", "tcp://127.0.0.1:28335"), + ]) # Generate 1 block in nodes[0] and receive all notifications self.nodes[0].generatetoaddress(1, ADDRESS_BCRT1_UNSPENDABLE) # Should receive the same block hash on both subscribers - assert_equal(self.nodes[0].getbestblockhash(), subscribers[0]['hashblock'].receive().hex()) - assert_equal(self.nodes[0].getbestblockhash(), subscribers[1]['hashblock'].receive().hex()) + assert_equal(self.nodes[0].getbestblockhash(), subscribers[0].receive().hex()) + assert_equal(self.nodes[0].getbestblockhash(), subscribers[1].receive().hex()) if __name__ == '__main__': ZMQTest().main() diff --git a/test/functional/mempool_accept.py b/test/functional/mempool_accept.py index fc4c775668..c4002f524a 100755 --- a/test/functional/mempool_accept.py +++ b/test/functional/mempool_accept.py @@ -51,6 +51,8 @@ class MempoolAcceptanceTest(BitcoinTestFramework): def check_mempool_result(self, result_expected, *args, **kwargs): """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 assert_equal(result_expected, result_test) assert_equal(self.nodes[0].getmempoolinfo()['size'], self.mempool_size) # Must not change mempool state diff --git a/test/functional/mempool_compatibility.py b/test/functional/mempool_compatibility.py index 8ac91bd008..eb08765ebf 100755 --- a/test/functional/mempool_compatibility.py +++ b/test/functional/mempool_compatibility.py @@ -16,15 +16,15 @@ Only v0.15.2 is required by this test. The rest is used in other backwards compa import os from test_framework.test_framework import BitcoinTestFramework +from test_framework.wallet import MiniWallet class MempoolCompatibilityTest(BitcoinTestFramework): def set_test_params(self): self.num_nodes = 2 - self.wallet_names = [None, self.default_wallet_name] + self.wallet_names = [None] def skip_test_if_missing_module(self): - self.skip_if_no_wallet() self.skip_if_no_previous_releases() def setup_network(self): @@ -38,8 +38,15 @@ class MempoolCompatibilityTest(BitcoinTestFramework): def run_test(self): self.log.info("Test that mempool.dat is compatible between versions") - old_node = self.nodes[0] - new_node = self.nodes[1] + old_node, new_node = self.nodes + new_wallet = MiniWallet(new_node) + new_wallet.generate(1) + new_node.generate(100) + # Sync the nodes to ensure old_node has the block that contains the coinbase that new_wallet will spend. + # Otherwise, because coinbases are only valid in a block and not as loose txns, if the nodes aren't synced + # 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) @@ -58,7 +65,7 @@ class MempoolCompatibilityTest(BitcoinTestFramework): assert old_tx_hash in new_node.getrawmempool() self.log.info("Add unbroadcasted tx to mempool on new node and shutdown") - unbroadcasted_tx_hash = new_node.sendtoaddress(recipient, 0.0001) + unbroadcasted_tx_hash = new_wallet.send_self_transfer(from_node=new_node)['txid'] assert unbroadcasted_tx_hash in new_node.getrawmempool() mempool = new_node.getrawmempool(True) assert mempool[unbroadcasted_tx_hash]['unbroadcast'] diff --git a/test/functional/mempool_expiry.py b/test/functional/mempool_expiry.py index 8b9b7b155a..4c46075ae9 100755 --- a/test/functional/mempool_expiry.py +++ b/test/functional/mempool_expiry.py @@ -16,8 +16,8 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.util import ( assert_equal, assert_raises_rpc_error, - find_vout_for_address, ) +from test_framework.wallet import MiniWallet DEFAULT_MEMPOOL_EXPIRY = 336 # hours CUSTOM_MEMPOOL_EXPIRY = 10 # hours @@ -26,44 +26,50 @@ CUSTOM_MEMPOOL_EXPIRY = 10 # hours class MempoolExpiryTest(BitcoinTestFramework): def set_test_params(self): self.num_nodes = 1 - - def skip_test_if_missing_module(self): - self.skip_if_no_wallet() + 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.wallet.generate(4) + node.generate(100) # Send a parent transaction that will expire. - parent_address = node.getnewaddress() - parent_txid = node.sendtoaddress(parent_address, 1.0) + parent_txid = self.wallet.send_self_transfer(from_node=node)['txid'] + parent_utxo = self.wallet.get_utxo(txid=parent_txid) + independent_utxo = self.wallet.get_utxo() + + # Ensure the transactions we send to trigger the mempool check spend utxos that are independent of + # the transactions being tested for expiration. + trigger_utxo1 = self.wallet.get_utxo() + trigger_utxo2 = self.wallet.get_utxo() # Set the mocktime to the arrival time of the parent transaction. entry_time = node.getmempoolentry(parent_txid)['time'] node.setmocktime(entry_time) - # Create child transaction spending the parent transaction - vout = find_vout_for_address(node, parent_txid, parent_address) - inputs = [{'txid': parent_txid, 'vout': vout}] - outputs = {node.getnewaddress(): 0.99} - child_raw = node.createrawtransaction(inputs, outputs) - child_signed = node.signrawtransactionwithwallet(child_raw)['hex'] - - # Let half of the timeout elapse and broadcast the child transaction. + # Let half of the timeout elapse and broadcast the child transaction spending the parent transaction. half_expiry_time = entry_time + int(60 * 60 * timeout/2) node.setmocktime(half_expiry_time) - child_txid = node.sendrawtransaction(child_signed) + child_txid = self.wallet.send_self_transfer(from_node=node, utxo_to_spend=parent_utxo)['txid'] + assert_equal(parent_txid, node.getmempoolentry(child_txid)['depends'][0]) self.log.info('Broadcast child transaction after {} hours.'.format( timedelta(seconds=(half_expiry_time-entry_time)))) + # Broadcast another (independent) transaction. + independent_txid = self.wallet.send_self_transfer(from_node=node, utxo_to_spend=independent_utxo)['txid'] + # Let most of the timeout elapse and check that the parent tx is still # in the mempool. nearly_expiry_time = entry_time + 60 * 60 * timeout - 5 node.setmocktime(nearly_expiry_time) - # Expiry of mempool transactions is only checked when a new transaction - # is added to the to the mempool. - node.sendtoaddress(node.getnewaddress(), 1.0) + # Broadcast a transaction as the expiry of transactions in the mempool is only checked + # when a new transaction is added to the mempool. + self.wallet.send_self_transfer(from_node=node, utxo_to_spend=trigger_utxo1) self.log.info('Test parent tx not expired after {} hours.'.format( timedelta(seconds=(nearly_expiry_time-entry_time)))) assert_equal(entry_time, node.getmempoolentry(parent_txid)['time']) @@ -72,9 +78,8 @@ class MempoolExpiryTest(BitcoinTestFramework): # has passed. expiry_time = entry_time + 60 * 60 * timeout + 5 node.setmocktime(expiry_time) - # Expiry of mempool transactions is only checked when a new transaction - # is added to the to the mempool. - node.sendtoaddress(node.getnewaddress(), 1.0) + # Again, broadcast a transaction so the expiry of transactions in the mempool is checked. + self.wallet.send_self_transfer(from_node=node, utxo_to_spend=trigger_utxo2) self.log.info('Test parent tx expiry after {} hours.'.format( timedelta(seconds=(expiry_time-entry_time)))) assert_raises_rpc_error(-5, 'Transaction not in mempool', @@ -85,6 +90,11 @@ class MempoolExpiryTest(BitcoinTestFramework): assert_raises_rpc_error(-5, 'Transaction not in mempool', node.getmempoolentry, child_txid) + # Check that the independent tx is still in the mempool. + self.log.info('Test the independent tx not expired after {} hours.'.format( + timedelta(seconds=(expiry_time-half_expiry_time)))) + assert_equal(half_expiry_time, node.getmempoolentry(independent_txid)['time']) + def run_test(self): self.log.info('Test default mempool expiry timeout of %d hours.' % DEFAULT_MEMPOOL_EXPIRY) diff --git a/test/functional/mempool_package_onemore.py b/test/functional/mempool_package_onemore.py index e956fe07d2..a9e2b000fb 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-2019 The Bitcoin Core developers +# Copyright (c) 2014-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 descendant package tracking carve-out allowing one final transaction in diff --git a/test/functional/mempool_packages.py b/test/functional/mempool_packages.py index d7cb7db9f8..461f9237ff 100755 --- a/test/functional/mempool_packages.py +++ b/test/functional/mempool_packages.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2014-2019 The Bitcoin Core developers +# Copyright (c) 2014-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 descendant package tracking code.""" diff --git a/test/functional/mempool_resurrect.py b/test/functional/mempool_resurrect.py index 187c9026f6..4aa58270b6 100755 --- a/test/functional/mempool_resurrect.py +++ b/test/functional/mempool_resurrect.py @@ -1,69 +1,62 @@ #!/usr/bin/env python3 -# Copyright (c) 2014-2019 The Bitcoin Core developers +# Copyright (c) 2014-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 resurrection of mined transactions when the blockchain is re-organized.""" -from test_framework.blocktools import create_raw_transaction from test_framework.test_framework import BitcoinTestFramework from test_framework.util import assert_equal +from test_framework.wallet import MiniWallet class MempoolCoinbaseTest(BitcoinTestFramework): def set_test_params(self): self.num_nodes = 1 - - def skip_test_if_missing_module(self): - self.skip_if_no_wallet() + self.setup_clean_chain = True def run_test(self): - node0_address = self.nodes[0].getnewaddress() + node = self.nodes[0] + wallet = MiniWallet(node) + + # Add enough mature utxos to the wallet so that all txs spend confirmed coins + wallet.generate(3) + node.generate(100) + # Spend block 1/2/3's coinbase transactions - # Mine a block. + # Mine a block # Create three more transactions, spending the spends - # Mine another block. + # Mine another block # ... make sure all the transactions are confirmed # Invalidate both blocks # ... make sure all the transactions are put back in the mempool # Mine a new block - # ... make sure all the transactions are confirmed again. - - b = [self.nodes[0].getblockhash(n) for n in range(1, 4)] - coinbase_txids = [self.nodes[0].getblock(h)['tx'][0] for h in b] - spends1_raw = [create_raw_transaction(self.nodes[0], txid, node0_address, amount=49.99) for txid in coinbase_txids] - spends1_id = [self.nodes[0].sendrawtransaction(tx) for tx in spends1_raw] - + # ... make sure all the transactions are confirmed again blocks = [] - blocks.extend(self.nodes[0].generate(1)) - - spends2_raw = [create_raw_transaction(self.nodes[0], txid, node0_address, amount=49.98) for txid in spends1_id] - spends2_id = [self.nodes[0].sendrawtransaction(tx) for tx in spends2_raw] + spends1_ids = [wallet.send_self_transfer(from_node=node)['txid'] for _ in range(3)] + blocks.extend(node.generate(1)) + spends2_ids = [wallet.send_self_transfer(from_node=node)['txid'] for _ in range(3)] + blocks.extend(node.generate(1)) - blocks.extend(self.nodes[0].generate(1)) + spends_ids = set(spends1_ids + spends2_ids) # mempool should be empty, all txns confirmed - assert_equal(set(self.nodes[0].getrawmempool()), set()) - for txid in spends1_id+spends2_id: - tx = self.nodes[0].gettransaction(txid) - assert tx["confirmations"] > 0 + assert_equal(set(node.getrawmempool()), set()) + confirmed_txns = set(node.getblock(blocks[0])['tx'] + node.getblock(blocks[1])['tx']) + # Checks that all spend txns are contained in the mined blocks + assert spends_ids < confirmed_txns # Use invalidateblock to re-org back - for node in self.nodes: - node.invalidateblock(blocks[0]) + node.invalidateblock(blocks[0]) # All txns should be back in mempool with 0 confirmations - assert_equal(set(self.nodes[0].getrawmempool()), set(spends1_id+spends2_id)) - for txid in spends1_id+spends2_id: - tx = self.nodes[0].gettransaction(txid) - assert tx["confirmations"] == 0 + assert_equal(set(node.getrawmempool()), spends_ids) # Generate another block, they should all get mined - self.nodes[0].generate(1) + blocks = node.generate(1) # mempool should be empty, all txns confirmed - assert_equal(set(self.nodes[0].getrawmempool()), set()) - for txid in spends1_id+spends2_id: - tx = self.nodes[0].gettransaction(txid) - assert tx["confirmations"] > 0 + assert_equal(set(node.getrawmempool()), set()) + confirmed_txns = set(node.getblock(blocks[0])['tx']) + assert spends_ids < confirmed_txns if __name__ == '__main__': diff --git a/test/functional/mempool_spend_coinbase.py b/test/functional/mempool_spend_coinbase.py index 86d382ff69..a249a73315 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-2018 The Bitcoin Core developers +# Copyright (c) 2014-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 spending coinbase transactions. diff --git a/test/functional/mining_getblocktemplate_longpoll.py b/test/functional/mining_getblocktemplate_longpoll.py index 2adafb1fdb..cc32f78e2e 100755 --- a/test/functional/mining_getblocktemplate_longpoll.py +++ b/test/functional/mining_getblocktemplate_longpoll.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2014-2019 The Bitcoin Core developers +# Copyright (c) 2014-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 longpolling with getblocktemplate.""" diff --git a/test/functional/p2p_add_connections.py b/test/functional/p2p_add_connections.py new file mode 100755 index 0000000000..a63c3a3287 --- /dev/null +++ b/test/functional/p2p_add_connections.py @@ -0,0 +1,97 @@ +#!/usr/bin/env python3 +# Copyright (c) 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 add_outbound_p2p_connection test framework functionality""" + +from test_framework.p2p import P2PInterface +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import assert_equal + + +def check_node_connections(*, node, num_in, num_out): + info = node.getnetworkinfo() + assert_equal(info["connections_in"], num_in) + assert_equal(info["connections_out"], num_out) + + +class P2PAddConnections(BitcoinTestFramework): + def set_test_params(self): + self.setup_clean_chain = False + self.num_nodes = 2 + + def setup_network(self): + self.setup_nodes() + # Don't connect the nodes + + def run_test(self): + self.log.info("Add 8 outbounds to node 0") + for i in range(8): + self.log.info(f"outbound: {i}") + self.nodes[0].add_outbound_p2p_connection(P2PInterface(), p2p_idx=i, connection_type="outbound-full-relay") + + self.log.info("Add 2 block-relay-only connections to node 0") + for i in range(2): + self.log.info(f"block-relay-only: {i}") + # set p2p_idx based on the outbound connections already open to the + # node, so add 8 to account for the previous full-relay connections + self.nodes[0].add_outbound_p2p_connection(P2PInterface(), p2p_idx=i + 8, connection_type="block-relay-only") + + self.log.info("Add 2 block-relay-only connections to node 1") + for i in range(2): + self.log.info(f"block-relay-only: {i}") + self.nodes[1].add_outbound_p2p_connection(P2PInterface(), p2p_idx=i, connection_type="block-relay-only") + + self.log.info("Add 5 inbound connections to node 1") + for i in range(5): + self.log.info(f"inbound: {i}") + self.nodes[1].add_p2p_connection(P2PInterface()) + + self.log.info("Add 8 outbounds to node 1") + for i in range(8): + self.log.info(f"outbound: {i}") + # bump p2p_idx to account for the 2 existing outbounds on node 1 + self.nodes[1].add_outbound_p2p_connection(P2PInterface(), p2p_idx=i + 2) + + self.log.info("Check the connections opened as expected") + check_node_connections(node=self.nodes[0], num_in=0, num_out=10) + check_node_connections(node=self.nodes[1], num_in=5, num_out=10) + + self.log.info("Disconnect p2p connections & try to re-open") + self.nodes[0].disconnect_p2ps() + check_node_connections(node=self.nodes[0], num_in=0, num_out=0) + + self.log.info("Add 8 outbounds to node 0") + for i in range(8): + self.log.info(f"outbound: {i}") + self.nodes[0].add_outbound_p2p_connection(P2PInterface(), p2p_idx=i) + check_node_connections(node=self.nodes[0], num_in=0, num_out=8) + + self.log.info("Add 2 block-relay-only connections to node 0") + for i in range(2): + self.log.info(f"block-relay-only: {i}") + # bump p2p_idx to account for the 8 existing outbounds on node 0 + self.nodes[0].add_outbound_p2p_connection(P2PInterface(), p2p_idx=i + 8, connection_type="block-relay-only") + check_node_connections(node=self.nodes[0], num_in=0, num_out=10) + + self.log.info("Restart node 0 and try to reconnect to p2ps") + self.restart_node(0) + + self.log.info("Add 4 outbounds to node 0") + for i in range(4): + self.log.info(f"outbound: {i}") + self.nodes[0].add_outbound_p2p_connection(P2PInterface(), p2p_idx=i) + check_node_connections(node=self.nodes[0], num_in=0, num_out=4) + + self.log.info("Add 2 block-relay-only connections to node 0") + for i in range(2): + self.log.info(f"block-relay-only: {i}") + # bump p2p_idx to account for the 4 existing outbounds on node 0 + self.nodes[0].add_outbound_p2p_connection(P2PInterface(), p2p_idx=i + 4, connection_type="block-relay-only") + check_node_connections(node=self.nodes[0], num_in=0, num_out=6) + + check_node_connections(node=self.nodes[1], num_in=5, num_out=10) + + +if __name__ == '__main__': + P2PAddConnections().main() diff --git a/test/functional/p2p_addr_relay.py b/test/functional/p2p_addr_relay.py index 80f262d0d3..91fbd722cf 100755 --- a/test/functional/p2p_addr_relay.py +++ b/test/functional/p2p_addr_relay.py @@ -19,8 +19,11 @@ from test_framework.util import ( ) import time +# Keep this with length <= 10. Addresses from larger messages are not relayed. ADDRS = [] -for i in range(10): +num_ipv4_addrs = 10 + +for i in range(num_ipv4_addrs): addr = CAddress() addr.time = int(time.time()) + i addr.nServices = NODE_NETWORK | NODE_WITNESS @@ -30,11 +33,15 @@ for i in range(10): class AddrReceiver(P2PInterface): + num_ipv4_received = 0 + def on_addr(self, message): for addr in message.addrs: assert_equal(addr.nServices, 9) + if not 8333 <= addr.port < 8343: + raise AssertionError("Invalid addr.port of {} (8333-8342 expected)".format(addr.port)) assert addr.ip.startswith('123.123.123.') - assert (8333 <= addr.port < 8343) + self.num_ipv4_received += 1 class AddrTest(BitcoinTestFramework): @@ -48,21 +55,33 @@ class AddrTest(BitcoinTestFramework): msg = msg_addr() self.log.info('Send too-large addr message') - msg.addrs = ADDRS * 101 + msg.addrs = ADDRS * 101 # more than 1000 addresses in one message with self.nodes[0].assert_debug_log(['addr message size = 1010']): addr_source.send_and_ping(msg) self.log.info('Check that addr message content is relayed and added to addrman') - addr_receiver = self.nodes[0].add_p2p_connection(AddrReceiver()) + num_receivers = 7 + receivers = [] + for _ in range(num_receivers): + receivers.append(self.nodes[0].add_p2p_connection(AddrReceiver())) msg.addrs = ADDRS - with self.nodes[0].assert_debug_log([ - 'Added 10 addresses from 127.0.0.1: 0 tried', + with self.nodes[0].assert_debug_log( + [ + 'Added {} addresses from 127.0.0.1: 0 tried'.format(num_ipv4_addrs), 'received: addr (301 bytes) peer=0', - 'sending addr (301 bytes) peer=1', - ]): + ] + ): addr_source.send_and_ping(msg) self.nodes[0].setmocktime(int(time.time()) + 30 * 60) - addr_receiver.sync_with_ping() + for receiver in receivers: + receiver.sync_with_ping() + + total_ipv4_received = sum(r.num_ipv4_received for r in receivers) + + # Every IPv4 address must be relayed to two peers, other than the + # originating node (addr_source). + ipv4_branching_factor = 2 + assert_equal(total_ipv4_received, num_ipv4_addrs * ipv4_branching_factor) if __name__ == '__main__': diff --git a/test/functional/p2p_blockfilters.py b/test/functional/p2p_blockfilters.py index 3250cbecf9..03662babef 100755 --- a/test/functional/p2p_blockfilters.py +++ b/test/functional/p2p_blockfilters.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2019 The Bitcoin Core developers +# Copyright (c) 2019-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. """Tests NODE_COMPACT_FILTERS (BIP 157/158). diff --git a/test/functional/p2p_blocksonly.py b/test/functional/p2p_blocksonly.py index e80422d1cf..c592ab52b1 100755 --- a/test/functional/p2p_blocksonly.py +++ b/test/functional/p2p_blocksonly.py @@ -1,11 +1,14 @@ #!/usr/bin/env python3 -# Copyright (c) 2019 The Bitcoin Core developers +# Copyright (c) 2019-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 p2p blocksonly""" +"""Test p2p blocksonly mode & block-relay-only connections.""" -from test_framework.messages import msg_tx, CTransaction, FromHex -from test_framework.p2p import P2PInterface +import time + +from test_framework.blocktools import create_transaction +from test_framework.messages import msg_tx +from test_framework.p2p import P2PInterface, P2PTxInvStore from test_framework.test_framework import BitcoinTestFramework from test_framework.util import assert_equal @@ -16,50 +19,32 @@ class P2PBlocksOnly(BitcoinTestFramework): self.num_nodes = 1 self.extra_args = [["-blocksonly"]] + def skip_test_if_missing_module(self): + self.skip_if_no_wallet() + def run_test(self): - block_relay_peer = self.nodes[0].add_p2p_connection(P2PInterface()) - - self.log.info('Check that txs from p2p are rejected and result in disconnect') - prevtx = self.nodes[0].getblock(self.nodes[0].getblockhash(1), 2)['tx'][0] - rawtx = self.nodes[0].createrawtransaction( - inputs=[{ - 'txid': prevtx['txid'], - 'vout': 0 - }], - outputs=[{ - self.nodes[0].get_deterministic_priv_key().address: 50 - 0.00125 - }], - ) - sigtx = self.nodes[0].signrawtransactionwithkey( - hexstring=rawtx, - privkeys=[self.nodes[0].get_deterministic_priv_key().key], - prevtxs=[{ - 'txid': prevtx['txid'], - 'vout': 0, - 'scriptPubKey': prevtx['vout'][0]['scriptPubKey']['hex'], - }], - )['hex'] + self.blocksonly_mode_tests() + self.blocks_relay_conn_tests() + + def blocksonly_mode_tests(self): + self.log.info("Tests with node running in -blocksonly mode") assert_equal(self.nodes[0].getnetworkinfo()['localrelay'], False) - with self.nodes[0].assert_debug_log(['transaction sent in violation of protocol peer=0']): - block_relay_peer.send_message(msg_tx(FromHex(CTransaction(), sigtx))) - block_relay_peer.wait_for_disconnect() - assert_equal(self.nodes[0].getmempoolinfo()['size'], 0) - # Remove the disconnected peer and add a new one. - del self.nodes[0].p2ps[0] - tx_relay_peer = self.nodes[0].add_p2p_connection(P2PInterface()) + self.nodes[0].add_p2p_connection(P2PInterface()) + tx, txid, tx_hex = self.check_p2p_tx_violation() self.log.info('Check that txs from rpc are not rejected and relayed to other peers') + tx_relay_peer = self.nodes[0].add_p2p_connection(P2PInterface()) assert_equal(self.nodes[0].getpeerinfo()[0]['relaytxes'], True) - txid = self.nodes[0].testmempoolaccept([sigtx])[0]['txid'] + + assert_equal(self.nodes[0].testmempoolaccept([tx_hex])[0]['allowed'], True) with self.nodes[0].assert_debug_log(['received getdata for: wtx {} peer=1'.format(txid)]): - self.nodes[0].sendrawtransaction(sigtx) + self.nodes[0].sendrawtransaction(tx_hex) tx_relay_peer.wait_for_tx(txid) assert_equal(self.nodes[0].getmempoolinfo()['size'], 1) - self.log.info('Check that txs from peers with relay-permission are not rejected and relayed to others') self.log.info("Restarting node 0 with relay permission and blocksonly") - self.restart_node(0, ["-persistmempool=0", "-whitelist=relay@127.0.0.1", "-blocksonly", '-deprecatedrpc=whitelisted']) + self.restart_node(0, ["-persistmempool=0", "-whitelist=relay@127.0.0.1", "-blocksonly"]) assert_equal(self.nodes[0].getrawmempool(), []) first_peer = self.nodes[0].add_p2p_connection(P2PInterface()) second_peer = self.nodes[0].add_p2p_connection(P2PInterface()) @@ -67,8 +52,7 @@ class P2PBlocksOnly(BitcoinTestFramework): assert_equal(peer_1_info['permissions'], ['relay']) peer_2_info = self.nodes[0].getpeerinfo()[1] assert_equal(peer_2_info['permissions'], ['relay']) - assert_equal(self.nodes[0].testmempoolaccept([sigtx])[0]['allowed'], True) - txid = self.nodes[0].testmempoolaccept([sigtx])[0]['txid'] + assert_equal(self.nodes[0].testmempoolaccept([tx_hex])[0]['allowed'], True) self.log.info('Check that the tx from first_peer with relay-permission is relayed to others (ie.second_peer)') with self.nodes[0].assert_debug_log(["received getdata"]): @@ -78,13 +62,58 @@ class P2PBlocksOnly(BitcoinTestFramework): # But if, for some reason, first_peer decides to relay transactions to us anyway, we should relay them to # second_peer since we gave relay permission to first_peer. # See https://github.com/bitcoin/bitcoin/issues/19943 for details. - first_peer.send_message(msg_tx(FromHex(CTransaction(), sigtx))) + first_peer.send_message(msg_tx(tx)) self.log.info('Check that the peer with relay-permission is still connected after sending the transaction') assert_equal(first_peer.is_connected, True) second_peer.wait_for_tx(txid) assert_equal(self.nodes[0].getmempoolinfo()['size'], 1) self.log.info("Relay-permission peer's transaction is accepted and relayed") + self.nodes[0].disconnect_p2ps() + self.nodes[0].generate(1) + + def blocks_relay_conn_tests(self): + self.log.info('Tests with node in normal mode with block-relay-only connections') + self.restart_node(0, ["-noblocksonly"]) # disables blocks only mode + assert_equal(self.nodes[0].getnetworkinfo()['localrelay'], True) + + # Ensure we disconnect if a block-relay-only connection sends us a transaction + self.nodes[0].add_outbound_p2p_connection(P2PInterface(), p2p_idx=0, connection_type="block-relay-only") + assert_equal(self.nodes[0].getpeerinfo()[0]['relaytxes'], False) + _, txid, tx_hex = self.check_p2p_tx_violation(index=2) + + self.log.info("Check that txs from RPC are not sent to blockrelay connection") + conn = self.nodes[0].add_outbound_p2p_connection(P2PTxInvStore(), p2p_idx=1, connection_type="block-relay-only") + + self.nodes[0].sendrawtransaction(tx_hex) + + # Bump time forward to ensure nNextInvSend timer pops + self.nodes[0].setmocktime(int(time.time()) + 60) + + # Calling sync_with_ping twice requires that the node calls + # `ProcessMessage` twice, and thus ensures `SendMessages` must have + # been called at least once + conn.sync_with_ping() + conn.sync_with_ping() + assert(int(txid, 16) not in conn.get_invs()) + + def check_p2p_tx_violation(self, index=1): + self.log.info('Check that txs from P2P are rejected and result in disconnect') + input_txid = self.nodes[0].getblock(self.nodes[0].getblockhash(index), 2)['tx'][0]['txid'] + tx = create_transaction(self.nodes[0], input_txid, self.nodes[0].getnewaddress(), amount=(50 - 0.001)) + txid = tx.rehash() + tx_hex = tx.serialize().hex() + + with self.nodes[0].assert_debug_log(['transaction sent in violation of protocol peer=0']): + self.nodes[0].p2ps[0].send_message(msg_tx(tx)) + self.nodes[0].p2ps[0].wait_for_disconnect() + assert_equal(self.nodes[0].getmempoolinfo()['size'], 0) + + # Remove the disconnected peer + del self.nodes[0].p2ps[0] + + return tx, txid, tx_hex + if __name__ == '__main__': P2PBlocksOnly().main() diff --git a/test/functional/p2p_compactblocks.py b/test/functional/p2p_compactblocks.py index 611bffb25f..55573efc06 100755 --- a/test/functional/p2p_compactblocks.py +++ b/test/functional/p2p_compactblocks.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 compact blocks (BIP 152). @@ -764,6 +764,34 @@ class CompactBlocksTest(BitcoinTestFramework): stalling_peer.send_and_ping(msg) assert_equal(int(node.getbestblockhash(), 16), block.sha256) + def test_highbandwidth_mode_states_via_getpeerinfo(self): + # create new p2p connection for a fresh state w/o any prior sendcmpct messages sent + hb_test_node = self.nodes[0].add_p2p_connection(TestP2PConn(cmpct_version=2)) + + # assert the RPC getpeerinfo boolean fields `bip152_hb_{to, from}` + # match the given parameters for the last peer of a given node + def assert_highbandwidth_states(node, hb_to, hb_from): + peerinfo = node.getpeerinfo()[-1] + assert_equal(peerinfo['bip152_hb_to'], hb_to) + assert_equal(peerinfo['bip152_hb_from'], hb_from) + + # initially, neither node has selected the other peer as high-bandwidth yet + assert_highbandwidth_states(self.nodes[0], hb_to=False, hb_from=False) + + # peer requests high-bandwidth mode by sending sendcmpct(1) + hb_test_node.send_and_ping(msg_sendcmpct(announce=True, version=2)) + assert_highbandwidth_states(self.nodes[0], hb_to=False, hb_from=True) + + # peer generates a block and sends it to node, which should + # select the peer as high-bandwidth (up to 3 peers according to BIP 152) + block = self.build_block_on_tip(self.nodes[0]) + hb_test_node.send_and_ping(msg_block(block)) + assert_highbandwidth_states(self.nodes[0], hb_to=True, hb_from=True) + + # peer requests low-bandwidth mode by sending sendcmpct(0) + hb_test_node.send_and_ping(msg_sendcmpct(announce=False, version=2)) + assert_highbandwidth_states(self.nodes[0], hb_to=True, hb_from=False) + def run_test(self): # Get the nodes out of IBD self.nodes[0].generate(1) @@ -822,6 +850,9 @@ class CompactBlocksTest(BitcoinTestFramework): self.log.info("Testing invalid index in cmpctblock message...") self.test_invalid_cmpctblock_message() + self.log.info("Testing high-bandwidth mode states via getpeerinfo...") + self.test_highbandwidth_mode_states_via_getpeerinfo() + if __name__ == '__main__': CompactBlocksTest().main() diff --git a/test/functional/p2p_disconnect_ban.py b/test/functional/p2p_disconnect_ban.py index a7f51b5364..fb8529ad2b 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-2019 The Bitcoin Core developers +# Copyright (c) 2014-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 node disconnect and ban behavior""" diff --git a/test/functional/p2p_eviction.py b/test/functional/p2p_eviction.py index 72a255991c..d60aa5b383 100755 --- a/test/functional/p2p_eviction.py +++ b/test/functional/p2p_eviction.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2019 The Bitcoin Core developers +# Copyright (c) 2019-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. diff --git a/test/functional/p2p_feefilter.py b/test/functional/p2p_feefilter.py index 51b7623fe3..a2a122b352 100755 --- a/test/functional/p2p_feefilter.py +++ b/test/functional/p2p_feefilter.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 processing of feefilter messages.""" diff --git a/test/functional/p2p_fingerprint.py b/test/functional/p2p_fingerprint.py index f60eba2dbf..469d66a851 100755 --- a/test/functional/p2p_fingerprint.py +++ b/test/functional/p2p_fingerprint.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2017-2018 The Bitcoin Core developers +# Copyright (c) 2017-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 various fingerprinting protections. @@ -98,9 +98,9 @@ class P2PFingerprintTest(BitcoinTestFramework): # Longest chain is extended so stale is much older than chain tip self.nodes[0].setmocktime(0) - self.nodes[0].generatetoaddress(1, self.nodes[0].get_deterministic_priv_key().address) + block_hash = int(self.nodes[0].generatetoaddress(1, self.nodes[0].get_deterministic_priv_key().address)[-1], 16) assert_equal(self.nodes[0].getblockcount(), 14) - node0.sync_with_ping() + node0.wait_for_block(block_hash, timeout=3) # Request for very old stale block should now fail with p2p_lock: @@ -128,5 +128,6 @@ class P2PFingerprintTest(BitcoinTestFramework): self.send_header_request(block_hash, node0) node0.wait_for_header(hex(block_hash), timeout=3) + if __name__ == '__main__': P2PFingerprintTest().main() diff --git a/test/functional/p2p_invalid_messages.py b/test/functional/p2p_invalid_messages.py index db72a361d9..c0b3c2cb12 100755 --- a/test/functional/p2p_invalid_messages.py +++ b/test/functional/p2p_invalid_messages.py @@ -18,6 +18,7 @@ from test_framework.messages import ( msg_inv, msg_ping, MSG_TX, + msg_version, ser_string, ) from test_framework.p2p import ( @@ -60,6 +61,7 @@ class InvalidMessagesTest(BitcoinTestFramework): def run_test(self): self.test_buffer() + self.test_duplicate_version_msg() self.test_magic_bytes() self.test_checksum() self.test_size() @@ -92,6 +94,13 @@ class InvalidMessagesTest(BitcoinTestFramework): conn.sync_with_ping(timeout=1) self.nodes[0].disconnect_p2ps() + def test_duplicate_version_msg(self): + self.log.info("Test duplicate version message is ignored") + conn = self.nodes[0].add_p2p_connection(P2PDataStore()) + with self.nodes[0].assert_debug_log(['redundant version message from peer']): + conn.send_and_ping(msg_version()) + self.nodes[0].disconnect_p2ps() + def test_magic_bytes(self): self.log.info("Test message with invalid magic bytes disconnects peer") conn = self.nodes[0].add_p2p_connection(P2PDataStore()) diff --git a/test/functional/p2p_invalid_tx.py b/test/functional/p2p_invalid_tx.py index 489d903c21..cca7390ae3 100755 --- a/test/functional/p2p_invalid_tx.py +++ b/test/functional/p2p_invalid_tx.py @@ -143,7 +143,7 @@ class InvalidTxRequestTest(BitcoinTestFramework): } # Transactions that do not end up in the mempool # tx_orphan_no_fee, because it has too low fee (p2ps[0] is not disconnected for relaying that tx) - # tx_orphan_invaid, because it has negative fee (p2ps[1] is disconnected for relaying that tx) + # tx_orphan_invalid, because it has negative fee (p2ps[1] is disconnected for relaying that tx) self.wait_until(lambda: 1 == len(node.getpeerinfo()), timeout=12) # p2ps[1] is no longer connected assert_equal(expected_mempool, set(node.getrawmempool())) diff --git a/test/functional/p2p_leak.py b/test/functional/p2p_leak.py index 4b32d60db0..ca8bf908a9 100755 --- a/test/functional/p2p_leak.py +++ b/test/functional/p2p_leak.py @@ -24,8 +24,6 @@ from test_framework.util import ( assert_greater_than_or_equal, ) -DISCOURAGEMENT_THRESHOLD = 100 - class LazyPeer(P2PInterface): def __init__(self): @@ -93,27 +91,16 @@ class P2PLeakTest(BitcoinTestFramework): self.num_nodes = 1 def run_test(self): - # Peer that never sends a version. We will send a bunch of messages - # from this peer anyway and verify eventual disconnection. - no_version_disconnect_peer = self.nodes[0].add_p2p_connection( - LazyPeer(), send_version=False, wait_for_verack=False) - # Another peer that never sends a version, nor any other messages. It shouldn't receive anything from the node. no_version_idle_peer = self.nodes[0].add_p2p_connection(LazyPeer(), send_version=False, wait_for_verack=False) # Peer that sends a version but not a verack. no_verack_idle_peer = self.nodes[0].add_p2p_connection(NoVerackIdlePeer(), wait_for_verack=False) - # Send enough ping messages (any non-version message will do) prior to sending - # version to reach the peer discouragement threshold. This should get us disconnected. - for _ in range(DISCOURAGEMENT_THRESHOLD): - no_version_disconnect_peer.send_message(msg_ping()) - # Wait until we got the verack in response to the version. Though, don't wait for the node to receive the # verack, since we never sent one no_verack_idle_peer.wait_for_verack() - no_version_disconnect_peer.wait_until(lambda: no_version_disconnect_peer.ever_connected, check_connected=False) no_version_idle_peer.wait_until(lambda: no_version_idle_peer.ever_connected) no_verack_idle_peer.wait_until(lambda: no_verack_idle_peer.version_received) @@ -123,13 +110,9 @@ class P2PLeakTest(BitcoinTestFramework): #Give the node enough time to possibly leak out a message time.sleep(5) - # Expect this peer to be disconnected for misbehavior - assert not no_version_disconnect_peer.is_connected - self.nodes[0].disconnect_p2ps() # Make sure no unexpected messages came in - assert no_version_disconnect_peer.unexpected_msg == False assert no_version_idle_peer.unexpected_msg == False assert no_verack_idle_peer.unexpected_msg == False @@ -148,7 +131,7 @@ class P2PLeakTest(BitcoinTestFramework): p2p_old_peer = self.nodes[0].add_p2p_connection(P2PInterface(), send_version=False, wait_for_verack=False) old_version_msg = msg_version() old_version_msg.nVersion = 31799 - with self.nodes[0].assert_debug_log(['peer=4 using obsolete version 31799; disconnecting']): + with self.nodes[0].assert_debug_log(['peer=3 using obsolete version 31799; disconnecting']): p2p_old_peer.send_message(old_version_msg) p2p_old_peer.wait_for_disconnect() diff --git a/test/functional/p2p_nobloomfilter_messages.py b/test/functional/p2p_nobloomfilter_messages.py index c2311cb197..507a71b2a9 100755 --- a/test/functional/p2p_nobloomfilter_messages.py +++ b/test/functional/p2p_nobloomfilter_messages.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2018 The Bitcoin Core developers +# Copyright (c) 2015-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 invalid p2p messages for nodes with bloom filters disabled. diff --git a/test/functional/p2p_node_network_limited.py b/test/functional/p2p_node_network_limited.py index b1a7ef6877..8d95f155c8 100755 --- a/test/functional/p2p_node_network_limited.py +++ b/test/functional/p2p_node_network_limited.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2017-2019 The Bitcoin Core developers +# Copyright (c) 2017-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. """Tests NODE_NETWORK_LIMITED. diff --git a/test/functional/p2p_permissions.py b/test/functional/p2p_permissions.py index ed82e6a2e2..62652d949d 100755 --- a/test/functional/p2p_permissions.py +++ b/test/functional/p2p_permissions.py @@ -38,35 +38,24 @@ class P2PPermissionsTests(BitcoinTestFramework): # default permissions (no specific permissions) ["-whitelist=127.0.0.1"], # Make sure the default values in the command line documentation match the ones here - ["relay", "noban", "mempool", "download"], - True) - - self.checkpermission( - # check without deprecatedrpc=whitelisted - ["-whitelist=127.0.0.1"], - # Make sure the default values in the command line documentation match the ones here - ["relay", "noban", "mempool", "download"], - None) + ["relay", "noban", "mempool", "download"]) self.checkpermission( # no permission (even with forcerelay) ["-whitelist=@127.0.0.1", "-whitelistforcerelay=1"], - [], - False) + []) self.checkpermission( # relay permission removed (no specific permissions) ["-whitelist=127.0.0.1", "-whitelistrelay=0"], - ["noban", "mempool", "download"], - True) + ["noban", "mempool", "download"]) self.checkpermission( # forcerelay and relay permission added # Legacy parameter interaction which set whitelistrelay to true # if whitelistforcerelay is true ["-whitelist=127.0.0.1", "-whitelistforcerelay"], - ["forcerelay", "relay", "noban", "mempool", "download"], - True) + ["forcerelay", "relay", "noban", "mempool", "download"]) # Let's make sure permissions are merged correctly # For this, we need to use whitebind instead of bind @@ -76,39 +65,28 @@ class P2PPermissionsTests(BitcoinTestFramework): self.checkpermission( ["-whitelist=noban@127.0.0.1"], # Check parameter interaction forcerelay should activate relay - ["noban", "bloomfilter", "forcerelay", "relay", "download"], - False) + ["noban", "bloomfilter", "forcerelay", "relay", "download"]) self.replaceinconfig(1, "whitebind=bloomfilter,forcerelay@" + ip_port, "bind=127.0.0.1") self.checkpermission( # legacy whitelistrelay should be ignored ["-whitelist=noban,mempool@127.0.0.1", "-whitelistrelay"], - ["noban", "mempool", "download"], - False) - - self.checkpermission( - # check without deprecatedrpc=whitelisted - ["-whitelist=noban,mempool@127.0.0.1", "-whitelistrelay"], - ["noban", "mempool", "download"], - None) + ["noban", "mempool", "download"]) self.checkpermission( # legacy whitelistforcerelay should be ignored ["-whitelist=noban,mempool@127.0.0.1", "-whitelistforcerelay"], - ["noban", "mempool", "download"], - False) + ["noban", "mempool", "download"]) self.checkpermission( # missing mempool permission to be considered legacy whitelisted ["-whitelist=noban@127.0.0.1"], - ["noban", "download"], - False) + ["noban", "download"]) self.checkpermission( # all permission added ["-whitelist=all@127.0.0.1"], - ["forcerelay", "noban", "mempool", "bloomfilter", "relay", "download", "addr"], - False) + ["forcerelay", "noban", "mempool", "bloomfilter", "relay", "download", "addr"]) self.stop_node(1) self.nodes[1].assert_start_raises_init_error(["-whitelist=oopsie@127.0.0.1"], "Invalid P2P permission", match=ErrorMatch.PARTIAL_REGEX) @@ -169,19 +147,13 @@ class P2PPermissionsTests(BitcoinTestFramework): reject_reason='Not relaying non-mempool transaction {} from forcerelay peer=0'.format(txid) ) - def checkpermission(self, args, expectedPermissions, whitelisted): - if whitelisted is not None: - args = [*args, '-deprecatedrpc=whitelisted'] + def checkpermission(self, args, expectedPermissions): self.restart_node(1, args) self.connect_nodes(0, 1) peerinfo = self.nodes[1].getpeerinfo()[0] - if whitelisted is None: - assert 'whitelisted' not in peerinfo - else: - assert_equal(peerinfo['whitelisted'], whitelisted) assert_equal(len(expectedPermissions), len(peerinfo['permissions'])) for p in expectedPermissions: - if not p in peerinfo['permissions']: + if p not in peerinfo['permissions']: raise AssertionError("Expected permissions %r is not granted." % p) def replaceinconfig(self, nodeid, old, new): diff --git a/test/functional/p2p_segwit.py b/test/functional/p2p_segwit.py index e99ecd8026..54891b07e1 100755 --- a/test/functional/p2p_segwit.py +++ b/test/functional/p2p_segwit.py @@ -37,7 +37,6 @@ from test_framework.messages import ( msg_tx, msg_block, msg_no_witness_tx, - msg_verack, ser_uint256, ser_vector, sha256, @@ -146,7 +145,7 @@ def test_witness_block(node, p2p, block, accepted, with_witness=True, reason=Non class TestP2PConn(P2PInterface): def __init__(self, wtxidrelay=False): - super().__init__() + super().__init__(wtxidrelay=wtxidrelay) self.getdataset = set() self.last_wtxidrelay = [] self.lastgetdata = [] @@ -157,13 +156,6 @@ class TestP2PConn(P2PInterface): def on_inv(self, message): pass - def on_version(self, message): - if self.wtxidrelay: - super().on_version(message) - else: - self.send_message(msg_verack()) - self.nServices = message.nServices - def on_getdata(self, message): self.lastgetdata = message.inv for inv in message.inv: @@ -694,13 +686,35 @@ 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()]), [{'txid': tx3.hash, 'allowed': True, 'vsize': tx3.get_vsize(), 'fees': { 'base': Decimal('0.00001000')}}]) + assert_equal( + self.nodes[0].testmempoolaccept([tx3.serialize_with_witness().hex()]), + [{ + 'txid': tx3.hash, + 'wtxid': tx3.getwtxid(), + 'allowed': True, + 'vsize': tx3.get_vsize(), + 'fees': { + 'base': Decimal('0.00001000'), + }, + }], + ) # Create the same output as tx3, but by replacing tx tx3_out = tx3.vout[0] tx3 = tx tx3.vout = [tx3_out] tx3.rehash() - assert_equal(self.nodes[0].testmempoolaccept([tx3.serialize_with_witness().hex()]), [{'txid': tx3.hash, 'allowed': True, 'vsize': tx3.get_vsize(), 'fees': { 'base': Decimal('0.00011000')}}]) + assert_equal( + self.nodes[0].testmempoolaccept([tx3.serialize_with_witness().hex()]), + [{ + 'txid': tx3.hash, + 'wtxid': tx3.getwtxid(), + 'allowed': True, + 'vsize': tx3.get_vsize(), + 'fees': { + 'base': Decimal('0.00011000'), + }, + }], + ) test_transaction_acceptance(self.nodes[0], self.test_node, tx3, with_witness=True, accepted=True) self.nodes[0].generate(1) diff --git a/test/functional/p2p_timeouts.py b/test/functional/p2p_timeouts.py index ce12ce26ce..47832b04bf 100755 --- a/test/functional/p2p_timeouts.py +++ b/test/functional/p2p_timeouts.py @@ -57,8 +57,10 @@ class TimeoutsTest(BitcoinTestFramework): assert no_version_node.is_connected assert no_send_node.is_connected - no_verack_node.send_message(msg_ping()) - no_version_node.send_message(msg_ping()) + with self.nodes[0].assert_debug_log(['Unsupported message "ping" prior to verack from peer=0']): + no_verack_node.send_message(msg_ping()) + with self.nodes[0].assert_debug_log(['non-version message before version handshake. Message "ping" from peer=1']): + no_version_node.send_message(msg_ping()) sleep(1) diff --git a/test/functional/p2p_tx_download.py b/test/functional/p2p_tx_download.py index 16d9302db8..8a751c6b54 100755 --- a/test/functional/p2p_tx_download.py +++ b/test/functional/p2p_tx_download.py @@ -30,8 +30,8 @@ import time class TestP2PConn(P2PInterface): - def __init__(self): - super().__init__() + def __init__(self, wtxidrelay=True): + super().__init__(wtxidrelay=wtxidrelay) self.tx_getdata_count = 0 def on_getdata(self, message): @@ -47,6 +47,7 @@ TXID_RELAY_DELAY = 2 # seconds OVERLOADED_PEER_DELAY = 2 # seconds MAX_GETDATA_IN_FLIGHT = 100 MAX_PEER_TX_ANNOUNCEMENTS = 5000 +NONPREF_PEER_TX_DELAY = 2 # Python test constants NUM_INBOUND = 10 @@ -168,8 +169,6 @@ class TxDownloadTest(BitcoinTestFramework): assert_equal(peer_fallback.tx_getdata_count, 0) self.nodes[0].setmocktime(int(time.time()) + GETDATA_TX_INTERVAL + 1) # Wait for request to peer_expiry to expire peer_fallback.wait_until(lambda: peer_fallback.tx_getdata_count >= 1, timeout=1) - with p2p_lock: - assert_equal(peer_fallback.tx_getdata_count, 1) self.restart_node(0) # reset mocktime def test_disconnect_fallback(self): @@ -187,8 +186,6 @@ class TxDownloadTest(BitcoinTestFramework): peer_disconnect.peer_disconnect() peer_disconnect.wait_for_disconnect() peer_fallback.wait_until(lambda: peer_fallback.tx_getdata_count >= 1, timeout=1) - with p2p_lock: - assert_equal(peer_fallback.tx_getdata_count, 1) def test_notfound_fallback(self): self.log.info('Check that notfounds will select another peer for download immediately') @@ -204,17 +201,42 @@ class TxDownloadTest(BitcoinTestFramework): assert_equal(peer_fallback.tx_getdata_count, 0) peer_notfound.send_and_ping(msg_notfound(vec=[CInv(MSG_WTX, WTXID)])) # Send notfound, so that fallback peer is selected peer_fallback.wait_until(lambda: peer_fallback.tx_getdata_count >= 1, timeout=1) - with p2p_lock: - assert_equal(peer_fallback.tx_getdata_count, 1) - def test_preferred_inv(self): - self.log.info('Check that invs from preferred peers are downloaded immediately') - self.restart_node(0, extra_args=['-whitelist=noban@127.0.0.1']) + def test_preferred_inv(self, preferred=False): + if preferred: + self.log.info('Check invs from preferred peers are downloaded immediately') + self.restart_node(0, extra_args=['-whitelist=noban@127.0.0.1']) + else: + self.log.info('Check invs from non-preferred peers are downloaded after {} s'.format(NONPREF_PEER_TX_DELAY)) + mock_time = int(time.time() + 1) + self.nodes[0].setmocktime(mock_time) peer = self.nodes[0].add_p2p_connection(TestP2PConn()) peer.send_message(msg_inv([CInv(t=MSG_WTX, h=0xff00ff00)])) - peer.wait_until(lambda: peer.tx_getdata_count >= 1, timeout=1) + peer.sync_with_ping() + if preferred: + peer.wait_until(lambda: peer.tx_getdata_count >= 1, timeout=1) + else: + with p2p_lock: + assert_equal(peer.tx_getdata_count, 0) + self.nodes[0].setmocktime(mock_time + NONPREF_PEER_TX_DELAY) + peer.wait_until(lambda: peer.tx_getdata_count >= 1, timeout=1) + + def test_txid_inv_delay(self, glob_wtxid=False): + self.log.info('Check that inv from a txid-relay peers are delayed by {} s, with a wtxid peer {}'.format(TXID_RELAY_DELAY, glob_wtxid)) + self.restart_node(0, extra_args=['-whitelist=noban@127.0.0.1']) + mock_time = int(time.time() + 1) + self.nodes[0].setmocktime(mock_time) + peer = self.nodes[0].add_p2p_connection(TestP2PConn(wtxidrelay=False)) + if glob_wtxid: + # Add a second wtxid-relay connection otherwise TXID_RELAY_DELAY is waived in + # lack of wtxid-relay peers + self.nodes[0].add_p2p_connection(TestP2PConn(wtxidrelay=True)) + peer.send_message(msg_inv([CInv(t=MSG_TX, h=0xff11ff11)])) + peer.sync_with_ping() with p2p_lock: - assert_equal(peer.tx_getdata_count, 1) + assert_equal(peer.tx_getdata_count, 0 if glob_wtxid else 1) + self.nodes[0].setmocktime(mock_time + TXID_RELAY_DELAY) + peer.wait_until(lambda: peer.tx_getdata_count >= 1, timeout=1) def test_large_inv_batch(self): self.log.info('Test how large inv batches are handled with relay permission') @@ -229,8 +251,6 @@ class TxDownloadTest(BitcoinTestFramework): peer.send_message(msg_inv([CInv(t=MSG_WTX, h=wtxid) for wtxid in range(MAX_PEER_TX_ANNOUNCEMENTS + 1)])) peer.wait_until(lambda: peer.tx_getdata_count == MAX_PEER_TX_ANNOUNCEMENTS) peer.sync_with_ping() - with p2p_lock: - assert_equal(peer.tx_getdata_count, MAX_PEER_TX_ANNOUNCEMENTS) def test_spurious_notfound(self): self.log.info('Check that spurious notfound is ignored') @@ -242,6 +262,9 @@ class TxDownloadTest(BitcoinTestFramework): self.test_disconnect_fallback() self.test_notfound_fallback() self.test_preferred_inv() + self.test_preferred_inv(True) + self.test_txid_inv_delay() + self.test_txid_inv_delay(True) self.test_large_inv_batch() self.test_spurious_notfound() diff --git a/test/functional/rpc_blockchain.py b/test/functional/rpc_blockchain.py index f965677408..99be6b7b8e 100755 --- a/test/functional/rpc_blockchain.py +++ b/test/functional/rpc_blockchain.py @@ -20,6 +20,7 @@ Tests correspond to code in rpc/blockchain.cpp. from decimal import Decimal import http.client +import os import subprocess from test_framework.blocktools import ( @@ -42,7 +43,9 @@ from test_framework.util import ( assert_raises_rpc_error, assert_is_hex_string, assert_is_hash_string, + get_datadir_path, ) +from test_framework.wallet import MiniWallet class BlockchainTest(BitcoinTestFramework): @@ -63,6 +66,7 @@ class BlockchainTest(BitcoinTestFramework): self._test_getnetworkhashps() self._test_stopatheight() self._test_waitforblockheight() + self._test_getblock() assert self.nodes[0].verifychain(4, 0) def mine_chain(self): @@ -364,6 +368,46 @@ class BlockchainTest(BitcoinTestFramework): assert_waitforheight(current_height) assert_waitforheight(current_height + 1) + def _test_getblock(self): + node = self.nodes[0] + + miniwallet = MiniWallet(node) + miniwallet.generate(5) + node.generate(100) + + fee_per_byte = Decimal('0.00000010') + fee_per_kb = 1000 * fee_per_byte + + miniwallet.send_self_transfer(fee_rate=fee_per_kb, from_node=node) + blockhash = node.generate(1)[0] + + self.log.info("Test that getblock with verbosity 1 doesn't include fee") + block = node.getblock(blockhash, 1) + assert 'fee' not in block['tx'][1] + + self.log.info('Test that getblock with verbosity 2 includes expected fee') + block = node.getblock(blockhash, 2) + tx = block['tx'][1] + assert 'fee' in tx + assert_equal(tx['fee'], tx['vsize'] * fee_per_byte) + + self.log.info("Test that getblock with verbosity 2 still works with pruned Undo data") + datadir = get_datadir_path(self.options.tmpdir, 0) + + def move_block_file(old, new): + old_path = os.path.join(datadir, self.chain, 'blocks', old) + new_path = os.path.join(datadir, self.chain, 'blocks', new) + os.rename(old_path, new_path) + + # Move instead of deleting so we can restore chain state afterwards + move_block_file('rev00000.dat', 'rev_wrong') + + block = node.getblock(blockhash, 2) + assert 'fee' not in block['tx'][1] + + # Restore chain state + move_block_file('rev_wrong', 'rev00000.dat') + if __name__ == '__main__': BlockchainTest().main() diff --git a/test/functional/rpc_createmultisig.py b/test/functional/rpc_createmultisig.py index f19c60dc36..31baeba582 100755 --- a/test/functional/rpc_createmultisig.py +++ b/test/functional/rpc_createmultisig.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2019 The Bitcoin Core developers +# Copyright (c) 2015-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 multisig RPCs""" diff --git a/test/functional/rpc_deprecated.py b/test/functional/rpc_deprecated.py index adcd8a7d4c..1af79b9f7c 100755 --- a/test/functional/rpc_deprecated.py +++ b/test/functional/rpc_deprecated.py @@ -1,10 +1,9 @@ #!/usr/bin/env python3 -# Copyright (c) 2017-2019 The Bitcoin Core developers +# Copyright (c) 2017-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 deprecation of RPC calls.""" from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import assert_raises_rpc_error, find_vout_for_address class DeprecatedRpcTest(BitcoinTestFramework): def set_test_params(self): @@ -24,37 +23,7 @@ class DeprecatedRpcTest(BitcoinTestFramework): # assert_raises_rpc_error(-32, 'The wallet generate rpc method is deprecated', self.nodes[0].rpc.generate, 1) # self.nodes[1].generate(1) - if self.is_wallet_compiled(): - self.log.info("Test bumpfee RPC") - self.nodes[0].generate(101) - self.nodes[0].createwallet(wallet_name='nopriv', disable_private_keys=True) - noprivs0 = self.nodes[0].get_wallet_rpc('nopriv') - w0 = self.nodes[0].get_wallet_rpc(self.default_wallet_name) - self.nodes[1].createwallet(wallet_name='nopriv', disable_private_keys=True) - noprivs1 = self.nodes[1].get_wallet_rpc('nopriv') - - address = w0.getnewaddress() - desc = w0.getaddressinfo(address)['desc'] - change_addr = w0.getrawchangeaddress() - change_desc = w0.getaddressinfo(change_addr)['desc'] - txid = w0.sendtoaddress(address=address, amount=10) - vout = find_vout_for_address(w0, txid, address) - self.nodes[0].generate(1) - rawtx = w0.createrawtransaction([{'txid': txid, 'vout': vout}], {w0.getnewaddress(): 5}, 0, True) - rawtx = w0.fundrawtransaction(rawtx, {'changeAddress': change_addr}) - signed_tx = w0.signrawtransactionwithwallet(rawtx['hex'])['hex'] - - noprivs0.importmulti([{'desc': desc, 'timestamp': 0}, {'desc': change_desc, 'timestamp': 0, 'internal': True}]) - noprivs1.importmulti([{'desc': desc, 'timestamp': 0}, {'desc': change_desc, 'timestamp': 0, 'internal': True}]) - - txid = w0.sendrawtransaction(signed_tx) - self.sync_all() - - assert_raises_rpc_error(-32, 'Using bumpfee with wallets that have private keys disabled is deprecated. Use psbtbumpfee instead or restart bitcoind with -deprecatedrpc=bumpfee. This functionality will be removed in 0.22', noprivs0.bumpfee, txid) - bumped_psbt = noprivs1.bumpfee(txid) - assert 'psbt' in bumped_psbt - else: - self.log.info("No tested deprecated RPC methods") + self.log.info("No tested deprecated RPC methods") if __name__ == '__main__': DeprecatedRpcTest().main() diff --git a/test/functional/rpc_estimatefee.py b/test/functional/rpc_estimatefee.py index 3b76c7dd1e..81862ac69e 100755 --- a/test/functional/rpc_estimatefee.py +++ b/test/functional/rpc_estimatefee.py @@ -41,6 +41,8 @@ class EstimateFeeTest(BitcoinTestFramework): self.nodes[0].estimatesmartfee(1) # self.nodes[0].estimatesmartfee(1, None) self.nodes[0].estimatesmartfee(1, 'ECONOMICAL') + self.nodes[0].estimatesmartfee(1, 'unset') + self.nodes[0].estimatesmartfee(1, 'conservative') self.nodes[0].estimaterawfee(1) self.nodes[0].estimaterawfee(1, None) diff --git a/test/functional/rpc_fundrawtransaction.py b/test/functional/rpc_fundrawtransaction.py index 8ee0ecab0a..569471dc87 100755 --- a/test/functional/rpc_fundrawtransaction.py +++ b/test/functional/rpc_fundrawtransaction.py @@ -717,10 +717,10 @@ class RawTransactionsTest(BitcoinTestFramework): result = node.fundrawtransaction(rawtx) # uses self.min_relay_tx_fee (set by settxfee) btc_kvb_to_sat_vb = 100000 # (1e5) - result1 = node.fundrawtransaction(rawtx, {"fee_rate": 2 * btc_kvb_to_sat_vb * self.min_relay_tx_fee}) + result1 = node.fundrawtransaction(rawtx, {"fee_rate": str(2 * btc_kvb_to_sat_vb * self.min_relay_tx_fee)}) result2 = node.fundrawtransaction(rawtx, {"feeRate": 2 * self.min_relay_tx_fee}) result3 = node.fundrawtransaction(rawtx, {"fee_rate": 10 * btc_kvb_to_sat_vb * self.min_relay_tx_fee}) - result4 = node.fundrawtransaction(rawtx, {"feeRate": 10 * self.min_relay_tx_fee}) + result4 = node.fundrawtransaction(rawtx, {"feeRate": str(10 * self.min_relay_tx_fee)}) # Test that funding non-standard "zero-fee" transactions is valid. result5 = self.nodes[3].fundrawtransaction(rawtx, {"fee_rate": 0}) result6 = self.nodes[3].fundrawtransaction(rawtx, {"feeRate": 0}) diff --git a/test/functional/rpc_getblockfilter.py b/test/functional/rpc_getblockfilter.py index c3c3622cf9..044dbd35bf 100755 --- a/test/functional/rpc_getblockfilter.py +++ b/test/functional/rpc_getblockfilter.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2018 The Bitcoin Core developers +# Copyright (c) 2018-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 the getblockfilter RPC.""" diff --git a/test/functional/rpc_getdescriptorinfo.py b/test/functional/rpc_getdescriptorinfo.py index ea064f9763..563f54c5e7 100755 --- a/test/functional/rpc_getdescriptorinfo.py +++ b/test/functional/rpc_getdescriptorinfo.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2019 The Bitcoin Core developers +# Copyright (c) 2019-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 getdescriptorinfo RPC. diff --git a/test/functional/rpc_getpeerinfo_deprecation.py b/test/functional/rpc_getpeerinfo_deprecation.py deleted file mode 100755 index 340a66e12f..0000000000 --- a/test/functional/rpc_getpeerinfo_deprecation.py +++ /dev/null @@ -1,38 +0,0 @@ -#!/usr/bin/env python3 -# Copyright (c) 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 deprecation of getpeerinfo RPC fields.""" - -from test_framework.test_framework import BitcoinTestFramework - - -class GetpeerinfoDeprecationTest(BitcoinTestFramework): - def set_test_params(self): - self.num_nodes = 2 - self.extra_args = [[], ["-deprecatedrpc=banscore"]] - - def run_test(self): - self.test_banscore_deprecation() - self.test_addnode_deprecation() - - def test_banscore_deprecation(self): - self.log.info("Test getpeerinfo by default no longer returns a banscore field") - assert "banscore" not in self.nodes[0].getpeerinfo()[0].keys() - - self.log.info("Test getpeerinfo returns banscore with -deprecatedrpc=banscore") - assert "banscore" in self.nodes[1].getpeerinfo()[0].keys() - - def test_addnode_deprecation(self): - self.restart_node(1, ["-deprecatedrpc=getpeerinfo_addnode"]) - self.connect_nodes(0, 1) - - self.log.info("Test getpeerinfo by default no longer returns an addnode field") - assert "addnode" not in self.nodes[0].getpeerinfo()[0].keys() - - self.log.info("Test getpeerinfo returns addnode with -deprecatedrpc=addnode") - assert "addnode" in self.nodes[1].getpeerinfo()[0].keys() - - -if __name__ == "__main__": - GetpeerinfoDeprecationTest().main() diff --git a/test/functional/rpc_help.py b/test/functional/rpc_help.py index 9b981b864e..c83157a843 100755 --- a/test/functional/rpc_help.py +++ b/test/functional/rpc_help.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2018-2019 The Bitcoin Core developers +# Copyright (c) 2018-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 RPC help output.""" diff --git a/test/functional/rpc_invalidateblock.py b/test/functional/rpc_invalidateblock.py index f884b8d293..114bd5e7e9 100755 --- a/test/functional/rpc_invalidateblock.py +++ b/test/functional/rpc_invalidateblock.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2014-2019 The Bitcoin Core developers +# Copyright (c) 2014-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 the invalidateblock RPC.""" diff --git a/test/functional/rpc_misc.py b/test/functional/rpc_misc.py index 0493ceeb64..1398d1237f 100755 --- a/test/functional/rpc_misc.py +++ b/test/functional/rpc_misc.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2019 The Bitcoin Core developers +# Copyright (c) 2019-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 RPC misc output.""" diff --git a/test/functional/rpc_net.py b/test/functional/rpc_net.py index 2efd090733..de0b7f303f 100755 --- a/test/functional/rpc_net.py +++ b/test/functional/rpc_net.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2017-2019 The Bitcoin Core developers +# Copyright (c) 2017-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 RPC calls related to net. diff --git a/test/functional/rpc_preciousblock.py b/test/functional/rpc_preciousblock.py index 04d55b103f..7d3a5cc9ce 100755 --- a/test/functional/rpc_preciousblock.py +++ b/test/functional/rpc_preciousblock.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2019 The Bitcoin Core developers +# Copyright (c) 2015-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 the preciousblock RPC.""" diff --git a/test/functional/rpc_psbt.py b/test/functional/rpc_psbt.py index 5840801b00..b364077a9a 100755 --- a/test/functional/rpc_psbt.py +++ b/test/functional/rpc_psbt.py @@ -190,11 +190,11 @@ class PSBTTest(BitcoinTestFramework): self.log.info("Test walletcreatefundedpsbt fee rate of 10000 sat/vB and 0.1 BTC/kvB produces a total fee at or slightly below -maxtxfee (~0.05290000)") res1 = self.nodes[1].walletcreatefundedpsbt(inputs, outputs, 0, {"fee_rate": 10000, "add_inputs": True}) assert_approx(res1["fee"], 0.055, 0.005) - res2 = self.nodes[1].walletcreatefundedpsbt(inputs, outputs, 0, {"feeRate": 0.1, "add_inputs": True}) + res2 = self.nodes[1].walletcreatefundedpsbt(inputs, outputs, 0, {"feeRate": "0.1", "add_inputs": True}) assert_approx(res2["fee"], 0.055, 0.005) self.log.info("Test min fee rate checks with walletcreatefundedpsbt are bypassed, e.g. a fee_rate under 1 sat/vB is allowed") - res3 = self.nodes[1].walletcreatefundedpsbt(inputs, outputs, 0, {"fee_rate": 0.99999999, "add_inputs": True}) + res3 = self.nodes[1].walletcreatefundedpsbt(inputs, outputs, 0, {"fee_rate": "0.99999999", "add_inputs": True}) assert_approx(res3["fee"], 0.00000381, 0.0000001) res4 = self.nodes[1].walletcreatefundedpsbt(inputs, outputs, 0, {"feeRate": 0.00000999, "add_inputs": True}) assert_approx(res4["fee"], 0.00000381, 0.0000001) diff --git a/test/functional/rpc_rawtransaction.py b/test/functional/rpc_rawtransaction.py index 554c30c0d2..86c7b3fbcc 100755 --- a/test/functional/rpc_rawtransaction.py +++ b/test/functional/rpc_rawtransaction.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2014-2019 The Bitcoin Core developers +# Copyright (c) 2014-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 the rawtransaction RPCs. @@ -372,6 +372,13 @@ class RawTransactionsTest(BitcoinTestFramework): encrawtx = "01000000010000000000000072c1a6a246ae63f74f931e8365e15a089c68d61900000000000000000000ffffffff0100e1f505000000000000000000" decrawtx = self.nodes[0].decoderawtransaction(encrawtx, False) # decode as non-witness transaction assert_equal(decrawtx['vout'][0]['value'], Decimal('1.00000000')) + # known ambiguous transaction in the chain (see https://github.com/bitcoin/bitcoin/issues/20579) + encrawtx = "020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff4b03c68708046ff8415c622f4254432e434f4d2ffabe6d6de1965d02c68f928e5b244ab1965115a36f56eb997633c7f690124bbf43644e23080000000ca3d3af6d005a65ff0200fd00000000ffffffff03f4c1fb4b0000000016001497cfc76442fe717f2a3f0cc9c175f7561b6619970000000000000000266a24aa21a9ed957d1036a80343e0d1b659497e1b48a38ebe876a056d45965fac4a85cda84e1900000000000000002952534b424c4f434b3a8e092581ab01986cbadc84f4b43f4fa4bb9e7a2e2a0caf9b7cf64d939028e22c0120000000000000000000000000000000000000000000000000000000000000000000000000" + decrawtx = self.nodes[0].decoderawtransaction(encrawtx) + decrawtx_wit = self.nodes[0].decoderawtransaction(encrawtx, True) + assert_raises_rpc_error(-22, 'TX decode failed', self.nodes[0].decoderawtransaction, encrawtx, False) # fails to decode as non-witness transaction + assert_equal(decrawtx, decrawtx_wit) # the witness interpretation should be chosen + assert_equal(decrawtx['vin'][0]['coinbase'], "03c68708046ff8415c622f4254432e434f4d2ffabe6d6de1965d02c68f928e5b244ab1965115a36f56eb997633c7f690124bbf43644e23080000000ca3d3af6d005a65ff0200fd00000000") # Basic signrawtransaction test addr = self.nodes[1].getnewaddress() diff --git a/test/functional/rpc_setban.py b/test/functional/rpc_setban.py index bc48449084..fd5f8aa098 100755 --- a/test/functional/rpc_setban.py +++ b/test/functional/rpc_setban.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2019 The Bitcoin Core developers +# Copyright (c) 2015-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 the setban rpc call.""" @@ -15,6 +15,9 @@ class SetBanTests(BitcoinTestFramework): self.setup_clean_chain = True self.extra_args = [[],[]] + def is_banned(self, node, addr): + return any(e['address'] == addr for e in node.listbanned()) + def run_test(self): # Node 0 connects to Node 1, check that the noban permission is not granted self.connect_nodes(0, 1) @@ -42,5 +45,18 @@ class SetBanTests(BitcoinTestFramework): peerinfo = self.nodes[1].getpeerinfo()[0] assert(not 'noban' in peerinfo['permissions']) + self.log.info("Test that a non-IP address can be banned/unbanned") + node = self.nodes[1] + tor_addr = "pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion" + ip_addr = "1.2.3.4" + assert(not self.is_banned(node, tor_addr)) + assert(not self.is_banned(node, ip_addr)) + node.setban(tor_addr, "add") + assert(self.is_banned(node, tor_addr)) + assert(not self.is_banned(node, ip_addr)) + node.setban(tor_addr, "remove") + assert(not self.is_banned(self.nodes[1], tor_addr)) + assert(not self.is_banned(node, ip_addr)) + if __name__ == '__main__': SetBanTests().main() diff --git a/test/functional/rpc_signrawtransaction.py b/test/functional/rpc_signrawtransaction.py index b962e1c3a5..2fbbdbbdf0 100755 --- a/test/functional/rpc_signrawtransaction.py +++ b/test/functional/rpc_signrawtransaction.py @@ -151,6 +151,19 @@ class SignRawTransactionsTest(BitcoinTestFramework): assert_equal(rawTxSigned['errors'][1]['witness'], ["304402203609e17b84f6a7d30c80bfa610b5b4542f32a8a0d5447a12fb1366d7f01cc44a0220573a954c4518331561406f90300e8f3358f51928d43c212a8caed02de67eebee01", "025476c2e83188368da1ff3e292e7acafcdb3566bb0ad253f62fc70f07aeee6357"]) assert not rawTxSigned['errors'][0]['witness'] + def test_fully_signed_tx(self): + self.log.info("Test signing a fully signed transaction does nothing") + self.nodes[0].walletpassphrase("password", 9999) + self.nodes[0].generate(101) + rawtx = self.nodes[0].createrawtransaction([], [{self.nodes[0].getnewaddress(): 10}]) + fundedtx = self.nodes[0].fundrawtransaction(rawtx) + signedtx = self.nodes[0].signrawtransactionwithwallet(fundedtx["hex"]) + assert_equal(signedtx["complete"], True) + signedtx2 = self.nodes[0].signrawtransactionwithwallet(signedtx["hex"]) + assert_equal(signedtx2["complete"], True) + assert_equal(signedtx["hex"], signedtx2["hex"]) + self.nodes[0].walletlock() + def witness_script_test(self): self.log.info("Test signing transaction to P2SH-P2WSH addresses without wallet") # Create a new P2SH-P2WSH 1-of-1 multisig address: @@ -231,6 +244,7 @@ class SignRawTransactionsTest(BitcoinTestFramework): self.witness_script_test() self.OP_1NEGATE_test() self.test_with_lock_outputs() + self.test_fully_signed_tx() if __name__ == '__main__': diff --git a/test/functional/rpc_txoutproof.py b/test/functional/rpc_txoutproof.py index 93fb62c5d6..528da0cbfc 100755 --- a/test/functional/rpc_txoutproof.py +++ b/test/functional/rpc_txoutproof.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2014-2019 The Bitcoin Core developers +# Copyright (c) 2014-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 gettxoutproof and verifytxoutproof RPCs.""" @@ -78,7 +78,7 @@ class MerkleBlockTest(BitcoinTestFramework): # We can't get a proof if we specify transactions from different blocks assert_raises_rpc_error(-5, "Not all transactions found in specified or retrieved block", self.nodes[0].gettxoutproof, [txid1, txid3]) # Test empty list - assert_raises_rpc_error(-5, "Transaction not yet in block", self.nodes[0].gettxoutproof, []) + assert_raises_rpc_error(-8, "Parameter 'txids' cannot be empty", self.nodes[0].gettxoutproof, []) # Test duplicate txid assert_raises_rpc_error(-8, 'Invalid parameter, duplicated txid', self.nodes[0].gettxoutproof, [txid1, txid1]) diff --git a/test/functional/rpc_users.py b/test/functional/rpc_users.py index daf02fc4f3..7cedb4336b 100755 --- a/test/functional/rpc_users.py +++ b/test/functional/rpc_users.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2019 The Bitcoin Core developers +# Copyright (c) 2015-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 multiple RPC users.""" @@ -99,11 +99,18 @@ class HTTPBasicsTest(BitcoinTestFramework): self.test_auth(self.nodes[1], self.rpcuser, self.rpcpassword) - self.log.info('Check that failure to write cookie file will abort the node gracefully') + init_error = 'Error: Unable to start HTTP server. See debug log for details.' + + self.log.info('Check -rpcauth are validated') + # Empty -rpcauth= are ignored + self.restart_node(0, extra_args=['-rpcauth=']) self.stop_node(0) + self.nodes[0].assert_start_raises_init_error(expected_msg=init_error, extra_args=['-rpcauth=foo']) + self.nodes[0].assert_start_raises_init_error(expected_msg=init_error, extra_args=['-rpcauth=foo:bar']) + + self.log.info('Check that failure to write cookie file will abort the node gracefully') cookie_file = os.path.join(get_datadir_path(self.options.tmpdir, 0), self.chain, '.cookie.tmp') os.mkdir(cookie_file) - init_error = 'Error: Unable to start HTTP server. See debug log for details.' self.nodes[0].assert_start_raises_init_error(expected_msg=init_error) diff --git a/test/functional/test_framework/blocktools.py b/test/functional/test_framework/blocktools.py index 6b7214f03a..e691b63df6 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-2019 The Bitcoin Core developers +# Copyright (c) 2015-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. """Utilities for manipulating blocks and transactions.""" diff --git a/test/functional/test_framework/key.py b/test/functional/test_framework/key.py index f3d13c049b..e0cbab45ce 100644 --- a/test/functional/test_framework/key.py +++ b/test/functional/test_framework/key.py @@ -27,7 +27,7 @@ def xor_bytes(b0, b1): def jacobi_symbol(n, k): """Compute the Jacobi symbol of n modulo k - See http://en.wikipedia.org/wiki/Jacobi_symbol + See https://en.wikipedia.org/wiki/Jacobi_symbol For our application k is always prime, so this is the same as the Legendre symbol.""" assert k > 0 and k & 1, "jacobi symbol is only defined for positive odd k" diff --git a/test/functional/test_framework/messages.py b/test/functional/test_framework/messages.py index ff7f73bdf4..6ad4e13db2 100755 --- a/test/functional/test_framework/messages.py +++ b/test/functional/test_framework/messages.py @@ -51,7 +51,6 @@ MAX_HEADERS_RESULTS = 2000 # Number of headers sent in one getheaders result MAX_INV_SIZE = 50000 # Maximum number of entries in an 'inv' protocol message NODE_NETWORK = (1 << 0) -NODE_GETUTXO = (1 << 1) NODE_BLOOM = (1 << 2) NODE_WITNESS = (1 << 3) NODE_COMPACT_FILTERS = (1 << 6) @@ -565,6 +564,9 @@ class CTransaction: def serialize(self): return self.serialize_with_witness() + def getwtxid(self): + return hash256(self.serialize())[::-1].hex() + # Recalculate the txid (transaction hash without witness) def rehash(self): self.sha256 = None @@ -580,7 +582,7 @@ class CTransaction: if self.sha256 is None: self.sha256 = uint256_from_str(hash256(self.serialize_without_witness())) - self.hash = encode(hash256(self.serialize_without_witness())[::-1], 'hex_codec').decode('ascii') + self.hash = hash256(self.serialize_without_witness())[::-1].hex() def is_valid(self): self.calc_sha256() diff --git a/test/functional/test_framework/muhash.py b/test/functional/test_framework/muhash.py index 97d02359cb..183548f71f 100644 --- a/test/functional/test_framework/muhash.py +++ b/test/functional/test_framework/muhash.py @@ -78,11 +78,13 @@ class MuHash3072: def insert(self, data): """Insert a byte array data in the set.""" - self.numerator = (self.numerator * data_to_num3072(data)) % self.MODULUS + data_hash = hashlib.sha256(data).digest() + self.numerator = (self.numerator * data_to_num3072(data_hash)) % self.MODULUS def remove(self, data): """Remove a byte array from the set.""" - self.denominator = (self.denominator * data_to_num3072(data)) % self.MODULUS + data_hash = hashlib.sha256(data).digest() + self.denominator = (self.denominator * data_to_num3072(data_hash)) % self.MODULUS def digest(self): """Extract the final hash. Does not modify this object.""" @@ -93,12 +95,12 @@ class MuHash3072: class TestFrameworkMuhash(unittest.TestCase): def test_muhash(self): muhash = MuHash3072() - muhash.insert([0]*32) - muhash.insert([1] + [0]*31) - muhash.remove([2] + [0]*31) + muhash.insert(b'\x00' * 32) + muhash.insert((b'\x01' + b'\x00' * 31)) + muhash.remove((b'\x02' + b'\x00' * 31)) finalized = muhash.digest() # This mirrors the result in the C++ MuHash3072 unit test - self.assertEqual(finalized[::-1].hex(), "a44e16d5e34d259b349af21c06e65d653915d2e208e4e03f389af750dc0bfdc3") + self.assertEqual(finalized[::-1].hex(), "10d312b100cbd32ada024a6646e40d3482fcff103668d2625f10002a607d5863") def test_chacha20(self): def chacha_check(key, result): diff --git a/test/functional/test_framework/netutil.py b/test/functional/test_framework/netutil.py index c98424e8e2..e047e7fa14 100644 --- a/test/functional/test_framework/netutil.py +++ b/test/functional/test_framework/netutil.py @@ -84,7 +84,7 @@ def get_bind_addrs(pid): bind_addrs.append(conn[1]) return bind_addrs -# from: http://code.activestate.com/recipes/439093/ +# from: https://code.activestate.com/recipes/439093/ def all_interfaces(): ''' Return all interfaces that are up diff --git a/test/functional/test_framework/p2p.py b/test/functional/test_framework/p2p.py index 6846d31221..fa4a567aac 100755 --- a/test/functional/test_framework/p2p.py +++ b/test/functional/test_framework/p2p.py @@ -71,7 +71,11 @@ from test_framework.messages import ( NODE_WITNESS, sha256, ) -from test_framework.util import wait_until_helper +from test_framework.util import ( + MAX_NODES, + p2p_port, + wait_until_helper, +) logger = logging.getLogger("TestFramework.p2p") @@ -139,7 +143,7 @@ class P2PConnection(asyncio.Protocol): def is_connected(self): return self._transport is not None - def peer_connect(self, dstaddr, dstport, *, net, timeout_factor): + def peer_connect_helper(self, dstaddr, dstport, net, timeout_factor): assert not self.is_connected self.timeout_factor = timeout_factor self.dstaddr = dstaddr @@ -148,12 +152,20 @@ class P2PConnection(asyncio.Protocol): self.on_connection_send_msg = None self.recvbuf = b"" self.magic_bytes = MAGIC_BYTES[net] - logger.debug('Connecting to Bitcoin Node: %s:%d' % (self.dstaddr, self.dstport)) + + def peer_connect(self, dstaddr, dstport, *, net, timeout_factor): + self.peer_connect_helper(dstaddr, dstport, net, timeout_factor) loop = NetworkThread.network_event_loop - conn_gen_unsafe = loop.create_connection(lambda: self, host=self.dstaddr, port=self.dstport) - conn_gen = lambda: loop.call_soon_threadsafe(loop.create_task, conn_gen_unsafe) - return conn_gen + logger.debug('Connecting to Bitcoin Node: %s:%d' % (self.dstaddr, self.dstport)) + coroutine = loop.create_connection(lambda: self, host=self.dstaddr, port=self.dstport) + return lambda: loop.call_soon_threadsafe(loop.create_task, coroutine) + + def peer_accept_connection(self, connect_id, connect_cb=lambda: None, *, net, timeout_factor): + self.peer_connect_helper('0', 0, net, timeout_factor) + + logger.debug('Listening for Bitcoin Node with id: {}'.format(connect_id)) + return lambda: NetworkThread.listen(self, connect_cb, idx=connect_id) def peer_disconnect(self): # Connection could have already been closed by other end. @@ -289,7 +301,7 @@ class P2PInterface(P2PConnection): Individual testcases should subclass this and override the on_* methods if they want to alter message handling behaviour.""" - def __init__(self, support_addrv2=False): + def __init__(self, support_addrv2=False, wtxidrelay=True): super().__init__() # Track number of messages of each type received. @@ -309,18 +321,30 @@ class P2PInterface(P2PConnection): self.support_addrv2 = support_addrv2 - def peer_connect(self, *args, services=NODE_NETWORK|NODE_WITNESS, send_version=True, **kwargs): + # If the peer supports wtxid-relay + self.wtxidrelay = wtxidrelay + + def peer_connect_send_version(self, services): + # Send a version msg + vt = msg_version() + vt.nServices = services + vt.addrTo.ip = self.dstaddr + vt.addrTo.port = self.dstport + vt.addrFrom.ip = "0.0.0.0" + vt.addrFrom.port = 0 + self.on_connection_send_msg = vt # Will be sent in connection_made callback + + def peer_connect(self, *args, services=NODE_NETWORK | NODE_WITNESS, send_version=True, **kwargs): create_conn = super().peer_connect(*args, **kwargs) if send_version: - # Send a version msg - vt = msg_version() - vt.nServices = services - vt.addrTo.ip = self.dstaddr - vt.addrTo.port = self.dstport - vt.addrFrom.ip = "0.0.0.0" - vt.addrFrom.port = 0 - self.on_connection_send_msg = vt # Will be sent soon after connection_made + self.peer_connect_send_version(services) + + return create_conn + + def peer_accept_connection(self, *args, services=NODE_NETWORK | NODE_WITNESS, **kwargs): + create_conn = super().peer_accept_connection(*args, **kwargs) + self.peer_connect_send_version(services) return create_conn @@ -394,11 +418,11 @@ class P2PInterface(P2PConnection): def on_version(self, message): assert message.nVersion >= MIN_VERSION_SUPPORTED, "Version {} received. Test framework only supports versions greater than {}".format(message.nVersion, MIN_VERSION_SUPPORTED) - if message.nVersion >= 70016: + if message.nVersion >= 70016 and self.wtxidrelay: self.send_message(msg_wtxidrelay()) - self.send_message(msg_verack()) if self.support_addrv2: self.send_message(msg_sendaddrv2()) + self.send_message(msg_verack()) self.nServices = message.nServices # Connection helper methods @@ -411,6 +435,10 @@ class P2PInterface(P2PConnection): wait_until_helper(test_function, timeout=timeout, lock=p2p_lock, timeout_factor=self.timeout_factor) + def wait_for_connect(self, timeout=60): + test_function = lambda: self.is_connected + wait_until_helper(test_function, timeout=timeout, lock=p2p_lock) + def wait_for_disconnect(self, timeout=60): test_function = lambda: not self.is_connected self.wait_until(test_function, timeout=timeout, check_connected=False) @@ -524,6 +552,8 @@ class NetworkThread(threading.Thread): # There is only one event loop and no more than one thread must be created assert not self.network_event_loop + NetworkThread.listeners = {} + NetworkThread.protos = {} NetworkThread.network_event_loop = asyncio.new_event_loop() def run(self): @@ -539,6 +569,48 @@ class NetworkThread(threading.Thread): # Safe to remove event loop. NetworkThread.network_event_loop = None + @classmethod + def listen(cls, p2p, callback, port=None, addr=None, idx=1): + """ Ensure a listening server is running on the given port, and run the + protocol specified by `p2p` on the next connection to it. Once ready + for connections, call `callback`.""" + + if port is None: + assert 0 < idx <= MAX_NODES + port = p2p_port(MAX_NODES - idx) + if addr is None: + addr = '127.0.0.1' + + coroutine = cls.create_listen_server(addr, port, callback, p2p) + cls.network_event_loop.call_soon_threadsafe(cls.network_event_loop.create_task, coroutine) + + @classmethod + async def create_listen_server(cls, addr, port, callback, proto): + def peer_protocol(): + """Returns a function that does the protocol handling for a new + connection. To allow different connections to have different + behaviors, the protocol function is first put in the cls.protos + dict. When the connection is made, the function removes the + protocol function from that dict, and returns it so the event loop + can start executing it.""" + response = cls.protos.get((addr, port)) + cls.protos[(addr, port)] = None + return response + + if (addr, port) not in cls.listeners: + # When creating a listener on a given (addr, port) we only need to + # do it once. If we want different behaviors for different + # connections, we can accomplish this by providing different + # `proto` functions + + listener = await cls.network_event_loop.create_server(peer_protocol, addr, port) + logger.debug("Listening server on %s:%d should be started" % (addr, port)) + cls.listeners[(addr, port)] = listener + + cls.protos[(addr, port)] = proto + callback(addr, port) + + class P2PDataStore(P2PInterface): """A P2P data store class. diff --git a/test/functional/test_framework/script.py b/test/functional/test_framework/script.py index 8e5848d493..be0e9f24e2 100644 --- a/test/functional/test_framework/script.py +++ b/test/functional/test_framework/script.py @@ -787,7 +787,7 @@ def TaprootSignatureHash(txTo, spent_utxos, hash_type, input_index = 0, scriptpa def taproot_tree_helper(scripts): if len(scripts) == 0: - return ([], bytes(0 for _ in range(32))) + return ([], bytes()) if len(scripts) == 1: # One entry: treat as a leaf script = scripts[0] @@ -824,21 +824,33 @@ def taproot_tree_helper(scripts): h = TaggedHash("TapBranch", left_h + right_h) return (left + right, h) +# A TaprootInfo object has the following fields: +# - scriptPubKey: the scriptPubKey (witness v1 CScript) +# - inner_pubkey: the inner pubkey (32 bytes) +# - negflag: whether the pubkey in the scriptPubKey was negated from inner_pubkey+tweak*G (bool). +# - tweak: the tweak (32 bytes) +# - leaves: a dict of name -> TaprootLeafInfo objects for all known leaves TaprootInfo = namedtuple("TaprootInfo", "scriptPubKey,inner_pubkey,negflag,tweak,leaves") + +# A TaprootLeafInfo object has the following fields: +# - script: the leaf script (CScript or bytes) +# - version: the leaf version (0xc0 for BIP342 tapscript) +# - merklebranch: the merkle branch to use for this leaf (32*N bytes) TaprootLeafInfo = namedtuple("TaprootLeafInfo", "script,version,merklebranch") def taproot_construct(pubkey, scripts=None): """Construct a tree of Taproot spending conditions - pubkey: an ECPubKey object for the internal pubkey + pubkey: a 32-byte xonly pubkey for the internal pubkey (bytes) scripts: a list of items; each item is either: - - a (name, CScript) tuple - - a (name, CScript, leaf version) tuple + - a (name, CScript or bytes, leaf version) tuple + - a (name, CScript or bytes) tuple (defaulting to leaf version 0xc0) - another list of items (with the same structure) - - a function, which specifies how to compute the hashing partner - in function of the hash of whatever it is combined with + - a list of two items; the first of which is an item itself, and the + second is a function. The function takes as input the Merkle root of the + first item, and produces a (fictitious) partner to hash with. - Returns: script (sPK or redeemScript), tweak, {name:(script, leaf version, negation flag, innerkey, merklepath), ...} + Returns: a TaprootInfo object """ if scripts is None: scripts = [] diff --git a/test/functional/test_framework/script_util.py b/test/functional/test_framework/script_util.py index 318a438705..457be6b0e6 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 The Bitcoin Core developers +# Copyright (c) 2019-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. """Useful Script constants and utils.""" diff --git a/test/functional/test_framework/test_framework.py b/test/functional/test_framework/test_framework.py index bf047c5f68..7c5317480c 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-2019 The Bitcoin Core developers +# Copyright (c) 2014-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. """Base class for RPC testing.""" @@ -332,7 +332,7 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass): # Methods to override in subclass test scripts. def set_test_params(self): - """Tests must this method to change default values for number of nodes, topology, etc""" + """Tests must override this method to change default values for number of nodes, topology, etc""" raise NotImplementedError def add_options(self, parser): @@ -517,13 +517,12 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass): def stop_node(self, i, expected_stderr='', wait=0): """Stop a bitcoind test node""" self.nodes[i].stop_node(expected_stderr, wait=wait) - self.nodes[i].wait_until_stopped() def stop_nodes(self, wait=0): """Stop multiple bitcoind test nodes""" for node in self.nodes: # Issue RPC to stop nodes - node.stop_node(wait=wait) + node.stop_node(wait=wait, wait_until_stopped=False) for node in self.nodes: # Wait for nodes to stop diff --git a/test/functional/test_framework/test_node.py b/test/functional/test_framework/test_node.py index 0a5b7f551c..b61d433652 100755 --- a/test/functional/test_framework/test_node.py +++ b/test/functional/test_framework/test_node.py @@ -71,6 +71,7 @@ class TestNode(): """ self.index = i + self.p2p_conn_index = 1 self.datadir = datadir self.bitcoinconf = os.path.join(self.datadir, "bitcoin.conf") self.stdout_dir = os.path.join(self.datadir, "stdout") @@ -308,7 +309,7 @@ class TestNode(): def version_is_at_least(self, ver): return self.version is None or self.version >= ver - def stop_node(self, expected_stderr='', wait=0): + def stop_node(self, expected_stderr='', *, wait=0, wait_until_stopped=True): """Stop the node.""" if not self.running: return @@ -337,6 +338,9 @@ class TestNode(): del self.p2ps[:] + if wait_until_stopped: + self.wait_until_stopped() + def is_node_stopped(self): """Checks whether the node has stopped. @@ -482,11 +486,8 @@ class TestNode(): tempfile.NamedTemporaryFile(dir=self.stdout_dir, delete=False) as log_stdout: try: self.start(extra_args, stdout=log_stdout, stderr=log_stderr, *args, **kwargs) - self.wait_for_rpc_connection() - self.stop_node() - self.wait_until_stopped() - except FailedToStartError as e: - self.log.debug('bitcoind failed to start: %s', e) + ret = self.process.wait(timeout=self.rpc_timeout) + self.log.debug(self._node_msg(f'bitcoind exited with status {ret} during initialization')) self.running = False self.process = None # Check stderr for expected message @@ -505,15 +506,19 @@ class TestNode(): if expected_msg != stderr: self._raise_assertion_error( 'Expected message "{}" does not fully match stderr:\n"{}"'.format(expected_msg, stderr)) - else: + except subprocess.TimeoutExpired: + self.process.kill() + self.running = False + self.process = None + assert_msg = f'bitcoind should have exited within {self.rpc_timeout}s ' if expected_msg is None: - assert_msg = "bitcoind should have exited with an error" + assert_msg += "with an error" else: - assert_msg = "bitcoind should have exited with expected error " + expected_msg + assert_msg += "with expected error " + expected_msg self._raise_assertion_error(assert_msg) def add_p2p_connection(self, p2p_conn, *, wait_for_verack=True, **kwargs): - """Add a p2p connection to the node. + """Add an inbound p2p connection to the node. This method adds the p2p connection to the self.p2ps list and also returns the connection to the caller.""" @@ -542,6 +547,29 @@ class TestNode(): return p2p_conn + def add_outbound_p2p_connection(self, p2p_conn, *, p2p_idx, connection_type="outbound-full-relay", **kwargs): + """Add an outbound p2p connection from node. Either + full-relay("outbound-full-relay") or + block-relay-only("block-relay-only") connection. + + This method adds the p2p connection to the self.p2ps list and returns + the connection to the caller. + """ + + def addconnection_callback(address, port): + self.log.debug("Connecting to %s:%d %s" % (address, port, connection_type)) + self.addconnection('%s:%d' % (address, port), connection_type) + + p2p_conn.peer_accept_connection(connect_cb=addconnection_callback, connect_id=p2p_idx + 1, net=self.chain, timeout_factor=self.timeout_factor, **kwargs)() + + p2p_conn.wait_for_connect() + self.p2ps.append(p2p_conn) + + p2p_conn.wait_for_verack() + p2p_conn.sync_with_ping() + + return p2p_conn + def num_test_p2p_connections(self): """Return number of test framework p2p connections to the node.""" return len([peer for peer in self.getpeerinfo() if peer['subver'] == MY_SUBVERSION.decode("utf-8")]) @@ -551,6 +579,7 @@ class TestNode(): for p in self.p2ps: p.peer_disconnect() del self.p2ps[:] + wait_until_helper(lambda: self.num_test_p2p_connections() == 0, timeout_factor=self.timeout_factor) diff --git a/test/functional/test_framework/util.py b/test/functional/test_framework/util.py index 62ff5c6e33..123c48852c 100644 --- a/test/functional/test_framework/util.py +++ b/test/functional/test_framework/util.py @@ -342,16 +342,25 @@ def initialize_datadir(dirname, n, chain): datadir = get_datadir_path(dirname, n) if not os.path.isdir(datadir): os.makedirs(datadir) - # Translate chain name to config name + write_config(os.path.join(datadir, "bitcoin.conf"), n=n, chain=chain) + os.makedirs(os.path.join(datadir, 'stderr'), exist_ok=True) + os.makedirs(os.path.join(datadir, 'stdout'), exist_ok=True) + return datadir + + +def write_config(config_path, *, n, chain, extra_config=""): + # Translate chain subdirectory name to config name if chain == 'testnet3': chain_name_conf_arg = 'testnet' chain_name_conf_section = 'test' else: chain_name_conf_arg = chain chain_name_conf_section = chain - with open(os.path.join(datadir, "bitcoin.conf"), 'w', encoding='utf8') as f: - f.write("{}=1\n".format(chain_name_conf_arg)) - f.write("[{}]\n".format(chain_name_conf_section)) + with open(config_path, 'w', encoding='utf8') as f: + if chain_name_conf_arg: + f.write("{}=1\n".format(chain_name_conf_arg)) + if chain_name_conf_section: + f.write("[{}]\n".format(chain_name_conf_section)) f.write("port=" + str(p2p_port(n)) + "\n") f.write("rpcport=" + str(rpc_port(n)) + "\n") f.write("fallbackfee=0.0002\n") @@ -362,10 +371,9 @@ def initialize_datadir(dirname, n, chain): f.write("listenonion=0\n") f.write("printtoconsole=0\n") f.write("upnp=0\n") + f.write("natpmp=0\n") f.write("shrinkdebugfile=0\n") - os.makedirs(os.path.join(datadir, 'stderr'), exist_ok=True) - os.makedirs(os.path.join(datadir, 'stdout'), exist_ok=True) - return datadir + f.write(extra_config) def get_datadir_path(dirname, n): diff --git a/test/functional/test_framework/wallet.py b/test/functional/test_framework/wallet.py index a71f2c69cb..edd7792608 100644 --- a/test/functional/test_framework/wallet.py +++ b/test/functional/test_framework/wallet.py @@ -71,9 +71,9 @@ class MiniWallet: tx.wit.vtxinwit[0].scriptWitness.stack = [CScript([OP_TRUE])] tx_hex = tx.serialize().hex() - txid = from_node.sendrawtransaction(tx_hex) - self._utxos.append({'txid': txid, 'vout': 0, 'value': send_value}) - tx_info = from_node.getmempoolentry(txid) + tx_info = from_node.testmempoolaccept([tx_hex])[0] + self._utxos.append({'txid': tx_info['txid'], 'vout': 0, 'value': send_value}) + from_node.sendrawtransaction(tx_hex) assert_equal(tx_info['vsize'], vsize) - assert_equal(tx_info['fee'], fee) - return {'txid': txid, 'wtxid': tx_info['wtxid'], 'hex': tx_hex} + assert_equal(tx_info['fees']['base'], fee) + return {'txid': tx_info['txid'], 'wtxid': tx_info['wtxid'], 'hex': tx_hex} diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py index 5b3db282e1..9bbf862568 100755 --- a/test/functional/test_runner.py +++ b/test/functional/test_runner.py @@ -261,6 +261,7 @@ BASE_SCRIPTS = [ 'feature_filelock.py', 'feature_loadblock.py', 'p2p_dos_header_tree.py', + 'p2p_add_connections.py', 'p2p_unrequested_blocks.py', 'p2p_blockfilters.py', 'feature_includeconf.py', @@ -279,7 +280,6 @@ BASE_SCRIPTS = [ 'feature_config_args.py', 'feature_settings.py', 'rpc_getdescriptorinfo.py', - 'rpc_getpeerinfo_deprecation.py', 'rpc_help.py', 'feature_help.py', 'feature_shutdown.py', diff --git a/test/functional/tool_wallet.py b/test/functional/tool_wallet.py index 615b772dc8..8a1af24dcf 100755 --- a/test/functional/tool_wallet.py +++ b/test/functional/tool_wallet.py @@ -10,6 +10,8 @@ import stat import subprocess import textwrap +from collections import OrderedDict + from test_framework.test_framework import BitcoinTestFramework from test_framework.util import assert_equal @@ -28,8 +30,11 @@ class ToolWalletTest(BitcoinTestFramework): def bitcoin_wallet_process(self, *args): binary = self.config["environment"]["BUILDDIR"] + '/src/bitcoin-wallet' + self.config["environment"]["EXEEXT"] - args = ['-datadir={}'.format(self.nodes[0].datadir), '-chain=%s' % self.chain] + list(args) - return subprocess.Popen([binary] + args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True) + default_args = ['-datadir={}'.format(self.nodes[0].datadir), '-chain=%s' % self.chain] + if self.options.descriptors and 'create' in args: + default_args.append('-descriptors') + + return subprocess.Popen([binary] + default_args + list(args), stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True) def assert_raises_tool_error(self, error, *args): p = self.bitcoin_wallet_process(*args) @@ -63,6 +68,119 @@ class ToolWalletTest(BitcoinTestFramework): result = 'unchanged' if new == old else 'increased!' self.log.debug('Wallet file timestamp {}'.format(result)) + def get_expected_info_output(self, name="", transactions=0, keypool=2, address=0): + wallet_name = self.default_wallet_name if name == "" else name + output_types = 3 # p2pkh, p2sh, segwit + if self.options.descriptors: + return textwrap.dedent('''\ + Wallet info + =========== + Name: %s + Format: sqlite + Descriptors: yes + Encrypted: no + HD (hd seed available): yes + Keypool Size: %d + Transactions: %d + Address Book: %d + ''' % (wallet_name, keypool * output_types, transactions, address)) + else: + return textwrap.dedent('''\ + Wallet info + =========== + Name: %s + Format: bdb + Descriptors: no + Encrypted: no + HD (hd seed available): yes + Keypool Size: %d + Transactions: %d + Address Book: %d + ''' % (wallet_name, keypool, transactions, address * output_types)) + + def read_dump(self, filename): + dump = OrderedDict() + with open(filename, "r", encoding="utf8") as f: + for row in f: + row = row.strip() + key, value = row.split(',') + dump[key] = value + return dump + + def assert_is_sqlite(self, filename): + with open(filename, 'rb') as f: + file_magic = f.read(16) + assert file_magic == b'SQLite format 3\x00' + + def assert_is_bdb(self, filename): + with open(filename, 'rb') as f: + f.seek(12, 0) + file_magic = f.read(4) + assert file_magic == b'\x00\x05\x31\x62' or file_magic == b'\x62\x31\x05\x00' + + def write_dump(self, dump, filename, magic=None, skip_checksum=False): + if magic is None: + magic = "BITCOIN_CORE_WALLET_DUMP" + with open(filename, "w", encoding="utf8") as f: + row = ",".join([magic, dump[magic]]) + "\n" + f.write(row) + for k, v in dump.items(): + if k == magic or k == "checksum": + continue + row = ",".join([k, v]) + "\n" + f.write(row) + if not skip_checksum: + row = ",".join(["checksum", dump["checksum"]]) + "\n" + f.write(row) + + def assert_dump(self, expected, received): + e = expected.copy() + r = received.copy() + + # BDB will add a "version" record that is not present in sqlite + # In that case, we should ignore this record in both + # But because this also effects the checksum, we also need to drop that. + v_key = "0776657273696f6e" # Version key + if v_key in e and v_key not in r: + del e[v_key] + del e["checksum"] + del r["checksum"] + if v_key not in e and v_key in r: + del r[v_key] + del e["checksum"] + del r["checksum"] + + assert_equal(len(e), len(r)) + for k, v in e.items(): + assert_equal(v, r[k]) + + def do_tool_createfromdump(self, wallet_name, dumpfile, file_format=None): + dumppath = os.path.join(self.nodes[0].datadir, dumpfile) + rt_dumppath = os.path.join(self.nodes[0].datadir, "rt-{}.dump".format(wallet_name)) + + dump_data = self.read_dump(dumppath) + + args = ["-wallet={}".format(wallet_name), + "-dumpfile={}".format(dumppath)] + if file_format is not None: + args.append("-format={}".format(file_format)) + args.append("createfromdump") + + load_output = "" + if file_format is not None and file_format != dump_data["format"]: + load_output += "Warning: Dumpfile wallet format \"{}\" does not match command line specified format \"{}\".\n".format(dump_data["format"], file_format) + self.assert_tool_output(load_output, *args) + assert os.path.isdir(os.path.join(self.nodes[0].datadir, "regtest/wallets", wallet_name)) + + self.assert_tool_output("The dumpfile may contain private keys. To ensure the safety of your Bitcoin, do not share the dumpfile.\n", '-wallet={}'.format(wallet_name), '-dumpfile={}'.format(rt_dumppath), 'dump') + + rt_dump_data = self.read_dump(rt_dumppath) + wallet_dat = os.path.join(self.nodes[0].datadir, "regtest/wallets/", wallet_name, "wallet.dat") + if rt_dump_data["format"] == "bdb": + self.assert_is_bdb(wallet_dat) + else: + self.assert_is_sqlite(wallet_dat) + def test_invalid_tool_commands_and_args(self): self.log.info('Testing that various invalid commands raise with specific error messages') self.assert_raises_tool_error('Invalid command: foo', 'foo') @@ -98,33 +216,7 @@ class ToolWalletTest(BitcoinTestFramework): # shasum_before = self.wallet_shasum() timestamp_before = self.wallet_timestamp() self.log.debug('Wallet file timestamp before calling info: {}'.format(timestamp_before)) - if self.options.descriptors: - out = textwrap.dedent('''\ - Wallet info - =========== - Name: default_wallet - Format: sqlite - Descriptors: yes - Encrypted: no - HD (hd seed available): yes - Keypool Size: 6 - Transactions: 0 - Address Book: 1 - ''') - else: - out = textwrap.dedent('''\ - Wallet info - =========== - Name: \ - - Format: bdb - Descriptors: no - Encrypted: no - HD (hd seed available): yes - Keypool Size: 2 - Transactions: 0 - Address Book: 3 - ''') + out = self.get_expected_info_output(address=1) self.assert_tool_output(out, '-wallet=' + self.default_wallet_name, 'info') timestamp_after = self.wallet_timestamp() self.log.debug('Wallet file timestamp after calling info: {}'.format(timestamp_after)) @@ -155,33 +247,7 @@ class ToolWalletTest(BitcoinTestFramework): shasum_before = self.wallet_shasum() timestamp_before = self.wallet_timestamp() self.log.debug('Wallet file timestamp before calling info: {}'.format(timestamp_before)) - if self.options.descriptors: - out = textwrap.dedent('''\ - Wallet info - =========== - Name: default_wallet - Format: sqlite - Descriptors: yes - Encrypted: no - HD (hd seed available): yes - Keypool Size: 6 - Transactions: 1 - Address Book: 1 - ''') - else: - out = textwrap.dedent('''\ - Wallet info - =========== - Name: \ - - Format: bdb - Descriptors: no - Encrypted: no - HD (hd seed available): yes - Keypool Size: 2 - Transactions: 1 - Address Book: 3 - ''') + out = self.get_expected_info_output(transactions=1, address=1) self.assert_tool_output(out, '-wallet=' + self.default_wallet_name, 'info') shasum_after = self.wallet_shasum() timestamp_after = self.wallet_timestamp() @@ -199,19 +265,7 @@ class ToolWalletTest(BitcoinTestFramework): shasum_before = self.wallet_shasum() timestamp_before = self.wallet_timestamp() self.log.debug('Wallet file timestamp before calling create: {}'.format(timestamp_before)) - out = textwrap.dedent('''\ - Topping up keypool... - Wallet info - =========== - Name: foo - Format: bdb - Descriptors: no - Encrypted: no - HD (hd seed available): yes - Keypool Size: 2000 - Transactions: 0 - Address Book: 0 - ''') + out = "Topping up keypool...\n" + self.get_expected_info_output(name="foo", keypool=2000) self.assert_tool_output(out, '-wallet=foo', 'create') shasum_after = self.wallet_shasum() timestamp_after = self.wallet_timestamp() @@ -237,9 +291,13 @@ class ToolWalletTest(BitcoinTestFramework): self.log.debug('Wallet file timestamp after calling getwalletinfo: {}'.format(timestamp_after)) assert_equal(0, out['txcount']) - assert_equal(1000, out['keypoolsize']) - assert_equal(1000, out['keypoolsize_hd_internal']) - assert_equal(True, 'hdseedid' in out) + if not self.options.descriptors: + assert_equal(1000, out['keypoolsize']) + assert_equal(1000, out['keypoolsize_hd_internal']) + assert_equal(True, 'hdseedid' in out) + else: + assert_equal(3000, out['keypoolsize']) + assert_equal(3000, out['keypoolsize_hd_internal']) self.log_wallet_timestamp_comparison(timestamp_before, timestamp_after) assert_equal(timestamp_before, timestamp_after) @@ -255,18 +313,95 @@ class ToolWalletTest(BitcoinTestFramework): self.assert_tool_output('', '-wallet=salvage', 'salvage') + def test_dump_createfromdump(self): + self.start_node(0) + self.nodes[0].createwallet("todump") + file_format = self.nodes[0].get_wallet_rpc("todump").getwalletinfo()["format"] + self.nodes[0].createwallet("todump2") + self.stop_node(0) + + self.log.info('Checking dump arguments') + self.assert_raises_tool_error('No dump file provided. To use dump, -dumpfile=<filename> must be provided.', '-wallet=todump', 'dump') + + self.log.info('Checking basic dump') + wallet_dump = os.path.join(self.nodes[0].datadir, "wallet.dump") + self.assert_tool_output('The dumpfile may contain private keys. To ensure the safety of your Bitcoin, do not share the dumpfile.\n', '-wallet=todump', '-dumpfile={}'.format(wallet_dump), 'dump') + + dump_data = self.read_dump(wallet_dump) + orig_dump = dump_data.copy() + # Check the dump magic + assert_equal(dump_data['BITCOIN_CORE_WALLET_DUMP'], '1') + # Check the file format + assert_equal(dump_data["format"], file_format) + + self.log.info('Checking that a dumpfile cannot be overwritten') + self.assert_raises_tool_error('File {} already exists. If you are sure this is what you want, move it out of the way first.'.format(wallet_dump), '-wallet=todump2', '-dumpfile={}'.format(wallet_dump), 'dump') + + self.log.info('Checking createfromdump arguments') + self.assert_raises_tool_error('No dump file provided. To use createfromdump, -dumpfile=<filename> must be provided.', '-wallet=todump', 'createfromdump') + non_exist_dump = os.path.join(self.nodes[0].datadir, "wallet.nodump") + self.assert_raises_tool_error('Unknown wallet file format "notaformat" provided. Please provide one of "bdb" or "sqlite".', '-wallet=todump', '-format=notaformat', '-dumpfile={}'.format(wallet_dump), 'createfromdump') + self.assert_raises_tool_error('Dump file {} does not exist.'.format(non_exist_dump), '-wallet=todump', '-dumpfile={}'.format(non_exist_dump), 'createfromdump') + wallet_path = os.path.join(self.nodes[0].datadir, 'regtest/wallets/todump2') + self.assert_raises_tool_error('Failed to create database path \'{}\'. Database already exists.'.format(wallet_path), '-wallet=todump2', '-dumpfile={}'.format(wallet_dump), 'createfromdump') + self.assert_raises_tool_error("The -descriptors option can only be used with the 'create' command.", '-descriptors', '-wallet=todump2', '-dumpfile={}'.format(wallet_dump), 'createfromdump') + + self.log.info('Checking createfromdump') + self.do_tool_createfromdump("load", "wallet.dump") + self.do_tool_createfromdump("load-bdb", "wallet.dump", "bdb") + if self.is_sqlite_compiled(): + self.do_tool_createfromdump("load-sqlite", "wallet.dump", "sqlite") + + self.log.info('Checking createfromdump handling of magic and versions') + bad_ver_wallet_dump = os.path.join(self.nodes[0].datadir, "wallet-bad_ver1.dump") + dump_data["BITCOIN_CORE_WALLET_DUMP"] = "0" + self.write_dump(dump_data, bad_ver_wallet_dump) + self.assert_raises_tool_error('Error: Dumpfile version is not supported. This version of bitcoin-wallet only supports version 1 dumpfiles. Got dumpfile with version 0', '-wallet=badload', '-dumpfile={}'.format(bad_ver_wallet_dump), 'createfromdump') + assert not os.path.isdir(os.path.join(self.nodes[0].datadir, "regtest/wallets", "badload")) + bad_ver_wallet_dump = os.path.join(self.nodes[0].datadir, "wallet-bad_ver2.dump") + dump_data["BITCOIN_CORE_WALLET_DUMP"] = "2" + self.write_dump(dump_data, bad_ver_wallet_dump) + self.assert_raises_tool_error('Error: Dumpfile version is not supported. This version of bitcoin-wallet only supports version 1 dumpfiles. Got dumpfile with version 2', '-wallet=badload', '-dumpfile={}'.format(bad_ver_wallet_dump), 'createfromdump') + assert not os.path.isdir(os.path.join(self.nodes[0].datadir, "regtest/wallets", "badload")) + bad_magic_wallet_dump = os.path.join(self.nodes[0].datadir, "wallet-bad_magic.dump") + del dump_data["BITCOIN_CORE_WALLET_DUMP"] + dump_data["not_the_right_magic"] = "1" + self.write_dump(dump_data, bad_magic_wallet_dump, "not_the_right_magic") + self.assert_raises_tool_error('Error: Dumpfile identifier record is incorrect. Got "not_the_right_magic", expected "BITCOIN_CORE_WALLET_DUMP".', '-wallet=badload', '-dumpfile={}'.format(bad_magic_wallet_dump), 'createfromdump') + assert not os.path.isdir(os.path.join(self.nodes[0].datadir, "regtest/wallets", "badload")) + + self.log.info('Checking createfromdump handling of checksums') + bad_sum_wallet_dump = os.path.join(self.nodes[0].datadir, "wallet-bad_sum1.dump") + dump_data = orig_dump.copy() + checksum = dump_data["checksum"] + dump_data["checksum"] = "1" * 64 + self.write_dump(dump_data, bad_sum_wallet_dump) + self.assert_raises_tool_error('Error: Dumpfile checksum does not match. Computed {}, expected {}'.format(checksum, "1" * 64), '-wallet=bad', '-dumpfile={}'.format(bad_sum_wallet_dump), 'createfromdump') + assert not os.path.isdir(os.path.join(self.nodes[0].datadir, "regtest/wallets", "badload")) + bad_sum_wallet_dump = os.path.join(self.nodes[0].datadir, "wallet-bad_sum2.dump") + del dump_data["checksum"] + self.write_dump(dump_data, bad_sum_wallet_dump, skip_checksum=True) + self.assert_raises_tool_error('Error: Missing checksum', '-wallet=badload', '-dumpfile={}'.format(bad_sum_wallet_dump), 'createfromdump') + assert not os.path.isdir(os.path.join(self.nodes[0].datadir, "regtest/wallets", "badload")) + bad_sum_wallet_dump = os.path.join(self.nodes[0].datadir, "wallet-bad_sum3.dump") + dump_data["checksum"] = "2" * 10 + self.write_dump(dump_data, bad_sum_wallet_dump) + self.assert_raises_tool_error('Error: Dumpfile checksum does not match. Computed {}, expected {}{}'.format(checksum, "2" * 10, "0" * 54), '-wallet=badload', '-dumpfile={}'.format(bad_sum_wallet_dump), 'createfromdump') + assert not os.path.isdir(os.path.join(self.nodes[0].datadir, "regtest/wallets", "badload")) + + def run_test(self): self.wallet_path = os.path.join(self.nodes[0].datadir, self.chain, 'wallets', self.default_wallet_name, self.wallet_data_filename) self.test_invalid_tool_commands_and_args() # Warning: The following tests are order-dependent. self.test_tool_wallet_info() self.test_tool_wallet_info_after_transaction() + self.test_tool_wallet_create_on_existing_wallet() + self.test_getwalletinfo_on_different_wallet() if not self.options.descriptors: - # TODO: Wallet tool needs more create options at which point these can be enabled. - self.test_tool_wallet_create_on_existing_wallet() - self.test_getwalletinfo_on_different_wallet() # Salvage is a legacy wallet only thing self.test_salvage() + self.test_dump_createfromdump() if __name__ == '__main__': ToolWalletTest().main() diff --git a/test/functional/wallet_balance.py b/test/functional/wallet_balance.py index 0e786ca595..433b40faee 100755 --- a/test/functional/wallet_balance.py +++ b/test/functional/wallet_balance.py @@ -263,8 +263,6 @@ class WalletTest(BitcoinTestFramework): self.log.info('Put txs back into mempool of node 1 (not node 0)') self.nodes[0].invalidateblock(block_reorg) self.nodes[1].invalidateblock(block_reorg) - self.sync_blocks() - self.nodes[0].syncwithvalidationinterfacequeue() assert_equal(self.nodes[0].getbalance(minconf=0), 0) # wallet txs not in the mempool are untrusted self.nodes[0].generatetoaddress(1, ADDRESS_WATCHONLY) assert_equal(self.nodes[0].getbalance(minconf=0), 0) # wallet txs not in the mempool are untrusted diff --git a/test/functional/wallet_basic.py b/test/functional/wallet_basic.py index ac4a6e4948..6bcb03e8ed 100755 --- a/test/functional/wallet_basic.py +++ b/test/functional/wallet_basic.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2014-2019 The Bitcoin Core developers +# Copyright (c) 2014-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 the wallet.""" @@ -235,7 +235,8 @@ class WalletTest(BitcoinTestFramework): fee_rate_btc_kvb = fee_rate_sat_vb * 1e3 / 1e8 explicit_fee_rate_btc_kvb = Decimal(fee_rate_btc_kvb) / 1000 - txid = self.nodes[2].sendmany(amounts={address: 10}, fee_rate=fee_rate_sat_vb) + # Test passing fee_rate as a string + txid = self.nodes[2].sendmany(amounts={address: 10}, fee_rate=str(fee_rate_sat_vb)) self.nodes[2].generate(1) self.sync_all(self.nodes[0:3]) balance = self.nodes[2].getbalance() @@ -244,6 +245,17 @@ class WalletTest(BitcoinTestFramework): node_0_bal += Decimal('10') assert_equal(self.nodes[0].getbalance(), node_0_bal) + # Test passing fee_rate as an integer + amount = Decimal("0.0001") + txid = self.nodes[2].sendmany(amounts={address: amount}, fee_rate=fee_rate_sat_vb) + self.nodes[2].generate(1) + self.sync_all(self.nodes[0:3]) + balance = self.nodes[2].getbalance() + node_2_bal = self.check_fee_amount(balance, node_2_bal - amount, explicit_fee_rate_btc_kvb, self.get_vsize(self.nodes[2].gettransaction(txid)['hex'])) + assert_equal(balance, node_2_bal) + node_0_bal += amount + assert_equal(self.nodes[0].getbalance(), node_0_bal) + for key in ["totalFee", "feeRate"]: assert_raises_rpc_error(-8, "Unknown named parameter key", self.nodes[2].sendtoaddress, address=address, amount=1, fee_rate=1, key=1) @@ -405,7 +417,7 @@ class WalletTest(BitcoinTestFramework): amount = 3 fee_rate_sat_vb = 2 fee_rate_btc_kvb = fee_rate_sat_vb * 1e3 / 1e8 - + # Test passing fee_rate as an integer txid = self.nodes[2].sendtoaddress(address=address, amount=amount, fee_rate=fee_rate_sat_vb) tx_size = self.get_vsize(self.nodes[2].gettransaction(txid)['hex']) self.nodes[0].generate(1) @@ -414,6 +426,19 @@ class WalletTest(BitcoinTestFramework): fee = prebalance - postbalance - Decimal(amount) assert_fee_amount(fee, tx_size, Decimal(fee_rate_btc_kvb)) + prebalance = self.nodes[2].getbalance() + amount = Decimal("0.001") + fee_rate_sat_vb = 1.23 + fee_rate_btc_kvb = fee_rate_sat_vb * 1e3 / 1e8 + # Test passing fee_rate as a string + txid = self.nodes[2].sendtoaddress(address=address, amount=amount, fee_rate=str(fee_rate_sat_vb)) + tx_size = self.get_vsize(self.nodes[2].gettransaction(txid)['hex']) + self.nodes[0].generate(1) + self.sync_all(self.nodes[0:3]) + postbalance = self.nodes[2].getbalance() + fee = prebalance - postbalance - amount + assert_fee_amount(fee, tx_size, Decimal(fee_rate_btc_kvb)) + for key in ["totalFee", "feeRate"]: assert_raises_rpc_error(-8, "Unknown named parameter key", self.nodes[2].sendtoaddress, address=address, amount=1, fee_rate=1, key=1) diff --git a/test/functional/wallet_bumpfee.py b/test/functional/wallet_bumpfee.py index 99c9737258..5fc8438e8f 100755 --- a/test/functional/wallet_bumpfee.py +++ b/test/functional/wallet_bumpfee.py @@ -149,7 +149,7 @@ def test_simple_bumpfee_succeeds(self, mode, rbf_node, peer_node, dest_address): self.sync_mempools((rbf_node, peer_node)) assert rbfid in rbf_node.getrawmempool() and rbfid in peer_node.getrawmempool() if mode == "fee_rate": - bumped_psbt = rbf_node.psbtbumpfee(rbfid, {"fee_rate": NORMAL}) + bumped_psbt = rbf_node.psbtbumpfee(rbfid, {"fee_rate": str(NORMAL)}) bumped_tx = rbf_node.bumpfee(rbfid, {"fee_rate": NORMAL}) else: bumped_psbt = rbf_node.psbtbumpfee(rbfid) @@ -425,6 +425,9 @@ def test_watchonly_psbt(self, peer_node, rbf_node, dest_address): original_txid = watcher.sendrawtransaction(psbt_final["hex"]) assert_equal(len(watcher.decodepsbt(psbt)["tx"]["vin"]), 1) + # bumpfee can't be used on watchonly wallets + assert_raises_rpc_error(-4, "bumpfee is not available with wallets that have private keys disabled. Use psbtbumpfee instead.", watcher.bumpfee, original_txid) + # Bump fee, obnoxiously high to add additional watchonly input bumped_psbt = watcher.psbtbumpfee(original_txid, {"fee_rate": HIGH}) assert_greater_than(len(watcher.decodepsbt(bumped_psbt['psbt'])["tx"]["vin"]), 1) diff --git a/test/functional/wallet_create_tx.py b/test/functional/wallet_create_tx.py index 0f11aca525..a39a3c8d9b 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-2019 The Bitcoin Core developers +# Copyright (c) 2018-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. diff --git a/test/functional/wallet_descriptor.py b/test/functional/wallet_descriptor.py index 0fec8ea4a4..1de41a5f96 100755 --- a/test/functional/wallet_descriptor.py +++ b/test/functional/wallet_descriptor.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2019 The Bitcoin Core developers +# Copyright (c) 2019-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 descriptor wallet function.""" diff --git a/test/functional/wallet_disable.py b/test/functional/wallet_disable.py index c2b30fb35b..78cf378642 100755 --- a/test/functional/wallet_disable.py +++ b/test/functional/wallet_disable.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2019 The Bitcoin Core developers +# Copyright (c) 2015-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 a node with the -disablewallet option. diff --git a/test/functional/wallet_encryption.py b/test/functional/wallet_encryption.py index 4509c1e0b2..0d702e44f6 100755 --- a/test/functional/wallet_encryption.py +++ b/test/functional/wallet_encryption.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 Wallet encryption""" @@ -78,7 +78,7 @@ class WalletEncryptionTest(BitcoinTestFramework): MAX_VALUE = 100000000 expected_time = int(time.time()) + MAX_VALUE - 600 self.nodes[0].walletpassphrase(passphrase2, MAX_VALUE - 600) - # give buffer for walletpassphrase, since it iterates over all crypted keys + # give buffer for walletpassphrase, since it iterates over all encrypted keys expected_time_with_buffer = time.time() + MAX_VALUE - 600 actual_time = self.nodes[0].getwalletinfo()['unlocked_until'] assert_greater_than_or_equal(actual_time, expected_time) diff --git a/test/functional/wallet_fallbackfee.py b/test/functional/wallet_fallbackfee.py index dbf853b35c..78eef4b790 100755 --- a/test/functional/wallet_fallbackfee.py +++ b/test/functional/wallet_fallbackfee.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2017-2018 The Bitcoin Core developers +# Copyright (c) 2017-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 wallet replace-by-fee capabilities in conjunction with the fallbackfee.""" diff --git a/test/functional/wallet_import_rescan.py b/test/functional/wallet_import_rescan.py index aad112b499..59089456e9 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-2019 The Bitcoin Core developers +# Copyright (c) 2014-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 wallet import RPCs. diff --git a/test/functional/wallet_importdescriptors.py b/test/functional/wallet_importdescriptors.py index 2903a84998..ed62ef0e9d 100755 --- a/test/functional/wallet_importdescriptors.py +++ b/test/functional/wallet_importdescriptors.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2019 The Bitcoin Core developers +# Copyright (c) 2019-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 the importdescriptors RPC. diff --git a/test/functional/wallet_importmulti.py b/test/functional/wallet_importmulti.py index f7fdd6e908..13186b9e1d 100755 --- a/test/functional/wallet_importmulti.py +++ b/test/functional/wallet_importmulti.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2014-2019 The Bitcoin Core developers +# Copyright (c) 2014-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 the importmulti RPC. @@ -64,7 +64,7 @@ class ImportMultiTest(BitcoinTestFramework): self.nodes[0].generate(1) self.nodes[1].generate(1) timestamp = self.nodes[1].getblock(self.nodes[1].getbestblockhash())['mediantime'] - self.nodes[1].syncwithvalidationinterfacequeue() + self.nodes[1].syncwithvalidationinterfacequeue() # Sync the timestamp to the wallet, so that importmulti works node0_address1 = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress()) diff --git a/test/functional/wallet_importprunedfunds.py b/test/functional/wallet_importprunedfunds.py index d10d8ae1c8..7635ce2139 100755 --- a/test/functional/wallet_importprunedfunds.py +++ b/test/functional/wallet_importprunedfunds.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2014-2018 The Bitcoin Core developers +# Copyright (c) 2014-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 the importprunedfunds and removeprunedfunds RPCs.""" diff --git a/test/functional/wallet_keypool.py b/test/functional/wallet_keypool.py index 51795aca23..3fe6adeebc 100755 --- a/test/functional/wallet_keypool.py +++ b/test/functional/wallet_keypool.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2014-2019 The Bitcoin Core developers +# Copyright (c) 2014-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 the wallet keypool and interaction with wallet encryption/locking.""" diff --git a/test/functional/wallet_keypool_topup.py b/test/functional/wallet_keypool_topup.py index 78e06c5916..5619d57947 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-2019 The Bitcoin Core developers +# Copyright (c) 2017-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 HD Wallet keypool restore function. diff --git a/test/functional/wallet_listsinceblock.py b/test/functional/wallet_listsinceblock.py index 6a1b9097c5..448720530c 100755 --- a/test/functional/wallet_listsinceblock.py +++ b/test/functional/wallet_listsinceblock.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2017-2019 The Bitcoin Core developers +# Copyright (c) 2017-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 the listsinceblock RPC.""" diff --git a/test/functional/wallet_listtransactions.py b/test/functional/wallet_listtransactions.py index 3e98208431..71573964de 100755 --- a/test/functional/wallet_listtransactions.py +++ b/test/functional/wallet_listtransactions.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2014-2019 The Bitcoin Core developers +# Copyright (c) 2014-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 the listtransactions API.""" diff --git a/test/functional/wallet_multiwallet.py b/test/functional/wallet_multiwallet.py index eeaa607db7..bb89e76a9a 100755 --- a/test/functional/wallet_multiwallet.py +++ b/test/functional/wallet_multiwallet.py @@ -23,9 +23,11 @@ from test_framework.util import ( ) got_loading_error = False + + def test_load_unload(node, name): global got_loading_error - for _ in range(10): + while True: if got_loading_error: return try: @@ -68,7 +70,7 @@ class MultiWalletTest(BitcoinTestFramework): return wallet_dir(name, "wallet.dat") return wallet_dir(name) - assert_equal(self.nodes[0].listwalletdir(), { 'wallets': [{ 'name': self.default_wallet_name }] }) + assert_equal(self.nodes[0].listwalletdir(), {'wallets': [{'name': self.default_wallet_name}]}) # check wallet.dat is created self.stop_nodes() @@ -127,7 +129,7 @@ class MultiWalletTest(BitcoinTestFramework): os.mkdir(wallet_dir('no_access')) os.chmod(wallet_dir('no_access'), 0) try: - with self.nodes[0].assert_debug_log(expected_msgs=['Too many levels of symbolic links', 'Error scanning']): + with self.nodes[0].assert_debug_log(expected_msgs=['Error scanning']): walletlist = self.nodes[0].listwalletdir()['wallets'] finally: # Need to ensure access is restored for cleanup @@ -278,7 +280,7 @@ class MultiWalletTest(BitcoinTestFramework): threads = [] for _ in range(3): n = node.cli if self.options.usecli else get_rpc_proxy(node.url, 1, timeout=600, coveragedir=node.coverage_dir) - t = Thread(target=test_load_unload, args=(n, wallet_names[2], )) + t = Thread(target=test_load_unload, args=(n, wallet_names[2])) t.start() threads.append(t) for t in threads: diff --git a/test/functional/wallet_reorgsrestore.py b/test/functional/wallet_reorgsrestore.py index a1d6b098ad..9a5866a361 100755 --- a/test/functional/wallet_reorgsrestore.py +++ b/test/functional/wallet_reorgsrestore.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2019 The Bitcoin Core developers +# Copyright (c) 2019-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. diff --git a/test/functional/wallet_resendwallettransactions.py b/test/functional/wallet_resendwallettransactions.py index 353deefcc1..78d88f8aa5 100755 --- a/test/functional/wallet_resendwallettransactions.py +++ b/test/functional/wallet_resendwallettransactions.py @@ -50,6 +50,7 @@ class ResendWalletTransactionsTest(BitcoinTestFramework): block.solve() node.submitblock(ToHex(block)) + # Set correct m_best_block_time, which is used in ResendWalletTransactions node.syncwithvalidationinterfacequeue() now = int(time.time()) diff --git a/test/functional/wallet_send.py b/test/functional/wallet_send.py index 192e9065e6..9835c5a2af 100755 --- a/test/functional/wallet_send.py +++ b/test/functional/wallet_send.py @@ -256,8 +256,8 @@ class WalletSendTest(BitcoinTestFramework): assert res["complete"] self.log.info("Test setting explicit fee rate") - res1 = self.test_send(from_wallet=w0, to_wallet=w1, amount=1, arg_fee_rate=1, add_to_wallet=False) - res2 = self.test_send(from_wallet=w0, to_wallet=w1, amount=1, fee_rate=1, add_to_wallet=False) + res1 = self.test_send(from_wallet=w0, to_wallet=w1, amount=1, arg_fee_rate="1", add_to_wallet=False) + res2 = self.test_send(from_wallet=w0, to_wallet=w1, amount=1, fee_rate="1", add_to_wallet=False) assert_equal(self.nodes[1].decodepsbt(res1["psbt"])["fee"], self.nodes[1].decodepsbt(res2["psbt"])["fee"]) res = self.test_send(from_wallet=w0, to_wallet=w1, amount=1, fee_rate=7, add_to_wallet=False) diff --git a/test/functional/wallet_startup.py b/test/functional/wallet_startup.py index d3119925f7..d96c2da686 100755 --- a/test/functional/wallet_startup.py +++ b/test/functional/wallet_startup.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2017-2019 The Bitcoin Core developers +# Copyright (c) 2017-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 wallet load on startup. diff --git a/test/functional/wallet_txn_clone.py b/test/functional/wallet_txn_clone.py index bdbbb3e530..893a2d9617 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-2019 The Bitcoin Core developers +# Copyright (c) 2014-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 the wallet accounts properly when there are cloned transactions with malleated scriptsigs.""" diff --git a/test/functional/wallet_txn_doublespend.py b/test/functional/wallet_txn_doublespend.py index 42de131354..c7f7a8546a 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-2019 The Bitcoin Core developers +# Copyright (c) 2014-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 the wallet accounts properly when there is a double-spend conflict.""" |