diff options
Diffstat (limited to 'test')
77 files changed, 1436 insertions, 686 deletions
diff --git a/test/config.ini.in b/test/config.ini.in index 35ee092be4..a1119dc739 100644 --- a/test/config.ini.in +++ b/test/config.ini.in @@ -9,6 +9,7 @@ SRCDIR=@abs_top_srcdir@ BUILDDIR=@abs_top_builddir@ EXEEXT=@EXEEXT@ +RPCAUTH=@abs_top_srcdir@/share/rpcauth/rpcauth.py [components] # Which components are enabled. These are commented out by `configure` if they were disabled when running config. diff --git a/test/functional/feature_bip68_sequence.py b/test/functional/feature_bip68_sequence.py index e5db9e18c7..d8521550b5 100755 --- a/test/functional/feature_bip68_sequence.py +++ b/test/functional/feature_bip68_sequence.py @@ -67,7 +67,7 @@ class BIP68Test(BitcoinTestFramework): # If sequence locks were used, this would require 1 block for the # input to mature. sequence_value = SEQUENCE_LOCKTIME_DISABLE_FLAG | 1 - tx1.vin = [CTxIn(COutPoint(int(utxo["txid"], 16), utxo["vout"]), nSequence=sequence_value)] + tx1.vin = [CTxIn(COutPoint(int(utxo["txid"], 16), utxo["vout"]), nSequence=sequence_value)] tx1.vout = [CTxOut(value, CScript([b'a']))] tx1_signed = self.nodes[0].signrawtransactionwithwallet(ToHex(tx1))["hex"] @@ -80,7 +80,7 @@ class BIP68Test(BitcoinTestFramework): tx2.nVersion = 2 sequence_value = sequence_value & 0x7fffffff tx2.vin = [CTxIn(COutPoint(tx1_id, 0), nSequence=sequence_value)] - tx2.vout = [CTxOut(int(value-self.relayfee*COIN), CScript([b'a']))] + tx2.vout = [CTxOut(int(value - self.relayfee * COIN), CScript([b'a' * 35]))] tx2.rehash() assert_raises_rpc_error(-26, NOT_FINAL_ERROR, self.nodes[0].sendrawtransaction, ToHex(tx2)) @@ -129,7 +129,7 @@ class BIP68Test(BitcoinTestFramework): # Track whether any sequence locks used should fail should_pass = True - + # Track whether this transaction was built with sequence locks using_sequence_locks = False @@ -222,7 +222,7 @@ class BIP68Test(BitcoinTestFramework): tx = CTransaction() tx.nVersion = 2 tx.vin = [CTxIn(COutPoint(orig_tx.sha256, 0), nSequence=sequence_value)] - tx.vout = [CTxOut(int(orig_tx.vout[0].nValue - relayfee*COIN), CScript([b'a']))] + tx.vout = [CTxOut(int(orig_tx.vout[0].nValue - relayfee * COIN), CScript([b'a' * 35]))] tx.rehash() if (orig_tx.hash in node.getrawmempool()): @@ -343,14 +343,14 @@ class BIP68Test(BitcoinTestFramework): tx2.rehash() self.nodes[0].sendrawtransaction(ToHex(tx2)) - + # Now make an invalid spend of tx2 according to BIP68 sequence_value = 100 # 100 block relative locktime tx3 = CTransaction() tx3.nVersion = 2 tx3.vin = [CTxIn(COutPoint(tx2.sha256, 0), nSequence=sequence_value)] - tx3.vout = [CTxOut(int(tx2.vout[0].nValue - self.relayfee*COIN), CScript([b'a']))] + tx3.vout = [CTxOut(int(tx2.vout[0].nValue - self.relayfee * COIN), CScript([b'a' * 35]))] tx3.rehash() assert_raises_rpc_error(-26, NOT_FINAL_ERROR, self.nodes[0].sendrawtransaction, ToHex(tx3)) diff --git a/test/functional/feature_block.py b/test/functional/feature_block.py index 181c7f3369..f943fdf176 100755 --- a/test/functional/feature_block.py +++ b/test/functional/feature_block.py @@ -32,6 +32,7 @@ from test_framework.script import ( OP_ELSE, OP_ENDIF, OP_EQUAL, + OP_DROP, OP_FALSE, OP_HASH160, OP_IF, @@ -82,10 +83,7 @@ class FullBlockTest(BitcoinTestFramework): def run_test(self): node = self.nodes[0] # convenience reference to the node - # reconnect_p2p() expects the network thread to be running - network_thread_start() - - self.reconnect_p2p() + self.bootstrap_p2p() # Add one p2p connection to the node self.block_heights = {} self.coinbase_key = CECKey() @@ -1218,7 +1216,7 @@ class FullBlockTest(BitcoinTestFramework): block.vtx.extend(tx_list) # this is a little handier to use than the version in blocktools.py - def create_tx(self, spend_tx, n, value, script=CScript([OP_TRUE])): + def create_tx(self, spend_tx, n, value, script=CScript([OP_TRUE, OP_DROP] * 15 + [OP_TRUE])): return create_transaction(spend_tx, n, b"", value, script) # sign a transaction, using the key we know about @@ -1296,17 +1294,28 @@ class FullBlockTest(BitcoinTestFramework): self.blocks[block_number] = block return block - def reconnect_p2p(self): + def bootstrap_p2p(self): """Add a P2P connection to the node. + Helper to connect and wait for version handshake.""" + self.nodes[0].add_p2p_connection(P2PDataStore()) + network_thread_start() + # We need to wait for the initial getheaders from the peer before we + # start populating our blockstore. If we don't, then we may run ahead + # to the next subtest before we receive the getheaders. We'd then send + # an INV for the next block and receive two getheaders - one for the + # IBD and one for the INV. We'd respond to both and could get + # unexpectedly disconnected if the DoS score for that error is 50. + self.nodes[0].p2p.wait_for_getheaders(timeout=5) + + def reconnect_p2p(self): + """Tear down and bootstrap the P2P connection to the node. + The node gets disconnected several times in this test. This helper method reconnects the p2p and restarts the network thread.""" - - network_thread_join() self.nodes[0].disconnect_p2ps() - self.nodes[0].add_p2p_connection(P2PDataStore()) - network_thread_start() - self.nodes[0].p2p.wait_for_verack() + network_thread_join() + self.bootstrap_p2p() def sync_blocks(self, blocks, success=True, reject_code=None, reject_reason=None, request_block=True, reconnect=False, timeout=60): """Sends blocks to test node. Syncs and verifies that tip has advanced to most recent block. diff --git a/test/functional/feature_config_args.py b/test/functional/feature_config_args.py index a1d22191af..e9924451d1 100755 --- a/test/functional/feature_config_args.py +++ b/test/functional/feature_config_args.py @@ -29,8 +29,13 @@ class ConfArgsTest(BitcoinTestFramework): # Check that using non-existent datadir in conf file fails conf_file = os.path.join(default_data_dir, "bitcoin.conf") - with open(conf_file, 'a', encoding='utf8') as f: + + # datadir needs to be set before [regtest] section + conf_file_contents = open(conf_file, encoding='utf8').read() + with open(conf_file, 'w', encoding='utf8') as f: f.write("datadir=" + new_data_dir + "\n") + f.write(conf_file_contents) + self.nodes[0].assert_start_raises_init_error(['-conf=' + conf_file], 'Error reading configuration file: specified data directory "' + new_data_dir + '" does not exist.') # Create the directory and ensure the config file now works diff --git a/test/functional/feature_includeconf.py b/test/functional/feature_includeconf.py new file mode 100755 index 0000000000..9ccb89af43 --- /dev/null +++ b/test/functional/feature_includeconf.py @@ -0,0 +1,75 @@ +#!/usr/bin/env python3 +# Copyright (c) 2018 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 the includeconf argument + +Verify that: + +1. adding includeconf to the configuration file causes the includeconf + file to be loaded in the correct order. +2. includeconf cannot be used as a command line argument. +3. includeconf cannot be used recursively (ie includeconf can only + be used from the base config file). +4. multiple includeconf arguments can be specified in the main config + file. +""" +import os + +from test_framework.test_framework import BitcoinTestFramework + +class IncludeConfTest(BitcoinTestFramework): + def set_test_params(self): + self.setup_clean_chain = False + self.num_nodes = 1 + + def setup_chain(self): + super().setup_chain() + # Create additional config files + # - tmpdir/node0/relative.conf + with open(os.path.join(self.options.tmpdir, "node0", "relative.conf"), "w", encoding="utf8") as f: + f.write("uacomment=relative\n") + # - tmpdir/node0/relative2.conf + with open(os.path.join(self.options.tmpdir, "node0", "relative2.conf"), "w", encoding="utf8") as f: + f.write("uacomment=relative2\n") + with open(os.path.join(self.options.tmpdir, "node0", "bitcoin.conf"), "a", encoding='utf8') as f: + f.write("uacomment=main\nincludeconf=relative.conf\n") + + def run_test(self): + self.log.info("-includeconf works from config file. subversion should end with 'main; relative)/'") + + subversion = self.nodes[0].getnetworkinfo()["subversion"] + assert subversion.endswith("main; relative)/") + + self.log.info("-includeconf cannot be used as command-line arg. subversion should still end with 'main; relative)/'") + self.stop_node(0) + + self.start_node(0, extra_args=["-includeconf=relative2.conf"]) + + subversion = self.nodes[0].getnetworkinfo()["subversion"] + assert subversion.endswith("main; relative)/") + self.stop_node(0, expected_stderr="warning: -includeconf cannot be used from commandline; ignoring -includeconf=relative2.conf") + + self.log.info("-includeconf cannot be used recursively. subversion should end with 'main; relative)/'") + with open(os.path.join(self.options.tmpdir, "node0", "relative.conf"), "a", encoding="utf8") as f: + f.write("includeconf=relative2.conf\n") + self.start_node(0) + + subversion = self.nodes[0].getnetworkinfo()["subversion"] + assert subversion.endswith("main; relative)/") + self.stop_node(0, expected_stderr="warning: -includeconf cannot be used from included files; ignoring -includeconf=relative2.conf") + + self.log.info("multiple -includeconf args can be used from the base config file. subversion should end with 'main; relative; relative2)/'") + with open(os.path.join(self.options.tmpdir, "node0", "relative.conf"), "w", encoding="utf8") as f: + f.write("uacomment=relative\n") + + with open(os.path.join(self.options.tmpdir, "node0", "bitcoin.conf"), "a", encoding='utf8') as f: + f.write("includeconf=relative2.conf\n") + + self.start_node(0) + + subversion = self.nodes[0].getnetworkinfo()["subversion"] + assert subversion.endswith("main; relative; relative2)/") + +if __name__ == '__main__': + IncludeConfTest().main() diff --git a/test/functional/feature_logging.py b/test/functional/feature_logging.py index 3c7aecf10a..166f8f8694 100755 --- a/test/functional/feature_logging.py +++ b/test/functional/feature_logging.py @@ -15,13 +15,17 @@ class LoggingTest(BitcoinTestFramework): self.num_nodes = 1 self.setup_clean_chain = True + def relative_log_path(self, name): + return os.path.join(self.nodes[0].datadir, "regtest", name) + def run_test(self): # test default log file name - assert os.path.isfile(os.path.join(self.nodes[0].datadir, "regtest", "debug.log")) + default_log_path = self.relative_log_path("debug.log") + assert os.path.isfile(default_log_path) # test alternative log file name in datadir self.restart_node(0, ["-debuglogfile=foo.log"]) - assert os.path.isfile(os.path.join(self.nodes[0].datadir, "regtest", "foo.log")) + assert os.path.isfile(self.relative_log_path("foo.log")) # test alternative log file name outside datadir tempname = os.path.join(self.options.tmpdir, "foo.log") @@ -29,7 +33,7 @@ class LoggingTest(BitcoinTestFramework): assert os.path.isfile(tempname) # check that invalid log (relative) will cause error - invdir = os.path.join(self.nodes[0].datadir, "regtest", "foo") + invdir = self.relative_log_path("foo") invalidname = os.path.join("foo", "foo.log") self.stop_node(0) exp_stderr = "Error: Could not open debug log file \S+$" @@ -55,6 +59,17 @@ class LoggingTest(BitcoinTestFramework): self.start_node(0, ["-debuglogfile=%s" % (invalidname)]) assert os.path.isfile(os.path.join(invdir, "foo.log")) + # check that -nodebuglogfile disables logging + self.stop_node(0) + os.unlink(default_log_path) + assert not os.path.isfile(default_log_path) + self.start_node(0, ["-nodebuglogfile"]) + assert not os.path.isfile(default_log_path) + + # just sanity check no crash here + self.stop_node(0) + self.start_node(0, ["-debuglogfile=%s" % os.devnull]) + if __name__ == '__main__': LoggingTest().main() diff --git a/test/functional/feature_maxuploadtarget.py b/test/functional/feature_maxuploadtarget.py index ce6ec76c61..0946f27b90 100755 --- a/test/functional/feature_maxuploadtarget.py +++ b/test/functional/feature_maxuploadtarget.py @@ -30,7 +30,7 @@ class TestP2PConn(P2PInterface): self.block_receive_map[message.block.sha256] += 1 class MaxUploadTest(BitcoinTestFramework): - + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 1 @@ -100,7 +100,7 @@ class MaxUploadTest(BitcoinTestFramework): assert_equal(p2p_conns[0].block_receive_map[big_old_block], i+1) assert_equal(len(self.nodes[0].getpeerinfo()), 3) - # At most a couple more tries should succeed (depending on how long + # At most a couple more tries should succeed (depending on how long # the test has been running so far). for i in range(3): p2p_conns[0].send_message(getdata_request) diff --git a/test/functional/feature_proxy.py b/test/functional/feature_proxy.py index 2eb1be47a5..2d10c547e2 100755 --- a/test/functional/feature_proxy.py +++ b/test/functional/feature_proxy.py @@ -79,9 +79,9 @@ class ProxyTest(BitcoinTestFramework): # Note: proxies are not used to connect to local nodes # this is because the proxy to use is based on CService.GetNetwork(), which return NET_UNROUTABLE for localhost args = [ - ['-listen', '-proxy=%s:%i' % (self.conf1.addr),'-proxyrandomize=1'], - ['-listen', '-proxy=%s:%i' % (self.conf1.addr),'-onion=%s:%i' % (self.conf2.addr),'-proxyrandomize=0'], - ['-listen', '-proxy=%s:%i' % (self.conf2.addr),'-proxyrandomize=1'], + ['-listen', '-proxy=%s:%i' % (self.conf1.addr),'-proxyrandomize=1'], + ['-listen', '-proxy=%s:%i' % (self.conf1.addr),'-onion=%s:%i' % (self.conf2.addr),'-proxyrandomize=0'], + ['-listen', '-proxy=%s:%i' % (self.conf2.addr),'-proxyrandomize=1'], [] ] if self.have_ipv6: @@ -182,7 +182,7 @@ class ProxyTest(BitcoinTestFramework): assert_equal(n1['onion']['proxy'], '%s:%i' % (self.conf2.addr)) assert_equal(n1['onion']['proxy_randomize_credentials'], False) assert_equal(n1['onion']['reachable'], True) - + n2 = networks_dict(self.nodes[2].getnetworkinfo()) for net in ['ipv4','ipv6','onion']: assert_equal(n2[net]['proxy'], '%s:%i' % (self.conf2.addr)) diff --git a/test/functional/feature_pruning.py b/test/functional/feature_pruning.py index 3adde8dd73..11a52b9ee2 100755 --- a/test/functional/feature_pruning.py +++ b/test/functional/feature_pruning.py @@ -124,7 +124,7 @@ class PruneTest(BitcoinTestFramework): # Reboot node 1 to clear its mempool (hopefully make the invalidate faster) # Lower the block max size so we don't keep mining all our big mempool transactions (from disconnected blocks) self.stop_node(1) - self.start_node(1, extra_args=["-maxreceivebuffer=20000","-blockmaxweight=20000", "-checkblocks=5", "-disablesafemode"]) + self.start_node(1, extra_args=["-maxreceivebuffer=20000","-blockmaxweight=20000", "-checkblocks=5"]) height = self.nodes[1].getblockcount() self.log.info("Current block height: %d" % height) @@ -147,7 +147,7 @@ class PruneTest(BitcoinTestFramework): # Reboot node1 to clear those giant tx's from mempool self.stop_node(1) - self.start_node(1, extra_args=["-maxreceivebuffer=20000","-blockmaxweight=20000", "-checkblocks=5", "-disablesafemode"]) + self.start_node(1, extra_args=["-maxreceivebuffer=20000","-blockmaxweight=20000", "-checkblocks=5"]) self.log.info("Generating new longer chain of 300 more blocks") self.nodes[1].generate(300) diff --git a/test/functional/feature_rbf.py b/test/functional/feature_rbf.py index d6ab5ecc37..c087960782 100755 --- a/test/functional/feature_rbf.py +++ b/test/functional/feature_rbf.py @@ -123,7 +123,7 @@ class ReplaceByFeeTest(BitcoinTestFramework): tx1a = CTransaction() tx1a.vin = [CTxIn(tx0_outpoint, nSequence=0)] - tx1a.vout = [CTxOut(1*COIN, CScript([b'a']))] + tx1a.vout = [CTxOut(1 * COIN, CScript([b'a' * 35]))] tx1a_hex = txToHex(tx1a) tx1a_txid = self.nodes[0].sendrawtransaction(tx1a_hex, True) @@ -132,7 +132,7 @@ class ReplaceByFeeTest(BitcoinTestFramework): # Should fail because we haven't changed the fee tx1b = CTransaction() tx1b.vin = [CTxIn(tx0_outpoint, nSequence=0)] - tx1b.vout = [CTxOut(1*COIN, CScript([b'b']))] + tx1b.vout = [CTxOut(1 * COIN, CScript([b'b' * 35]))] tx1b_hex = txToHex(tx1b) # This will raise an exception due to insufficient fee @@ -143,7 +143,7 @@ class ReplaceByFeeTest(BitcoinTestFramework): # Extra 0.1 BTC fee tx1b = CTransaction() tx1b.vin = [CTxIn(tx0_outpoint, nSequence=0)] - tx1b.vout = [CTxOut(int(0.9*COIN), CScript([b'b']))] + tx1b.vout = [CTxOut(int(0.9 * COIN), CScript([b'b' * 35]))] tx1b_hex = txToHex(tx1b) # Replacement still disabled even with "enough fee" assert_raises_rpc_error(-26, "txn-mempool-conflict", self.nodes[1].sendrawtransaction, tx1b_hex, True) @@ -175,7 +175,7 @@ class ReplaceByFeeTest(BitcoinTestFramework): remaining_value -= 1*COIN tx = CTransaction() tx.vin = [CTxIn(prevout, nSequence=0)] - tx.vout = [CTxOut(remaining_value, CScript([1]))] + tx.vout = [CTxOut(remaining_value, CScript([1, OP_DROP] * 15 + [1]))] tx_hex = txToHex(tx) txid = self.nodes[0].sendrawtransaction(tx_hex, True) chain_txids.append(txid) @@ -185,7 +185,7 @@ class ReplaceByFeeTest(BitcoinTestFramework): # child fees - 40 BTC - so this attempt is rejected. dbl_tx = CTransaction() dbl_tx.vin = [CTxIn(tx0_outpoint, nSequence=0)] - dbl_tx.vout = [CTxOut(initial_nValue - 30*COIN, CScript([1]))] + dbl_tx.vout = [CTxOut(initial_nValue - 30 * COIN, CScript([1] * 35))] dbl_tx_hex = txToHex(dbl_tx) # This will raise an exception due to insufficient fee @@ -194,7 +194,7 @@ class ReplaceByFeeTest(BitcoinTestFramework): # Accepted with sufficient fee dbl_tx = CTransaction() dbl_tx.vin = [CTxIn(tx0_outpoint, nSequence=0)] - dbl_tx.vout = [CTxOut(1*COIN, CScript([1]))] + dbl_tx.vout = [CTxOut(1 * COIN, CScript([1] * 35))] dbl_tx_hex = txToHex(dbl_tx) self.nodes[0].sendrawtransaction(dbl_tx_hex, True) @@ -247,7 +247,7 @@ class ReplaceByFeeTest(BitcoinTestFramework): # Attempt double-spend, will fail because too little fee paid dbl_tx = CTransaction() dbl_tx.vin = [CTxIn(tx0_outpoint, nSequence=0)] - dbl_tx.vout = [CTxOut(initial_nValue - fee*n, CScript([1]))] + dbl_tx.vout = [CTxOut(initial_nValue - fee * n, CScript([1] * 35))] dbl_tx_hex = txToHex(dbl_tx) # This will raise an exception due to insufficient fee assert_raises_rpc_error(-26, "insufficient fee", self.nodes[0].sendrawtransaction, dbl_tx_hex, True) @@ -255,7 +255,7 @@ class ReplaceByFeeTest(BitcoinTestFramework): # 1 BTC fee is enough dbl_tx = CTransaction() dbl_tx.vin = [CTxIn(tx0_outpoint, nSequence=0)] - dbl_tx.vout = [CTxOut(initial_nValue - fee*n - 1*COIN, CScript([1]))] + dbl_tx.vout = [CTxOut(initial_nValue - fee * n - 1 * COIN, CScript([1] * 35))] dbl_tx_hex = txToHex(dbl_tx) self.nodes[0].sendrawtransaction(dbl_tx_hex, True) @@ -275,7 +275,7 @@ class ReplaceByFeeTest(BitcoinTestFramework): dbl_tx = CTransaction() dbl_tx.vin = [CTxIn(tx0_outpoint, nSequence=0)] - dbl_tx.vout = [CTxOut(initial_nValue - 2*fee*n, CScript([1]))] + dbl_tx.vout = [CTxOut(initial_nValue - 2 * fee * n, CScript([1] * 35))] dbl_tx_hex = txToHex(dbl_tx) # This will raise an exception assert_raises_rpc_error(-26, "too many potential replacements", self.nodes[0].sendrawtransaction, dbl_tx_hex, True) @@ -290,7 +290,7 @@ class ReplaceByFeeTest(BitcoinTestFramework): tx1a = CTransaction() tx1a.vin = [CTxIn(tx0_outpoint, nSequence=0)] - tx1a.vout = [CTxOut(1*COIN, CScript([b'a']))] + tx1a.vout = [CTxOut(1 * COIN, CScript([b'a' * 35]))] tx1a_hex = txToHex(tx1a) self.nodes[0].sendrawtransaction(tx1a_hex, True) @@ -311,7 +311,7 @@ class ReplaceByFeeTest(BitcoinTestFramework): tx1a = CTransaction() tx1a.vin = [CTxIn(utxo1, nSequence=0)] - tx1a.vout = [CTxOut(int(1.1*COIN), CScript([b'a']))] + tx1a.vout = [CTxOut(int(1.1 * COIN), CScript([b'a' * 35]))] tx1a_hex = txToHex(tx1a) tx1a_txid = self.nodes[0].sendrawtransaction(tx1a_hex, True) @@ -330,7 +330,7 @@ class ReplaceByFeeTest(BitcoinTestFramework): # Spend tx1a's output to test the indirect case. tx1b = CTransaction() tx1b.vin = [CTxIn(COutPoint(tx1a_txid, 0), nSequence=0)] - tx1b.vout = [CTxOut(1*COIN, CScript([b'a']))] + tx1b.vout = [CTxOut(1 * COIN, CScript([b'a' * 35]))] tx1b_hex = txToHex(tx1b) tx1b_txid = self.nodes[0].sendrawtransaction(tx1b_hex, True) tx1b_txid = int(tx1b_txid, 16) @@ -351,7 +351,7 @@ class ReplaceByFeeTest(BitcoinTestFramework): tx1 = CTransaction() tx1.vin = [CTxIn(confirmed_utxo)] - tx1.vout = [CTxOut(1*COIN, CScript([b'a']))] + tx1.vout = [CTxOut(1 * COIN, CScript([b'a' * 35]))] tx1_hex = txToHex(tx1) self.nodes[0].sendrawtransaction(tx1_hex, True) @@ -390,7 +390,7 @@ class ReplaceByFeeTest(BitcoinTestFramework): for i in range(MAX_REPLACEMENT_LIMIT+1): tx_i = CTransaction() tx_i.vin = [CTxIn(COutPoint(txid, i), nSequence=0)] - tx_i.vout = [CTxOut(split_value-fee, CScript([b'a']))] + tx_i.vout = [CTxOut(split_value - fee, CScript([b'a' * 35]))] tx_i_hex = txToHex(tx_i) self.nodes[0].sendrawtransaction(tx_i_hex, True) @@ -423,14 +423,14 @@ class ReplaceByFeeTest(BitcoinTestFramework): # Create a non-opting in transaction tx1a = CTransaction() tx1a.vin = [CTxIn(tx0_outpoint, nSequence=0xffffffff)] - tx1a.vout = [CTxOut(1*COIN, CScript([b'a']))] + tx1a.vout = [CTxOut(1 * COIN, CScript([b'a' * 35]))] tx1a_hex = txToHex(tx1a) tx1a_txid = self.nodes[0].sendrawtransaction(tx1a_hex, True) # Shouldn't be able to double-spend tx1b = CTransaction() tx1b.vin = [CTxIn(tx0_outpoint, nSequence=0)] - tx1b.vout = [CTxOut(int(0.9*COIN), CScript([b'b']))] + tx1b.vout = [CTxOut(int(0.9 * COIN), CScript([b'b' * 35]))] tx1b_hex = txToHex(tx1b) # This will raise an exception @@ -441,14 +441,14 @@ class ReplaceByFeeTest(BitcoinTestFramework): # Create a different non-opting in transaction tx2a = CTransaction() tx2a.vin = [CTxIn(tx1_outpoint, nSequence=0xfffffffe)] - tx2a.vout = [CTxOut(1*COIN, CScript([b'a']))] + tx2a.vout = [CTxOut(1 * COIN, CScript([b'a' * 35]))] tx2a_hex = txToHex(tx2a) tx2a_txid = self.nodes[0].sendrawtransaction(tx2a_hex, True) # Still shouldn't be able to double-spend tx2b = CTransaction() tx2b.vin = [CTxIn(tx1_outpoint, nSequence=0)] - tx2b.vout = [CTxOut(int(0.9*COIN), CScript([b'b']))] + tx2b.vout = [CTxOut(int(0.9 * COIN), CScript([b'b' * 35]))] tx2b_hex = txToHex(tx2b) # This will raise an exception @@ -471,12 +471,12 @@ class ReplaceByFeeTest(BitcoinTestFramework): tx3b = CTransaction() tx3b.vin = [CTxIn(COutPoint(tx1a_txid, 0), nSequence=0)] - tx3b.vout = [CTxOut(int(0.5*COIN), CScript([b'e']))] + tx3b.vout = [CTxOut(int(0.5 * COIN), CScript([b'e' * 35]))] tx3b_hex = txToHex(tx3b) tx3c = CTransaction() tx3c.vin = [CTxIn(COutPoint(tx2a_txid, 0), nSequence=0)] - tx3c.vout = [CTxOut(int(0.5*COIN), CScript([b'f']))] + tx3c.vout = [CTxOut(int(0.5 * COIN), CScript([b'f' * 35]))] tx3c_hex = txToHex(tx3c) self.nodes[0].sendrawtransaction(tx3b_hex, True) @@ -493,7 +493,7 @@ class ReplaceByFeeTest(BitcoinTestFramework): tx1a = CTransaction() tx1a.vin = [CTxIn(tx0_outpoint, nSequence=0)] - tx1a.vout = [CTxOut(1*COIN, CScript([b'a']))] + tx1a.vout = [CTxOut(1 * COIN, CScript([b'a' * 35]))] tx1a_hex = txToHex(tx1a) tx1a_txid = self.nodes[0].sendrawtransaction(tx1a_hex, True) @@ -519,14 +519,14 @@ class ReplaceByFeeTest(BitcoinTestFramework): tx2a = CTransaction() tx2a.vin = [CTxIn(tx1_outpoint, nSequence=0)] - tx2a.vout = [CTxOut(1*COIN, CScript([b'a']))] + tx2a.vout = [CTxOut(1 * COIN, CScript([b'a' * 35]))] tx2a_hex = txToHex(tx2a) self.nodes[0].sendrawtransaction(tx2a_hex, True) # Lower fee, but we'll prioritise it tx2b = CTransaction() tx2b.vin = [CTxIn(tx1_outpoint, nSequence=0)] - tx2b.vout = [CTxOut(int(1.01*COIN), CScript([b'a']))] + tx2b.vout = [CTxOut(int(1.01 * COIN), CScript([b'a' * 35]))] tx2b.rehash() tx2b_hex = txToHex(tx2b) diff --git a/test/functional/feature_segwit.py b/test/functional/feature_segwit.py index fa1732c4c5..3622e1834a 100755 --- a/test/functional/feature_segwit.py +++ b/test/functional/feature_segwit.py @@ -16,7 +16,7 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.util import * from test_framework.mininode import sha256, CTransaction, CTxIn, COutPoint, CTxOut, COIN, ToHex, FromHex from test_framework.address import script_to_p2sh, key_to_p2pkh -from test_framework.script import CScript, OP_HASH160, OP_CHECKSIG, OP_0, hash160, OP_EQUAL, OP_DUP, OP_EQUALVERIFY, OP_1, OP_2, OP_CHECKMULTISIG, OP_TRUE +from test_framework.script import CScript, OP_HASH160, OP_CHECKSIG, OP_0, hash160, OP_EQUAL, OP_DUP, OP_EQUALVERIFY, OP_1, OP_2, OP_CHECKMULTISIG, OP_TRUE, OP_DROP from io import BytesIO NODE_0 = 0 @@ -150,19 +150,11 @@ class SegWitTest(BitcoinTestFramework): self.skip_mine(self.nodes[2], p2sh_ids[NODE_2][WIT_V0][0], True) #block 426 self.skip_mine(self.nodes[2], p2sh_ids[NODE_2][WIT_V1][0], True) #block 427 - # TODO: An old node would see these txs without witnesses and be able to mine them - - self.log.info("Verify unsigned bare witness txs in versionbits-setting blocks are valid before the fork") - self.success_mine(self.nodes[2], wit_ids[NODE_2][WIT_V0][1], False) #block 428 - self.success_mine(self.nodes[2], wit_ids[NODE_2][WIT_V1][1], False) #block 429 - self.log.info("Verify unsigned p2sh witness txs without a redeem script are invalid") self.fail_accept(self.nodes[2], "mandatory-script-verify-flag", p2sh_ids[NODE_2][WIT_V0][1], False) self.fail_accept(self.nodes[2], "mandatory-script-verify-flag", p2sh_ids[NODE_2][WIT_V1][1], False) - self.log.info("Verify unsigned p2sh witness txs with a redeem script in versionbits-settings blocks are valid before the fork") - self.success_mine(self.nodes[2], p2sh_ids[NODE_2][WIT_V0][1], False, witness_script(False, self.pubkey[2])) #block 430 - self.success_mine(self.nodes[2], p2sh_ids[NODE_2][WIT_V1][1], False, witness_script(True, self.pubkey[2])) #block 431 + self.nodes[2].generate(4) # blocks 428-431 self.log.info("Verify previous witness txs skipped for mining can now be mined") assert_equal(len(self.nodes[2].getrawmempool()), 4) @@ -220,7 +212,7 @@ class SegWitTest(BitcoinTestFramework): # Now create tx2, which will spend from txid1. tx = CTransaction() tx.vin.append(CTxIn(COutPoint(int(txid1, 16), 0), b'')) - tx.vout.append(CTxOut(int(49.99*COIN), CScript([OP_TRUE]))) + tx.vout.append(CTxOut(int(49.99 * COIN), CScript([OP_TRUE, OP_DROP] * 15 + [OP_TRUE]))) tx2_hex = self.nodes[0].signrawtransactionwithwallet(ToHex(tx))['hex'] txid2 = self.nodes[0].sendrawtransaction(tx2_hex) tx = FromHex(CTransaction(), tx2_hex) @@ -229,7 +221,7 @@ class SegWitTest(BitcoinTestFramework): # Now create tx3, which will spend from txid2 tx = CTransaction() tx.vin.append(CTxIn(COutPoint(int(txid2, 16), 0), b"")) - tx.vout.append(CTxOut(int(49.95*COIN), CScript([OP_TRUE]))) # Huge fee + tx.vout.append(CTxOut(int(49.95 * COIN), CScript([OP_TRUE, OP_DROP] * 15 + [OP_TRUE]))) # Huge fee tx.calc_sha256() txid3 = self.nodes[0].sendrawtransaction(ToHex(tx)) assert(tx.wit.is_null()) @@ -311,8 +303,10 @@ class SegWitTest(BitcoinTestFramework): v = self.nodes[0].getaddressinfo(i) if (v['isscript']): [bare, p2sh, p2wsh, p2sh_p2wsh] = self.p2sh_address_to_script(v) - # bare and p2sh multisig with compressed keys should always be spendable - spendable_anytime.extend([bare, p2sh]) + # p2sh multisig with compressed keys should always be spendable + spendable_anytime.extend([p2sh]) + # bare multisig can be watched and signed, but is not treated as ours + solvable_after_importaddress.extend([bare]) # P2WSH and P2SH(P2WSH) multisig with compressed keys are spendable after direct importaddress spendable_after_importaddress.extend([p2wsh, p2sh_p2wsh]) else: @@ -328,8 +322,10 @@ class SegWitTest(BitcoinTestFramework): v = self.nodes[0].getaddressinfo(i) if (v['isscript']): [bare, p2sh, p2wsh, p2sh_p2wsh] = self.p2sh_address_to_script(v) - # bare and p2sh multisig with uncompressed keys should always be spendable - spendable_anytime.extend([bare, p2sh]) + # p2sh multisig with uncompressed keys should always be spendable + spendable_anytime.extend([p2sh]) + # bare multisig can be watched and signed, but is not treated as ours + solvable_after_importaddress.extend([bare]) # P2WSH and P2SH(P2WSH) multisig with uncompressed keys are never seen unseen_anytime.extend([p2wsh, p2sh_p2wsh]) else: diff --git a/test/functional/interface_rest.py b/test/functional/interface_rest.py index 2ee33aa869..a48939d2e0 100755 --- a/test/functional/interface_rest.py +++ b/test/functional/interface_rest.py @@ -186,10 +186,10 @@ class RESTTest (BitcoinTestFramework): self.test_rest_request("/getutxos/checkmempool", http_method='POST', req_type=ReqType.JSON, status=400, ret_type=RetType.OBJ) # Test limits - long_uri = '/'.join(["{}-{}".format(txid, n) for n in range(20)]) + long_uri = '/'.join(["{}-{}".format(txid, n_) for n_ in range(20)]) self.test_rest_request("/getutxos/checkmempool/{}".format(long_uri), http_method='POST', status=400, ret_type=RetType.OBJ) - long_uri = '/'.join(['{}-{}'.format(txid, n) for n in range(15)]) + long_uri = '/'.join(['{}-{}'.format(txid, n_) for n_ in range(15)]) self.test_rest_request("/getutxos/checkmempool/{}".format(long_uri), http_method='POST', status=200) self.nodes[0].generate(1) # generate block to not affect upcoming tests diff --git a/test/functional/interface_zmq.py b/test/functional/interface_zmq.py index 86ccea4394..af2e752b7a 100755 --- a/test/functional/interface_zmq.py +++ b/test/functional/interface_zmq.py @@ -4,7 +4,6 @@ # file COPYING or http://www.opensource.org/licenses/mit-license.php. """Test the ZMQ notification interface.""" import configparser -import os import struct from test_framework.test_framework import BitcoinTestFramework, SkipTest @@ -47,8 +46,6 @@ class ZMQTest (BitcoinTestFramework): # Check that bitcoin has been built with ZMQ enabled. config = configparser.ConfigParser() - if not self.options.configfile: - self.options.configfile = os.path.abspath(os.path.join(os.path.dirname(__file__), "../config.ini")) config.read_file(open(self.options.configfile)) if not config["components"].getboolean("ENABLE_ZMQ"): diff --git a/test/functional/mempool_packages.py b/test/functional/mempool_packages.py index 8880db8002..79cf159f43 100755 --- a/test/functional/mempool_packages.py +++ b/test/functional/mempool_packages.py @@ -70,7 +70,10 @@ class MempoolPackagesTest(BitcoinTestFramework): assert_equal(mempool[x]['descendantcount'], descendant_count) descendant_fees += mempool[x]['fee'] assert_equal(mempool[x]['modifiedfee'], mempool[x]['fee']) + assert_equal(mempool[x]['fees']['base'], mempool[x]['fee']) + assert_equal(mempool[x]['fees']['modified'], mempool[x]['modifiedfee']) assert_equal(mempool[x]['descendantfees'], descendant_fees * COIN) + assert_equal(mempool[x]['fees']['descendant'], descendant_fees) descendant_size += mempool[x]['size'] assert_equal(mempool[x]['descendantsize'], descendant_size) descendant_count += 1 @@ -132,6 +135,7 @@ class MempoolPackagesTest(BitcoinTestFramework): ancestor_fees = 0 for x in chain: ancestor_fees += mempool[x]['fee'] + assert_equal(mempool[x]['fees']['ancestor'], ancestor_fees + Decimal('0.00001')) assert_equal(mempool[x]['ancestorfees'], ancestor_fees * COIN + 1000) # Undo the prioritisetransaction for later tests @@ -145,6 +149,7 @@ class MempoolPackagesTest(BitcoinTestFramework): descendant_fees = 0 for x in reversed(chain): descendant_fees += mempool[x]['fee'] + assert_equal(mempool[x]['fees']['descendant'], descendant_fees + Decimal('0.00001')) assert_equal(mempool[x]['descendantfees'], descendant_fees * COIN + 1000) # Adding one more transaction on to the chain should fail. @@ -170,7 +175,9 @@ class MempoolPackagesTest(BitcoinTestFramework): descendant_fees += mempool[x]['fee'] if (x == chain[-1]): assert_equal(mempool[x]['modifiedfee'], mempool[x]['fee']+satoshi_round(0.00002)) + assert_equal(mempool[x]['fees']['modified'], mempool[x]['fee']+satoshi_round(0.00002)) assert_equal(mempool[x]['descendantfees'], descendant_fees * COIN + 2000) + assert_equal(mempool[x]['fees']['descendant'], descendant_fees+satoshi_round(0.00002)) # TODO: check that node1's mempool is as expected diff --git a/test/functional/mining_prioritisetransaction.py b/test/functional/mining_prioritisetransaction.py index e754dd31ad..85f1af6682 100755 --- a/test/functional/mining_prioritisetransaction.py +++ b/test/functional/mining_prioritisetransaction.py @@ -120,7 +120,7 @@ class PrioritiseTransactionTest(BitcoinTestFramework): tx_id = self.nodes[0].decoderawtransaction(tx_hex)["txid"] # This will raise an exception due to min relay fee not being met - assert_raises_rpc_error(-26, "min relay fee not met (code 66)", self.nodes[0].sendrawtransaction, tx_hex) + assert_raises_rpc_error(-26, "min relay fee not met", self.nodes[0].sendrawtransaction, tx_hex) assert(tx_id not in self.nodes[0].getrawmempool()) # This is a less than 1000-byte transaction, so just set the fee diff --git a/test/functional/p2p_compactblocks.py b/test/functional/p2p_compactblocks.py index 1657d97281..cb4c9867a3 100755 --- a/test/functional/p2p_compactblocks.py +++ b/test/functional/p2p_compactblocks.py @@ -12,7 +12,8 @@ from test_framework.mininode import * from test_framework.test_framework import BitcoinTestFramework from test_framework.util import * from test_framework.blocktools import create_block, create_coinbase, add_witness_commitment -from test_framework.script import CScript, OP_TRUE +from test_framework.script import CScript, OP_TRUE, OP_DROP + # TestP2PConn: A peer we use to send messages to bitcoind, and store responses. class TestP2PConn(P2PInterface): @@ -423,7 +424,7 @@ class CompactBlocksTest(BitcoinTestFramework): for i in range(num_transactions): tx = CTransaction() tx.vin.append(CTxIn(COutPoint(utxo[0], utxo[1]), b'')) - tx.vout.append(CTxOut(utxo[2] - 1000, CScript([OP_TRUE]))) + tx.vout.append(CTxOut(utxo[2] - 1000, CScript([OP_TRUE, OP_DROP] * 15 + [OP_TRUE]))) tx.rehash() utxo = [tx.sha256, 0, tx.vout[0].nValue] block.vtx.append(tx) diff --git a/test/functional/p2p_feefilter.py b/test/functional/p2p_feefilter.py index 7c954cdca2..c304bbba85 100755 --- a/test/functional/p2p_feefilter.py +++ b/test/functional/p2p_feefilter.py @@ -69,7 +69,7 @@ class FeeFilterTest(BitcoinTestFramework): # Change tx fee rate to 10 sat/byte and test they are no longer received node1.settxfee(Decimal("0.00010000")) [node1.sendtoaddress(node1.getnewaddress(), 1) for x in range(3)] - sync_mempools(self.nodes) # must be sure node 0 has received all txs + sync_mempools(self.nodes) # must be sure node 0 has received all txs # Send one transaction from node0 that should be received, so that we # we can sync the test on receipt (if node1's txs were relayed, they'd diff --git a/test/functional/p2p_invalid_tx.py b/test/functional/p2p_invalid_tx.py index 69ce529ad6..a12c1d6023 100755 --- a/test/functional/p2p_invalid_tx.py +++ b/test/functional/p2p_invalid_tx.py @@ -6,24 +6,48 @@ In this test we connect to one node over p2p, and test tx requests.""" from test_framework.blocktools import create_block, create_coinbase, create_transaction -from test_framework.messages import COIN -from test_framework.mininode import network_thread_start, P2PDataStore +from test_framework.messages import ( + COIN, + COutPoint, + CTransaction, + CTxIn, + CTxOut, +) +from test_framework.mininode import network_thread_start, P2PDataStore, network_thread_join from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import ( + assert_equal, + wait_until, +) -class InvalidTxRequestTest(BitcoinTestFramework): +class InvalidTxRequestTest(BitcoinTestFramework): def set_test_params(self): self.num_nodes = 1 self.setup_clean_chain = True - self.extra_args = [["-whitelist=127.0.0.1"]] + + def bootstrap_p2p(self, *, num_connections=1): + """Add a P2P connection to the node. + + Helper to connect and wait for version handshake.""" + for _ in range(num_connections): + self.nodes[0].add_p2p_connection(P2PDataStore()) + network_thread_start() + self.nodes[0].p2p.wait_for_verack() + + def reconnect_p2p(self, **kwargs): + """Tear down and bootstrap the P2P connection to the node. + + The node gets disconnected several times in this test. This helper + method reconnects the p2p and restarts the network thread.""" + self.nodes[0].disconnect_p2ps() + network_thread_join() + self.bootstrap_p2p(**kwargs) def run_test(self): - # Add p2p connection to node0 node = self.nodes[0] # convenience reference to the node - node.add_p2p_connection(P2PDataStore()) - network_thread_start() - node.p2p.wait_for_verack() + self.bootstrap_p2p() # Add one p2p connection to the node best_block = self.nodes[0].getbestblockhash() tip = int(best_block, 16) @@ -44,12 +68,74 @@ class InvalidTxRequestTest(BitcoinTestFramework): # b'\x64' is OP_NOTIF # Transaction will be rejected with code 16 (REJECT_INVALID) - tx1 = create_transaction(block1.vtx[0], 0, b'\x64', 50 * COIN - 12000) - node.p2p.send_txs_and_test([tx1], node, success=False, reject_code=16, reject_reason=b'mandatory-script-verify-flag-failed (Invalid OP_IF construction)') + # and we get disconnected immediately + self.log.info('Test a transaction that is rejected') + tx1 = create_transaction(block1.vtx[0], 0, b'\x64' * 35, 50 * COIN - 12000) + node.p2p.send_txs_and_test([tx1], node, success=False, expect_disconnect=True) + + # Make two p2p connections to provide the node with orphans + # * p2ps[0] will send valid orphan txs (one with low fee) + # * p2ps[1] will send an invalid orphan tx (and is later disconnected for that) + self.reconnect_p2p(num_connections=2) + + self.log.info('Test orphan transaction handling ... ') + # Create a root transaction that we withold until all dependend transactions + # are sent out and in the orphan cache + SCRIPT_PUB_KEY_OP_TRUE = b'\x51\x75' * 15 + b'\x51' + tx_withhold = CTransaction() + tx_withhold.vin.append(CTxIn(outpoint=COutPoint(block1.vtx[0].sha256, 0))) + tx_withhold.vout.append(CTxOut(nValue=50 * COIN - 12000, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE)) + tx_withhold.calc_sha256() + + # Our first orphan tx with some outputs to create further orphan txs + tx_orphan_1 = CTransaction() + tx_orphan_1.vin.append(CTxIn(outpoint=COutPoint(tx_withhold.sha256, 0))) + tx_orphan_1.vout = [CTxOut(nValue=10 * COIN, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE)] * 3 + tx_orphan_1.calc_sha256() + + # A valid transaction with low fee + tx_orphan_2_no_fee = CTransaction() + tx_orphan_2_no_fee.vin.append(CTxIn(outpoint=COutPoint(tx_orphan_1.sha256, 0))) + tx_orphan_2_no_fee.vout.append(CTxOut(nValue=10 * COIN, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE)) + + # A valid transaction with sufficient fee + tx_orphan_2_valid = CTransaction() + tx_orphan_2_valid.vin.append(CTxIn(outpoint=COutPoint(tx_orphan_1.sha256, 1))) + tx_orphan_2_valid.vout.append(CTxOut(nValue=10 * COIN - 12000, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE)) + tx_orphan_2_valid.calc_sha256() + + # An invalid transaction with negative fee + tx_orphan_2_invalid = CTransaction() + tx_orphan_2_invalid.vin.append(CTxIn(outpoint=COutPoint(tx_orphan_1.sha256, 2))) + tx_orphan_2_invalid.vout.append(CTxOut(nValue=11 * COIN, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE)) + + self.log.info('Send the orphans ... ') + # Send valid orphan txs from p2ps[0] + node.p2p.send_txs_and_test([tx_orphan_1, tx_orphan_2_no_fee, tx_orphan_2_valid], node, success=False) + # Send invalid tx from p2ps[1] + node.p2ps[1].send_txs_and_test([tx_orphan_2_invalid], node, success=False) + + assert_equal(0, node.getmempoolinfo()['size']) # Mempool should be empty + assert_equal(2, len(node.getpeerinfo())) # p2ps[1] is still connected + + self.log.info('Send the withhold tx ... ') + node.p2p.send_txs_and_test([tx_withhold], node, success=True) + + # Transactions that should end up in the mempool + expected_mempool = { + t.hash + for t in [ + tx_withhold, # The transaction that is the root for all orphans + tx_orphan_1, # The orphan transaction that splits the coins + tx_orphan_2_valid, # The valid transaction (with sufficient fee) + ] + } + # 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) - # Verify valid transaction - tx1 = create_transaction(block1.vtx[0], 0, b'', 50 * COIN - 12000) - node.p2p.send_txs_and_test([tx1], node, success=True) + wait_until(lambda: 1 == len(node.getpeerinfo()), timeout=12) # p2ps[1] is no longer connected + assert_equal(expected_mempool, set(node.getrawmempool())) if __name__ == '__main__': diff --git a/test/functional/p2p_mempool.py b/test/functional/p2p_mempool.py index 485a8af3d0..e54843b26f 100755 --- a/test/functional/p2p_mempool.py +++ b/test/functional/p2p_mempool.py @@ -30,6 +30,6 @@ class P2PMempoolTests(BitcoinTestFramework): #mininode must be disconnected at this point assert_equal(len(self.nodes[0].getpeerinfo()), 0) - + if __name__ == '__main__': P2PMempoolTests().main() diff --git a/test/functional/p2p_segwit.py b/test/functional/p2p_segwit.py index 5546bf6b29..e80a764477 100755 --- a/test/functional/p2p_segwit.py +++ b/test/functional/p2p_segwit.py @@ -10,6 +10,7 @@ from test_framework.util import * from test_framework.script import * from test_framework.blocktools import create_block, create_coinbase, add_witness_commitment, get_witness_script, WITNESS_COMMITMENT_HEADER from test_framework.key import CECKey, CPubKey +import math import time import random from binascii import hexlify @@ -47,7 +48,7 @@ def test_transaction_acceptance(rpc, p2p, tx, with_witness, accepted, reason=Non with mininode_lock: assert_equal(p2p.last_message["reject"].reason, reason) -def test_witness_block(rpc, p2p, block, accepted, with_witness=True): +def test_witness_block(rpc, p2p, block, accepted, with_witness=True, reason=None): """Send a block to the node and check that it's accepted - Submit the block over the p2p interface @@ -58,6 +59,10 @@ def test_witness_block(rpc, p2p, block, accepted, with_witness=True): p2p.send_message(msg_block(block)) p2p.sync_with_ping() assert_equal(rpc.getbestblockhash() == block.hash, accepted) + if (reason != None and not accepted): + # Check the rejection reason as well. + with mininode_lock: + assert_equal(p2p.last_message["reject"].reason, reason) class TestP2PConn(P2PInterface): def __init__(self): @@ -68,11 +73,15 @@ class TestP2PConn(P2PInterface): for inv in message.inv: self.getdataset.add(inv.hash) - def announce_tx_and_wait_for_getdata(self, tx, timeout=60): + def announce_tx_and_wait_for_getdata(self, tx, timeout=60, success=True): with mininode_lock: self.last_message.pop("getdata", None) self.send_message(msg_inv(inv=[CInv(1, tx.sha256)])) - self.wait_for_getdata(timeout) + if success: + self.wait_for_getdata(timeout) + else: + time.sleep(timeout) + assert not self.last_message.get("getdata") def announce_block_and_wait_for_getdata(self, block, use_header, timeout=60): with mininode_lock: @@ -168,7 +177,7 @@ class SegWitTest(BitcoinTestFramework): # Create a transaction that spends the coinbase tx = CTransaction() tx.vin.append(CTxIn(COutPoint(txid, 0), b"")) - tx.vout.append(CTxOut(49*100000000, CScript([OP_TRUE]))) + tx.vout.append(CTxOut(49 * 100000000, CScript([OP_TRUE, OP_DROP] * 15 + [OP_TRUE]))) tx.calc_sha256() # Check that serializing it with or without witness is the same @@ -259,7 +268,7 @@ class SegWitTest(BitcoinTestFramework): # Now create a new anyone-can-spend utxo for the next test. tx4 = CTransaction() tx4.vin.append(CTxIn(COutPoint(tx3.sha256, 0), CScript([p2sh_program]))) - tx4.vout.append(CTxOut(tx3.vout[0].nValue-1000, CScript([OP_TRUE]))) + tx4.vout.append(CTxOut(tx3.vout[0].nValue - 1000, CScript([OP_TRUE, OP_DROP] * 15 + [OP_TRUE]))) tx4.rehash() test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx3, False, True) test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx4, False, True) @@ -271,6 +280,80 @@ class SegWitTest(BitcoinTestFramework): self.utxo.pop(0) self.utxo.append(UTXO(tx4.sha256, 0, tx4.vout[0].nValue)) + # ~6 months after segwit activation, the SCRIPT_VERIFY_WITNESS flag was + # backdated so that it applies to all blocks, going back to the genesis + # block. + # + # Consequently, version 0 witness outputs are never spendable without + # witness, and so can't be spent before segwit activation (the point at which + # blocks are permitted to contain witnesses). + def test_v0_outputs_arent_spendable(self): + self.log.info("Testing that v0 witness program outputs aren't spendable before activation") + + assert len(self.utxo), "self.utxo is empty" + + # Create two outputs, a p2wsh and p2sh-p2wsh + witness_program = CScript([OP_TRUE]) + witness_hash = sha256(witness_program) + scriptPubKey = CScript([OP_0, witness_hash]) + + p2sh_pubkey = hash160(scriptPubKey) + p2sh_scriptPubKey = CScript([OP_HASH160, p2sh_pubkey, OP_EQUAL]) + + value = self.utxo[0].nValue // 3 + + tx = CTransaction() + tx.vin = [CTxIn(COutPoint(self.utxo[0].sha256, self.utxo[0].n), b'')] + tx.vout = [CTxOut(value, scriptPubKey), CTxOut(value, p2sh_scriptPubKey)] + tx.vout.append(CTxOut(value, CScript([OP_TRUE]))) + tx.rehash() + txid = tx.sha256 + + # Add it to a block + block = self.build_next_block() + self.update_witness_block_with_transactions(block, [tx]) + # Verify that segwit isn't activated. A block serialized with witness + # should be rejected prior to activation. + test_witness_block(self.nodes[0], self.test_node, block, accepted=False, with_witness=True, reason = b'unexpected-witness') + # Now send the block without witness. It should be accepted + test_witness_block(self.nodes[0], self.test_node, block, accepted=True, with_witness=False) + + # Now try to spend the outputs. This should fail since SCRIPT_VERIFY_WITNESS is always enabled. + p2wsh_tx = CTransaction() + p2wsh_tx.vin = [CTxIn(COutPoint(txid, 0), b'')] + p2wsh_tx.vout = [CTxOut(value, CScript([OP_TRUE]))] + p2wsh_tx.wit.vtxinwit.append(CTxInWitness()) + p2wsh_tx.wit.vtxinwit[0].scriptWitness.stack = [CScript([OP_TRUE])] + p2wsh_tx.rehash() + + p2sh_p2wsh_tx = CTransaction() + p2sh_p2wsh_tx.vin = [CTxIn(COutPoint(txid, 1), CScript([scriptPubKey]))] + p2sh_p2wsh_tx.vout = [CTxOut(value, CScript([OP_TRUE]))] + p2sh_p2wsh_tx.wit.vtxinwit.append(CTxInWitness()) + p2sh_p2wsh_tx.wit.vtxinwit[0].scriptWitness.stack = [CScript([OP_TRUE])] + p2sh_p2wsh_tx.rehash() + + for tx in [p2wsh_tx, p2sh_p2wsh_tx]: + + block = self.build_next_block() + self.update_witness_block_with_transactions(block, [tx]) + + # When the block is serialized with a witness, the block will be rejected because witness + # data isn't allowed in blocks that don't commit to witness data. + test_witness_block(self.nodes[0], self.test_node, block, accepted=False, with_witness=True, reason=b'unexpected-witness') + + # When the block is serialized without witness, validation fails because the transaction is + # invalid (transactions are always validated with SCRIPT_VERIFY_WITNESS so a segwit v0 transaction + # without a witness is invalid). + # Note: The reject reason for this failure could be + # 'block-validation-failed' (if script check threads > 1) or + # 'non-mandatory-script-verify-flag (Witness program was passed an + # empty witness)' (otherwise). + # TODO: support multiple acceptable reject reasons. + test_witness_block(self.nodes[0], self.test_node, block, accepted=False, with_witness=False) + + self.utxo.pop(0) + self.utxo.append(UTXO(txid, 2, value)) # Mine enough blocks for segwit's vb state to be 'started'. def advance_to_segwit_started(self): @@ -450,7 +533,7 @@ class SegWitTest(BitcoinTestFramework): block = self.build_next_block() assert(len(self.utxo) > 0) - + # Create a P2WSH transaction. # The witness program will be a bunch of OP_2DROP's, followed by OP_TRUE. # This should give us plenty of room to tweak the spending tx's @@ -562,7 +645,7 @@ class SegWitTest(BitcoinTestFramework): self.log.info("Testing extra witness data in tx") assert(len(self.utxo) > 0) - + block = self.build_next_block() witness_program = CScript([OP_DROP, OP_TRUE]) @@ -730,7 +813,7 @@ class SegWitTest(BitcoinTestFramework): witness_program = CScript([OP_DROP, OP_TRUE]) witness_hash = sha256(witness_program) scriptPubKey = CScript([OP_0, witness_hash]) - + # Create a transaction that splits our utxo into many outputs tx = CTransaction() tx.vin.append(CTxIn(COutPoint(self.utxo[0].sha256, self.utxo[0].n), b"")) @@ -814,7 +897,7 @@ class SegWitTest(BitcoinTestFramework): assert(len(self.utxo)) tx = CTransaction() tx.vin.append(CTxIn(COutPoint(self.utxo[0].sha256, self.utxo[0].n), b"")) - tx.vout.append(CTxOut(self.utxo[0].nValue-1000, CScript([OP_TRUE]))) + tx.vout.append(CTxOut(self.utxo[0].nValue - 1000, CScript([OP_TRUE, OP_DROP] * 15 + [OP_TRUE]))) tx.wit.vtxinwit.append(CTxInWitness()) tx.wit.vtxinwit[0].scriptWitness.stack = [ b'a' ] tx.rehash() @@ -829,12 +912,7 @@ class SegWitTest(BitcoinTestFramework): # Since we haven't delivered the tx yet, inv'ing the same tx from # a witness transaction ought not result in a getdata. - try: - self.test_node.announce_tx_and_wait_for_getdata(tx, timeout=2) - self.log.error("Error: duplicate tx getdata!") - assert(False) - except AssertionError: - pass + self.test_node.announce_tx_and_wait_for_getdata(tx, timeout=2, success=False) # Delivering this transaction with witness should fail (no matter who # its from) @@ -866,7 +944,7 @@ class SegWitTest(BitcoinTestFramework): assert(len(self.utxo)) tx = CTransaction() tx.vin.append(CTxIn(COutPoint(self.utxo[0].sha256, self.utxo[0].n), b"")) - tx.vout.append(CTxOut(self.utxo[0].nValue-1000, CScript([OP_TRUE]))) + tx.vout.append(CTxOut(self.utxo[0].nValue - 1000, CScript([OP_TRUE, OP_DROP] * 15 + [OP_TRUE]))) tx.wit.vtxinwit.append(CTxInWitness()) tx.wit.vtxinwit[0].scriptWitness.stack = [ b'a' ] tx.rehash() @@ -910,7 +988,7 @@ class SegWitTest(BitcoinTestFramework): test_transaction_acceptance(self.nodes[1].rpc, self.std_node, tx3, True, False, b'tx-size') # Remove witness stuffing, instead add extra witness push on stack - tx3.vout[0] = CTxOut(tx2.vout[0].nValue-1000, CScript([OP_TRUE])) + tx3.vout[0] = CTxOut(tx2.vout[0].nValue - 1000, CScript([OP_TRUE, OP_DROP] * 15 + [OP_TRUE])) tx3.wit.vtxinwit[0].scriptWitness.stack = [CScript([CScriptNum(1)]), witness_program ] tx3.rehash() @@ -930,8 +1008,10 @@ class SegWitTest(BitcoinTestFramework): raw_tx = self.nodes[0].getrawtransaction(tx3.hash, 1) assert_equal(int(raw_tx["hash"], 16), tx3.calc_sha256(True)) assert_equal(raw_tx["size"], len(tx3.serialize_with_witness())) - vsize = (len(tx3.serialize_with_witness()) + 3*len(tx3.serialize_without_witness()) + 3) / 4 + weight = len(tx3.serialize_with_witness()) + 3*len(tx3.serialize_without_witness()) + vsize = math.ceil(weight / 4) assert_equal(raw_tx["vsize"], vsize) + assert_equal(raw_tx["weight"], weight) assert_equal(len(raw_tx["vin"][0]["txinwitness"]), 1) assert_equal(raw_tx["vin"][0]["txinwitness"][0], hexlify(witness_program).decode('ascii')) assert(vsize != raw_tx["size"]) @@ -1096,7 +1176,7 @@ class SegWitTest(BitcoinTestFramework): # P2PKH output; just send tx's first output back to an anyone-can-spend. sync_mempools([self.nodes[0], self.nodes[1]]) tx3.vin = [CTxIn(COutPoint(tx.sha256, 0), b"")] - tx3.vout = [CTxOut(tx.vout[0].nValue-1000, CScript([OP_TRUE]))] + tx3.vout = [CTxOut(tx.vout[0].nValue - 1000, CScript([OP_TRUE, OP_DROP] * 15 + [OP_TRUE]))] tx3.wit.vtxinwit.append(CTxInWitness()) tx3.wit.vtxinwit[0].scriptWitness.stack = [witness_program] tx3.rehash() @@ -1104,7 +1184,7 @@ class SegWitTest(BitcoinTestFramework): else: # tx and tx2 didn't go anywhere; just clean up the p2sh_tx output. tx3.vin = [CTxIn(COutPoint(p2sh_tx.sha256, 0), CScript([witness_program]))] - tx3.vout = [CTxOut(p2sh_tx.vout[0].nValue-1000, witness_program)] + tx3.vout = [CTxOut(p2sh_tx.vout[0].nValue - 1000, CScript([OP_TRUE, OP_DROP] * 15 + [OP_TRUE]))] tx3.rehash() test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx3, with_witness=True, accepted=True) @@ -1476,9 +1556,10 @@ class SegWitTest(BitcoinTestFramework): block = self.build_next_block() self.update_witness_block_with_transactions(block, [spend_tx]) - # If we're before activation, then sending this without witnesses - # should be valid. If we're after activation, then sending this with - # witnesses should be valid. + # If we're after activation, then sending this with witnesses should be valid. + # This no longer works before activation, because SCRIPT_VERIFY_WITNESS + # is always set. + # TODO: rewrite this test to make clear that it only works after activation. if segwit_activated: test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True) else: @@ -1577,7 +1658,7 @@ class SegWitTest(BitcoinTestFramework): tx2.wit.vtxinwit.append(CTxInWitness()) tx2.wit.vtxinwit[-1].scriptWitness.stack = [ witness_program ] total_value += tx.vout[i].nValue - tx2.wit.vtxinwit[-1].scriptWitness.stack = [ witness_program_toomany ] + tx2.wit.vtxinwit[-1].scriptWitness.stack = [ witness_program_toomany ] tx2.vout.append(CTxOut(total_value, CScript([OP_TRUE]))) tx2.rehash() @@ -1897,6 +1978,7 @@ class SegWitTest(BitcoinTestFramework): self.test_witness_services() # Verifies NODE_WITNESS self.test_non_witness_transaction() # non-witness tx's are accepted self.test_unnecessary_witness_before_segwit_activation() + self.test_v0_outputs_arent_spendable() self.test_block_relay(segwit_activated=False) # Advance to segwit being 'started' @@ -1914,7 +1996,6 @@ class SegWitTest(BitcoinTestFramework): self.test_unnecessary_witness_before_segwit_activation() self.test_witness_tx_relay_before_segwit_activation() self.test_block_relay(segwit_activated=False) - self.test_p2sh_witness(segwit_activated=False) self.test_standardness_v0(segwit_activated=False) sync_blocks(self.nodes) diff --git a/test/functional/p2p_sendheaders.py b/test/functional/p2p_sendheaders.py index 4c7d5e65c5..095cc4b734 100755 --- a/test/functional/p2p_sendheaders.py +++ b/test/functional/p2p_sendheaders.py @@ -116,6 +116,7 @@ class BaseNode(P2PInterface): self.block_announced = False self.last_blockhash_announced = None + self.recent_headers_announced = [] def send_get_data(self, block_hashes): """Request data for a list of block hashes.""" @@ -163,40 +164,45 @@ class BaseNode(P2PInterface): def on_headers(self, message): if len(message.headers): self.block_announced = True - message.headers[-1].calc_sha256() + for x in message.headers: + x.calc_sha256() + # append because headers may be announced over multiple messages. + self.recent_headers_announced.append(x.sha256) self.last_blockhash_announced = message.headers[-1].sha256 - def clear_last_announcement(self): + def clear_block_announcements(self): with mininode_lock: self.block_announced = False self.last_message.pop("inv", None) self.last_message.pop("headers", None) + self.recent_headers_announced = [] - def check_last_announcement(self, headers=None, inv=None): - """Test whether the last announcement received had the right header or the right inv. - inv and headers should be lists of block hashes.""" + def check_last_headers_announcement(self, headers): + """Test whether the last headers announcements received are right. + Headers may be announced across more than one message.""" + test_function = lambda: (len(self.recent_headers_announced) >= len(headers)) + wait_until(test_function, timeout=60, lock=mininode_lock) + with mininode_lock: + assert_equal(self.recent_headers_announced, headers) + self.block_announced = False + self.last_message.pop("headers", None) + self.recent_headers_announced = [] + + def check_last_inv_announcement(self, inv): + """Test whether the last announcement received had the right inv. + inv should be a list of block hashes.""" test_function = lambda: self.block_announced wait_until(test_function, timeout=60, lock=mininode_lock) with mininode_lock: - self.block_announced = False - compare_inv = [] if "inv" in self.last_message: compare_inv = [x.hash for x in self.last_message["inv"].inv] - if inv is not None: - assert_equal(compare_inv, inv) - - compare_headers = [] - if "headers" in self.last_message: - compare_headers = [x.sha256 for x in self.last_message["headers"].headers] - if headers is not None: - assert_equal(compare_headers, headers) - + assert_equal(compare_inv, inv) + self.block_announced = False self.last_message.pop("inv", None) - self.last_message.pop("headers", None) class SendHeadersTest(BitcoinTestFramework): def set_test_params(self): @@ -206,8 +212,8 @@ class SendHeadersTest(BitcoinTestFramework): def mine_blocks(self, count): """Mine count blocks and return the new tip.""" - # Clear out last block announcement from each p2p listener - [x.clear_last_announcement() for x in self.nodes[0].p2ps] + # Clear out block announcements from each p2p listener + [x.clear_block_announcements() for x in self.nodes[0].p2ps] self.nodes[0].generate(count) return int(self.nodes[0].getbestblockhash(), 16) @@ -222,7 +228,7 @@ class SendHeadersTest(BitcoinTestFramework): sync_blocks(self.nodes, wait=0.1) for x in self.nodes[0].p2ps: x.wait_for_block_announcement(int(self.nodes[0].getbestblockhash(), 16)) - x.clear_last_announcement() + x.clear_block_announcements() tip_height = self.nodes[1].getblockcount() hash_to_invalidate = self.nodes[1].getblockhash(tip_height - (length - 1)) @@ -255,25 +261,25 @@ class SendHeadersTest(BitcoinTestFramework): tip = self.nodes[0].getblockheader(self.nodes[0].generate(1)[0]) tip_hash = int(tip["hash"], 16) - inv_node.check_last_announcement(inv=[tip_hash], headers=[]) - test_node.check_last_announcement(inv=[tip_hash], headers=[]) + inv_node.check_last_inv_announcement(inv=[tip_hash]) + test_node.check_last_inv_announcement(inv=[tip_hash]) self.log.info("Verify getheaders with null locator and valid hashstop returns headers.") - test_node.clear_last_announcement() + test_node.clear_block_announcements() test_node.send_get_headers(locator=[], hashstop=tip_hash) - test_node.check_last_announcement(headers=[tip_hash]) + test_node.check_last_headers_announcement(headers=[tip_hash]) self.log.info("Verify getheaders with null locator and invalid hashstop does not return headers.") block = create_block(int(tip["hash"], 16), create_coinbase(tip["height"] + 1), tip["mediantime"] + 1) block.solve() test_node.send_header_for_blocks([block]) - test_node.clear_last_announcement() + test_node.clear_block_announcements() test_node.send_get_headers(locator=[], hashstop=int(block.hash, 16)) test_node.sync_with_ping() assert_equal(test_node.block_announced, False) - inv_node.clear_last_announcement() + inv_node.clear_block_announcements() test_node.send_message(msg_block(block)) - inv_node.check_last_announcement(inv=[int(block.hash, 16)], headers=[]) + inv_node.check_last_inv_announcement(inv=[int(block.hash, 16)]) def test_nonnull_locators(self, test_node, inv_node): tip = int(self.nodes[0].getbestblockhash(), 16) @@ -284,8 +290,8 @@ class SendHeadersTest(BitcoinTestFramework): for i in range(4): old_tip = tip tip = self.mine_blocks(1) - inv_node.check_last_announcement(inv=[tip], headers=[]) - test_node.check_last_announcement(inv=[tip], headers=[]) + inv_node.check_last_inv_announcement(inv=[tip]) + test_node.check_last_inv_announcement(inv=[tip]) # Try a few different responses; none should affect next announcement if i == 0: # first request the block @@ -296,7 +302,7 @@ class SendHeadersTest(BitcoinTestFramework): test_node.send_get_headers(locator=[old_tip], hashstop=tip) test_node.send_get_data([tip]) test_node.wait_for_block(tip) - test_node.clear_last_announcement() # since we requested headers... + test_node.clear_block_announcements() # since we requested headers... elif i == 2: # this time announce own block via headers height = self.nodes[0].getblockcount() @@ -308,8 +314,8 @@ class SendHeadersTest(BitcoinTestFramework): test_node.wait_for_getdata([new_block.sha256]) test_node.send_message(msg_block(new_block)) test_node.sync_with_ping() # make sure this block is processed - inv_node.clear_last_announcement() - test_node.clear_last_announcement() + inv_node.clear_block_announcements() + test_node.clear_block_announcements() self.log.info("Part 1: success!") self.log.info("Part 2: announce blocks with headers after sendheaders message...") @@ -323,8 +329,8 @@ class SendHeadersTest(BitcoinTestFramework): # Now that we've synced headers, headers announcements should work tip = self.mine_blocks(1) - inv_node.check_last_announcement(inv=[tip], headers=[]) - test_node.check_last_announcement(headers=[tip]) + inv_node.check_last_inv_announcement(inv=[tip]) + test_node.check_last_headers_announcement(headers=[tip]) height = self.nodes[0].getblockcount() + 1 block_time += 10 # Advance far enough ahead @@ -368,8 +374,8 @@ class SendHeadersTest(BitcoinTestFramework): assert "inv" not in inv_node.last_message assert "headers" not in inv_node.last_message tip = self.mine_blocks(1) - inv_node.check_last_announcement(inv=[tip], headers=[]) - test_node.check_last_announcement(headers=[tip]) + inv_node.check_last_inv_announcement(inv=[tip]) + test_node.check_last_headers_announcement(headers=[tip]) height += 1 block_time += 1 @@ -383,16 +389,16 @@ class SendHeadersTest(BitcoinTestFramework): # First try mining a reorg that can propagate with header announcement new_block_hashes = self.mine_reorg(length=7) tip = new_block_hashes[-1] - inv_node.check_last_announcement(inv=[tip], headers=[]) - test_node.check_last_announcement(headers=new_block_hashes) + inv_node.check_last_inv_announcement(inv=[tip]) + test_node.check_last_headers_announcement(headers=new_block_hashes) block_time += 8 # Mine a too-large reorg, which should be announced with a single inv new_block_hashes = self.mine_reorg(length=8) tip = new_block_hashes[-1] - inv_node.check_last_announcement(inv=[tip], headers=[]) - test_node.check_last_announcement(inv=[tip], headers=[]) + inv_node.check_last_inv_announcement(inv=[tip]) + test_node.check_last_inv_announcement(inv=[tip]) block_time += 9 @@ -401,15 +407,15 @@ class SendHeadersTest(BitcoinTestFramework): # Use getblocks/getdata test_node.send_getblocks(locator=[fork_point]) - test_node.check_last_announcement(inv=new_block_hashes, headers=[]) + test_node.check_last_inv_announcement(inv=new_block_hashes) test_node.send_get_data(new_block_hashes) test_node.wait_for_block(new_block_hashes[-1]) for i in range(3): # Mine another block, still should get only an inv tip = self.mine_blocks(1) - inv_node.check_last_announcement(inv=[tip], headers=[]) - test_node.check_last_announcement(inv=[tip], headers=[]) + inv_node.check_last_inv_announcement(inv=[tip]) + test_node.check_last_inv_announcement(inv=[tip]) if i == 0: self.log.debug("Just get the data -- shouldn't cause headers announcements to resume") test_node.send_get_data([tip]) @@ -431,8 +437,8 @@ class SendHeadersTest(BitcoinTestFramework): test_node.sync_with_ping() # New blocks should now be announced with header tip = self.mine_blocks(1) - inv_node.check_last_announcement(inv=[tip], headers=[]) - test_node.check_last_announcement(headers=[tip]) + inv_node.check_last_inv_announcement(inv=[tip]) + test_node.check_last_headers_announcement(headers=[tip]) self.log.info("Part 3: success!") diff --git a/test/functional/p2p_unrequested_blocks.py b/test/functional/p2p_unrequested_blocks.py index 53b2856eb5..49c619a4ce 100755 --- a/test/functional/p2p_unrequested_blocks.py +++ b/test/functional/p2p_unrequested_blocks.py @@ -57,12 +57,8 @@ from test_framework.util import * import time from test_framework.blocktools import create_block, create_coinbase, create_transaction -class AcceptBlockTest(BitcoinTestFramework): - def add_options(self, parser): - parser.add_option("--testbinary", dest="testbinary", - default=os.getenv("BITCOIND", "bitcoind"), - help="bitcoind binary to test") +class AcceptBlockTest(BitcoinTestFramework): def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 2 diff --git a/test/functional/rpc_bind.py b/test/functional/rpc_bind.py index 5b50520d3f..343e162058 100755 --- a/test/functional/rpc_bind.py +++ b/test/functional/rpc_bind.py @@ -4,7 +4,6 @@ # file COPYING or http://www.opensource.org/licenses/mit-license.php. """Test running bitcoind with the -rpcbind and -rpcallowip options.""" -import socket import sys from test_framework.test_framework import BitcoinTestFramework, SkipTest @@ -20,6 +19,11 @@ class RPCBindTest(BitcoinTestFramework): def setup_network(self): self.add_nodes(self.num_nodes, None) + def add_options(self, parser): + parser.add_option("--ipv4", action='store_true', dest="run_ipv4", help="Run ipv4 tests only", default=False) + parser.add_option("--ipv6", action='store_true', dest="run_ipv6", help="Run ipv6 tests only", default=False) + parser.add_option("--nonloopback", action='store_true', dest="run_nonloopback", help="Run non-loopback tests only", default=False) + def run_bind_test(self, allow_ips, connect_to, addresses, expected): ''' Start a node with requested rpcallowip and rpcbind parameters, @@ -54,55 +58,69 @@ class RPCBindTest(BitcoinTestFramework): def run_test(self): # due to OS-specific network stats queries, this test works only on Linux + if sum([self.options.run_ipv4, self.options.run_ipv6, self.options.run_nonloopback]) > 1: + raise AssertionError("Only one of --ipv4, --ipv6 and --nonloopback can be set") + + self.log.info("Check for linux") if not sys.platform.startswith('linux'): - raise SkipTest("This test can only be run on Linux.") - # find the first non-loopback interface for testing - non_loopback_ip = None + raise SkipTest("This test can only be run on linux.") + + self.log.info("Check for ipv6") + have_ipv6 = test_ipv6_local() + if not have_ipv6 and not self.options.run_ipv4: + raise SkipTest("This test requires ipv6 support.") + + self.log.info("Check for non-loopback interface") + self.non_loopback_ip = None for name,ip in all_interfaces(): if ip != '127.0.0.1': - non_loopback_ip = ip + self.non_loopback_ip = ip break - if non_loopback_ip is None: - raise SkipTest("This test requires at least one non-loopback IPv4 interface.") - try: - s = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM) - s.connect(("::1",1)) - s.close - except OSError: - raise SkipTest("This test requires IPv6 support.") - - self.log.info("Using interface %s for testing" % non_loopback_ip) - - defaultport = rpc_port(0) - - # check default without rpcallowip (IPv4 and IPv6 localhost) - self.run_bind_test(None, '127.0.0.1', [], - [('127.0.0.1', defaultport), ('::1', defaultport)]) - # check default with rpcallowip (IPv6 any) - self.run_bind_test(['127.0.0.1'], '127.0.0.1', [], - [('::0', defaultport)]) - # check only IPv4 localhost (explicit) - self.run_bind_test(['127.0.0.1'], '127.0.0.1', ['127.0.0.1'], - [('127.0.0.1', defaultport)]) - # check only IPv4 localhost (explicit) with alternative port - self.run_bind_test(['127.0.0.1'], '127.0.0.1:32171', ['127.0.0.1:32171'], - [('127.0.0.1', 32171)]) - # check only IPv4 localhost (explicit) with multiple alternative ports on same host - self.run_bind_test(['127.0.0.1'], '127.0.0.1:32171', ['127.0.0.1:32171', '127.0.0.1:32172'], - [('127.0.0.1', 32171), ('127.0.0.1', 32172)]) - # check only IPv6 localhost (explicit) - self.run_bind_test(['[::1]'], '[::1]', ['[::1]'], - [('::1', defaultport)]) - # check both IPv4 and IPv6 localhost (explicit) - self.run_bind_test(['127.0.0.1'], '127.0.0.1', ['127.0.0.1', '[::1]'], - [('127.0.0.1', defaultport), ('::1', defaultport)]) + if self.non_loopback_ip is None and self.options.run_nonloopback: + raise SkipTest("This test requires a non-loopback ip address.") + + self.defaultport = rpc_port(0) + + if not self.options.run_nonloopback: + self._run_loopback_tests() + if not self.options.run_ipv4 and not self.options.run_ipv6: + self._run_nonloopback_tests() + + def _run_loopback_tests(self): + if self.options.run_ipv4: + # check only IPv4 localhost (explicit) + self.run_bind_test(['127.0.0.1'], '127.0.0.1', ['127.0.0.1'], + [('127.0.0.1', self.defaultport)]) + # check only IPv4 localhost (explicit) with alternative port + self.run_bind_test(['127.0.0.1'], '127.0.0.1:32171', ['127.0.0.1:32171'], + [('127.0.0.1', 32171)]) + # check only IPv4 localhost (explicit) with multiple alternative ports on same host + self.run_bind_test(['127.0.0.1'], '127.0.0.1:32171', ['127.0.0.1:32171', '127.0.0.1:32172'], + [('127.0.0.1', 32171), ('127.0.0.1', 32172)]) + else: + # check default without rpcallowip (IPv4 and IPv6 localhost) + self.run_bind_test(None, '127.0.0.1', [], + [('127.0.0.1', self.defaultport), ('::1', self.defaultport)]) + # check default with rpcallowip (IPv6 any) + self.run_bind_test(['127.0.0.1'], '127.0.0.1', [], + [('::0', self.defaultport)]) + # check only IPv6 localhost (explicit) + self.run_bind_test(['[::1]'], '[::1]', ['[::1]'], + [('::1', self.defaultport)]) + # check both IPv4 and IPv6 localhost (explicit) + self.run_bind_test(['127.0.0.1'], '127.0.0.1', ['127.0.0.1', '[::1]'], + [('127.0.0.1', self.defaultport), ('::1', self.defaultport)]) + + def _run_nonloopback_tests(self): + self.log.info("Using interface %s for testing" % self.non_loopback_ip) + # check only non-loopback interface - self.run_bind_test([non_loopback_ip], non_loopback_ip, [non_loopback_ip], - [(non_loopback_ip, defaultport)]) + self.run_bind_test([self.non_loopback_ip], self.non_loopback_ip, [self.non_loopback_ip], + [(self.non_loopback_ip, self.defaultport)]) # Check that with invalid rpcallowip, we are denied - self.run_allowip_test([non_loopback_ip], non_loopback_ip, defaultport) - assert_raises_rpc_error(-342, "non-JSON HTTP response with '403 Forbidden' from server", self.run_allowip_test, ['1.1.1.1'], non_loopback_ip, defaultport) + self.run_allowip_test([self.non_loopback_ip], self.non_loopback_ip, self.defaultport) + assert_raises_rpc_error(-342, "non-JSON HTTP response with '403 Forbidden' from server", self.run_allowip_test, ['1.1.1.1'], self.non_loopback_ip, self.defaultport) if __name__ == '__main__': RPCBindTest().main() diff --git a/test/functional/rpc_decodescript.py b/test/functional/rpc_decodescript.py index 1ffc570437..d588151768 100755 --- a/test/functional/rpc_decodescript.py +++ b/test/functional/rpc_decodescript.py @@ -50,8 +50,11 @@ class DecodeScriptTest(BitcoinTestFramework): def decodescript_script_pub_key(self): public_key = '03b0da749730dc9b4b1f4a14d6902877a92541f5368778853d9c4a0cb7802dcfb2' push_public_key = '21' + public_key - public_key_hash = '11695b6cd891484c2d49ec5aa738ec2b2f897777' + public_key_hash = '5dd1d3a048119c27b28293056724d9522f26d945' push_public_key_hash = '14' + public_key_hash + uncompressed_public_key = '04b0da749730dc9b4b1f4a14d6902877a92541f5368778853d9c4a0cb7802dcfb25e01fc8fde47c96c98a4f3a8123e33a38a50cf9025cc8c4494a518f991792bb7' + push_uncompressed_public_key = '41' + uncompressed_public_key + p2wsh_p2pk_script_hash = 'd8590cf8ea0674cf3d49fd7ca249b85ef7485dea62c138468bddeb20cd6519f7' # below are test cases for all of the standard transaction types @@ -59,18 +62,26 @@ class DecodeScriptTest(BitcoinTestFramework): # <pubkey> OP_CHECKSIG rpc_result = self.nodes[0].decodescript(push_public_key + 'ac') assert_equal(public_key + ' OP_CHECKSIG', rpc_result['asm']) + # P2PK is translated to P2WPKH + assert_equal('0 ' + public_key_hash, rpc_result['segwit']['asm']) # 2) P2PKH scriptPubKey # OP_DUP OP_HASH160 <PubKeyHash> OP_EQUALVERIFY OP_CHECKSIG rpc_result = self.nodes[0].decodescript('76a9' + push_public_key_hash + '88ac') assert_equal('OP_DUP OP_HASH160 ' + public_key_hash + ' OP_EQUALVERIFY OP_CHECKSIG', rpc_result['asm']) + # P2PKH is translated to P2WPKH + assert_equal('0 ' + public_key_hash, rpc_result['segwit']['asm']) # 3) multisig scriptPubKey # <m> <A pubkey> <B pubkey> <C pubkey> <n> OP_CHECKMULTISIG # just imagine that the pub keys used below are different. # for our purposes here it does not matter that they are the same even though it is unrealistic. - rpc_result = self.nodes[0].decodescript('52' + push_public_key + push_public_key + push_public_key + '53ae') + multisig_script = '52' + push_public_key + push_public_key + push_public_key + '53ae' + rpc_result = self.nodes[0].decodescript(multisig_script) assert_equal('2 ' + public_key + ' ' + public_key + ' ' + public_key + ' 3 OP_CHECKMULTISIG', rpc_result['asm']) + # multisig in P2WSH + multisig_script_hash = bytes_to_hex_str(sha256(hex_str_to_bytes(multisig_script))) + assert_equal('0 ' + multisig_script_hash, rpc_result['segwit']['asm']) # 4) P2SH scriptPubKey # OP_HASH160 <Hash160(redeemScript)> OP_EQUAL. @@ -78,6 +89,8 @@ class DecodeScriptTest(BitcoinTestFramework): # but this works the same for purposes of this test. rpc_result = self.nodes[0].decodescript('a9' + push_public_key_hash + '87') assert_equal('OP_HASH160 ' + public_key_hash + ' OP_EQUAL', rpc_result['asm']) + # P2SH does not work in segwit secripts. decodescript should not return a result for it. + assert 'segwit' not in rpc_result # 5) null data scriptPubKey # use a signature look-alike here to make sure that we do not decode random data as a signature. @@ -101,8 +114,49 @@ class DecodeScriptTest(BitcoinTestFramework): # <sender-pubkey> OP_CHECKSIG # # lock until block 500,000 - rpc_result = self.nodes[0].decodescript('63' + push_public_key + 'ad670320a107b17568' + push_public_key + 'ac') + cltv_script = '63' + push_public_key + 'ad670320a107b17568' + push_public_key + 'ac' + rpc_result = self.nodes[0].decodescript(cltv_script) assert_equal('OP_IF ' + public_key + ' OP_CHECKSIGVERIFY OP_ELSE 500000 OP_CHECKLOCKTIMEVERIFY OP_DROP OP_ENDIF ' + public_key + ' OP_CHECKSIG', rpc_result['asm']) + # CLTV script in P2WSH + cltv_script_hash = bytes_to_hex_str(sha256(hex_str_to_bytes(cltv_script))) + assert_equal('0 ' + cltv_script_hash, rpc_result['segwit']['asm']) + + # 7) P2PK scriptPubKey + # <pubkey> OP_CHECKSIG + rpc_result = self.nodes[0].decodescript(push_uncompressed_public_key + 'ac') + assert_equal(uncompressed_public_key + ' OP_CHECKSIG', rpc_result['asm']) + # uncompressed pubkeys are invalid for checksigs in segwit scripts. + # decodescript should not return a P2WPKH equivalent. + assert 'segwit' not in rpc_result + + # 8) multisig scriptPubKey with an uncompressed pubkey + # <m> <A pubkey> <B pubkey> <n> OP_CHECKMULTISIG + # just imagine that the pub keys used below are different. + # the purpose of this test is to check that a segwit script is not returned for bare multisig scripts + # with an uncompressed pubkey in them. + rpc_result = self.nodes[0].decodescript('52' + push_public_key + push_uncompressed_public_key +'52ae') + assert_equal('2 ' + public_key + ' ' + uncompressed_public_key + ' 2 OP_CHECKMULTISIG', rpc_result['asm']) + # uncompressed pubkeys are invalid for checksigs in segwit scripts. + # decodescript should not return a P2WPKH equivalent. + assert 'segwit' not in rpc_result + + # 9) P2WPKH scriptpubkey + # 0 <PubKeyHash> + rpc_result = self.nodes[0].decodescript('00' + push_public_key_hash) + assert_equal('0 ' + public_key_hash, rpc_result['asm']) + # segwit scripts do not work nested into each other. + # a nested segwit script should not be returned in the results. + assert 'segwit' not in rpc_result + + # 10) P2WSH scriptpubkey + # 0 <ScriptHash> + # even though this hash is of a P2PK script which is better used as bare P2WPKH, it should not matter + # for the purpose of this test. + rpc_result = self.nodes[0].decodescript('0020' + p2wsh_p2pk_script_hash) + assert_equal('0 ' + p2wsh_p2pk_script_hash, rpc_result['asm']) + # segwit scripts do not work nested into each other. + # a nested segwit script should not be returned in the results. + assert 'segwit' not in rpc_result def decoderawtransaction_asm_sighashtype(self): """Test decoding scripts via RPC command "decoderawtransaction". diff --git a/test/functional/rpc_deprecated.py b/test/functional/rpc_deprecated.py index b94b9d8fae..2e0500e7c4 100755 --- a/test/functional/rpc_deprecated.py +++ b/test/functional/rpc_deprecated.py @@ -4,12 +4,13 @@ # 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 class DeprecatedRpcTest(BitcoinTestFramework): def set_test_params(self): self.num_nodes = 2 self.setup_clean_chain = True - self.extra_args = [[], ["-deprecatedrpc=validateaddress"]] + self.extra_args = [[], ["-deprecatedrpc=validateaddress", "-deprecatedrpc=accounts"]] def run_test(self): # This test should be used to verify correct behaviour of deprecated @@ -20,11 +21,93 @@ class DeprecatedRpcTest(BitcoinTestFramework): # self.nodes[1].createmultisig(1, [self.nodes[1].getnewaddress()]) self.log.info("Test validateaddress deprecation") - SOME_ADDRESS = "mnvGjUy3NMj67yJ6gkK5o9e5RS33Z2Vqcu" # This is just some random address to pass as a parameter to validateaddress + SOME_ADDRESS = "mnvGjUy3NMj67yJ6gkK5o9e5RS33Z2Vqcu" # This is just some random address to pass as a parameter to validateaddress dep_validate_address = self.nodes[0].validateaddress(SOME_ADDRESS) assert "ismine" not in dep_validate_address not_dep_val = self.nodes[1].validateaddress(SOME_ADDRESS) assert "ismine" in not_dep_val + self.log.info("Test accounts deprecation") + # The following account RPC methods are deprecated: + # - getaccount + # - getaccountaddress + # - getaddressesbyaccount + # - getreceivedbyaccount + # - listaccouts + # - listreceivedbyaccount + # - move + # - setaccount + # + # The following 'label' RPC methods are usable both with and without the + # -deprecatedrpc=accounts switch enabled. + # - getlabeladdress + # - getaddressesbylabel + # - getreceivedbylabel + # - listlabels + # - listreceivedbylabel + # - setlabel + # + address0 = self.nodes[0].getnewaddress() + self.nodes[0].generatetoaddress(101, address0) + self.sync_all() + address1 = self.nodes[1].getnewaddress() + self.nodes[1].generatetoaddress(101, address1) + + self.log.info("- getaccount") + assert_raises_rpc_error(-32, "getaccount is deprecated", self.nodes[0].getaccount, address0) + self.nodes[1].getaccount(address1) + + self.log.info("- setaccount") + assert_raises_rpc_error(-32, "setaccount is deprecated", self.nodes[0].setaccount, address0, "label0") + self.nodes[1].setaccount(address1, "label1") + + self.log.info("- setlabel") + self.nodes[0].setlabel(address0, "label0") + self.nodes[1].setlabel(address1, "label1") + + self.log.info("- getaccountaddress") + assert_raises_rpc_error(-32, "getaccountaddress is deprecated", self.nodes[0].getaccountaddress, "label0") + self.nodes[1].getaccountaddress("label1") + + self.log.info("- getlabeladdress") + self.nodes[0].getlabeladdress("label0") + self.nodes[1].getlabeladdress("label1") + + self.log.info("- getaddressesbyaccount") + assert_raises_rpc_error(-32, "getaddressesbyaccount is deprecated", self.nodes[0].getaddressesbyaccount, "label0") + self.nodes[1].getaddressesbyaccount("label1") + + self.log.info("- getaddressesbylabel") + self.nodes[0].getaddressesbylabel("label0") + self.nodes[1].getaddressesbylabel("label1") + + self.log.info("- getreceivedbyaccount") + assert_raises_rpc_error(-32, "getreceivedbyaccount is deprecated", self.nodes[0].getreceivedbyaccount, "label0") + self.nodes[1].getreceivedbyaccount("label1") + + self.log.info("- getreceivedbylabel") + self.nodes[0].getreceivedbylabel("label0") + self.nodes[1].getreceivedbylabel("label1") + + self.log.info("- listaccounts") + assert_raises_rpc_error(-32, "listaccounts is deprecated", self.nodes[0].listaccounts) + self.nodes[1].listaccounts() + + self.log.info("- listlabels") + self.nodes[0].listlabels() + self.nodes[1].listlabels() + + self.log.info("- listreceivedbyaccount") + assert_raises_rpc_error(-32, "listreceivedbyaccount is deprecated", self.nodes[0].listreceivedbyaccount) + self.nodes[1].listreceivedbyaccount() + + self.log.info("- listreceivedbylabel") + self.nodes[0].listreceivedbylabel() + self.nodes[1].listreceivedbylabel() + + self.log.info("- move") + assert_raises_rpc_error(-32, "move is deprecated", self.nodes[0].move, "label0", "label0b", 10) + self.nodes[1].move("label1", "label1b", 10) + if __name__ == '__main__': DeprecatedRpcTest().main() diff --git a/test/functional/rpc_fundrawtransaction.py b/test/functional/rpc_fundrawtransaction.py index 5fb9a361d9..1e91cf598c 100755 --- a/test/functional/rpc_fundrawtransaction.py +++ b/test/functional/rpc_fundrawtransaction.py @@ -4,8 +4,18 @@ # file COPYING or http://www.opensource.org/licenses/mit-license.php. """Test the fundrawtransaction RPC.""" +from decimal import Decimal from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import * +from test_framework.util import ( + assert_equal, + assert_fee_amount, + assert_greater_than, + assert_greater_than_or_equal, + assert_raises_rpc_error, + connect_nodes_bi, + count_bytes, + find_vout_for_address, +) def get_unspent(listunspent, amount): @@ -57,6 +67,11 @@ class RawTransactionsTest(BitcoinTestFramework): watchonly_amount = Decimal(200) self.nodes[3].importpubkey(watchonly_pubkey, "", True) watchonly_txid = self.nodes[0].sendtoaddress(watchonly_address, watchonly_amount) + + # Lock UTXO so nodes[0] doesn't accidentally spend it + watchonly_vout = find_vout_for_address(self.nodes[0], watchonly_txid, watchonly_address) + self.nodes[0].lockunspent(False, [{"txid": watchonly_txid, "vout": watchonly_vout}]) + self.nodes[0].sendtoaddress(self.nodes[3].getnewaddress(), watchonly_amount / 10) self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 1.5) @@ -224,7 +239,7 @@ class RawTransactionsTest(BitcoinTestFramework): outputs = { self.nodes[0].getnewaddress() : Decimal(4.0) } rawtx = self.nodes[2].createrawtransaction(inputs, outputs) assert_raises_rpc_error(-1, "JSON value is not a string as expected", self.nodes[2].fundrawtransaction, rawtx, {'change_type': None}) - assert_raises_rpc_error(-5, "Unknown change type", self.nodes[2].fundrawtransaction, rawtx, {'change_type': ''}) + assert_raises_rpc_error(-5, "Unknown change type ''", self.nodes[2].fundrawtransaction, rawtx, {'change_type': ''}) rawtx = self.nodes[2].fundrawtransaction(rawtx, {'change_type': 'bech32'}) dec_tx = self.nodes[2].decoderawtransaction(rawtx['hex']) assert_equal('witness_v0_keyhash', dec_tx['vout'][rawtx['changepos']]['scriptPubKey']['type']) @@ -475,6 +490,9 @@ class RawTransactionsTest(BitcoinTestFramework): connect_nodes_bi(self.nodes,1,2) connect_nodes_bi(self.nodes,0,2) connect_nodes_bi(self.nodes,0,3) + # Again lock the watchonly UTXO or nodes[0] may spend it, because + # lockunspent is memory-only and thus lost on restart + self.nodes[0].lockunspent(False, [{"txid": watchonly_txid, "vout": watchonly_vout}]) self.sync_all() # drain the keypool diff --git a/test/functional/rpc_rawtransaction.py b/test/functional/rpc_rawtransaction.py index 658782e82a..48b4a4a9db 100755 --- a/test/functional/rpc_rawtransaction.py +++ b/test/functional/rpc_rawtransaction.py @@ -14,13 +14,10 @@ Test the following RPCs: from collections import OrderedDict from io import BytesIO +from test_framework.messages import CTransaction, ToHex from test_framework.test_framework import BitcoinTestFramework -from test_framework.messages import ( - CTransaction, -) from test_framework.util import * - class multidict(dict): """Dictionary that allows duplicate keys. @@ -363,5 +360,23 @@ class RawTransactionsTest(BitcoinTestFramework): decrawtx= self.nodes[0].decoderawtransaction(rawtx) assert_equal(decrawtx['vin'][0]['sequence'], 4294967294) + #################################### + # TRANSACTION VERSION NUMBER TESTS # + #################################### + + # Test the minimum transaction version number that fits in a signed 32-bit integer. + tx = CTransaction() + tx.nVersion = -0x80000000 + rawtx = ToHex(tx) + decrawtx = self.nodes[0].decoderawtransaction(rawtx) + assert_equal(decrawtx['version'], -0x80000000) + + # Test the maximum transaction version number that fits in a signed 32-bit integer. + tx = CTransaction() + tx.nVersion = 0x7fffffff + rawtx = ToHex(tx) + decrawtx = self.nodes[0].decoderawtransaction(rawtx) + assert_equal(decrawtx['version'], 0x7fffffff) + if __name__ == '__main__': RawTransactionsTest().main() diff --git a/test/functional/rpc_users.py b/test/functional/rpc_users.py index 0ce412f74a..1ef59da5ad 100755 --- a/test/functional/rpc_users.py +++ b/test/functional/rpc_users.py @@ -14,6 +14,10 @@ from test_framework.util import ( import os import http.client import urllib.parse +import subprocess +from random import SystemRandom +import string +import configparser class HTTPBasicsTest(BitcoinTestFramework): @@ -27,9 +31,20 @@ class HTTPBasicsTest(BitcoinTestFramework): rpcauth2 = "rpcauth=rt2:f8607b1a88861fac29dfccf9b52ff9f$ff36a0c23c8c62b4846112e50fa888416e94c17bfd4c42f88fd8f55ec6a3137e" rpcuser = "rpcuser=rpcuser💻" rpcpassword = "rpcpassword=rpcpassword🔑" + + self.user = ''.join(SystemRandom().choice(string.ascii_letters + string.digits) for _ in range(10)) + config = configparser.ConfigParser() + config.read_file(open(self.options.configfile)) + gen_rpcauth = config['environment']['RPCAUTH'] + p = subprocess.Popen([gen_rpcauth, self.user], stdout=subprocess.PIPE, universal_newlines=True) + lines = p.stdout.read().splitlines() + rpcauth3 = lines[1] + self.password = lines[3] + with open(os.path.join(get_datadir_path(self.options.tmpdir, 0), "bitcoin.conf"), 'a', encoding='utf8') as f: f.write(rpcauth+"\n") f.write(rpcauth2+"\n") + f.write(rpcauth3+"\n") with open(os.path.join(get_datadir_path(self.options.tmpdir, 1), "bitcoin.conf"), 'a', encoding='utf8') as f: f.write(rpcuser+"\n") f.write(rpcpassword+"\n") @@ -51,6 +66,7 @@ class HTTPBasicsTest(BitcoinTestFramework): password2 = "8/F3uMDw4KSEbw96U3CA1C4X05dkHDN2BPFjTgZW4KI=" authpairnew = "rt:"+password + self.log.info('Correct...') headers = {"Authorization": "Basic " + str_to_b64str(authpair)} conn = http.client.HTTPConnection(url.hostname, url.port) @@ -61,6 +77,7 @@ class HTTPBasicsTest(BitcoinTestFramework): conn.close() #Use new authpair to confirm both work + self.log.info('Correct...') headers = {"Authorization": "Basic " + str_to_b64str(authpairnew)} conn = http.client.HTTPConnection(url.hostname, url.port) @@ -71,6 +88,7 @@ class HTTPBasicsTest(BitcoinTestFramework): conn.close() #Wrong login name with rt's password + self.log.info('Wrong...') authpairnew = "rtwrong:"+password headers = {"Authorization": "Basic " + str_to_b64str(authpairnew)} @@ -82,6 +100,7 @@ class HTTPBasicsTest(BitcoinTestFramework): conn.close() #Wrong password for rt + self.log.info('Wrong...') authpairnew = "rt:"+password+"wrong" headers = {"Authorization": "Basic " + str_to_b64str(authpairnew)} @@ -93,6 +112,7 @@ class HTTPBasicsTest(BitcoinTestFramework): conn.close() #Correct for rt2 + self.log.info('Correct...') authpairnew = "rt2:"+password2 headers = {"Authorization": "Basic " + str_to_b64str(authpairnew)} @@ -104,6 +124,7 @@ class HTTPBasicsTest(BitcoinTestFramework): conn.close() #Wrong password for rt2 + self.log.info('Wrong...') authpairnew = "rt2:"+password2+"wrong" headers = {"Authorization": "Basic " + str_to_b64str(authpairnew)} @@ -114,12 +135,37 @@ class HTTPBasicsTest(BitcoinTestFramework): assert_equal(resp.status, 401) conn.close() + #Correct for randomly generated user + self.log.info('Correct...') + authpairnew = self.user+":"+self.password + headers = {"Authorization": "Basic " + str_to_b64str(authpairnew)} + + conn = http.client.HTTPConnection(url.hostname, url.port) + conn.connect() + conn.request('POST', '/', '{"method": "getbestblockhash"}', headers) + resp = conn.getresponse() + assert_equal(resp.status, 200) + conn.close() + + #Wrong password for randomly generated user + self.log.info('Wrong...') + authpairnew = self.user+":"+self.password+"Wrong" + headers = {"Authorization": "Basic " + str_to_b64str(authpairnew)} + + conn = http.client.HTTPConnection(url.hostname, url.port) + conn.connect() + conn.request('POST', '/', '{"method": "getbestblockhash"}', headers) + resp = conn.getresponse() + assert_equal(resp.status, 401) + conn.close() + ############################################################### # Check correctness of the rpcuser/rpcpassword config options # ############################################################### url = urllib.parse.urlparse(self.nodes[1].url) # rpcuser and rpcpassword authpair + self.log.info('Correct...') rpcuserauthpair = "rpcuser💻:rpcpassword🔑" headers = {"Authorization": "Basic " + str_to_b64str(rpcuserauthpair)} @@ -143,6 +189,7 @@ class HTTPBasicsTest(BitcoinTestFramework): conn.close() #Wrong password for rpcuser + self.log.info('Wrong...') rpcuserauthpair = "rpcuser:rpcpasswordwrong" headers = {"Authorization": "Basic " + str_to_b64str(rpcuserauthpair)} diff --git a/test/functional/test_framework/blocktools.py b/test/functional/test_framework/blocktools.py index 43982cd09a..5c2b1815e5 100644 --- a/test/functional/test_framework/blocktools.py +++ b/test/functional/test_framework/blocktools.py @@ -10,7 +10,24 @@ from .address import ( script_to_p2sh_p2wsh, script_to_p2wsh, ) -from .mininode import * +from .messages import ( + CBlock, + COIN, + COutPoint, + CTransaction, + CTxIn, + CTxInWitness, + CTxOut, + FromHex, + ToHex, + bytes_to_hex_str, + hash256, + hex_str_to_bytes, + ser_string, + ser_uint256, + sha256, + uint256_from_str, +) from .script import ( CScript, OP_0, @@ -23,34 +40,34 @@ from .script import ( ) from .util import assert_equal -# Create a block (with regtest difficulty) -def create_block(hashprev, coinbase, nTime=None): +# From BIP141 +WITNESS_COMMITMENT_HEADER = b"\xaa\x21\xa9\xed" + +def create_block(hashprev, coinbase, ntime=None): + """Create a block (with regtest difficulty).""" block = CBlock() - if nTime is None: + if ntime is None: import time - block.nTime = int(time.time()+600) + block.nTime = int(time.time() + 600) else: - block.nTime = nTime + block.nTime = ntime block.hashPrevBlock = hashprev - block.nBits = 0x207fffff # Will break after a difficulty adjustment... + block.nBits = 0x207fffff # difficulty retargeting is disabled in REGTEST chainparams block.vtx.append(coinbase) block.hashMerkleRoot = block.calc_merkle_root() block.calc_sha256() return block -# From BIP141 -WITNESS_COMMITMENT_HEADER = b"\xaa\x21\xa9\xed" - - def get_witness_script(witness_root, witness_nonce): - witness_commitment = uint256_from_str(hash256(ser_uint256(witness_root)+ser_uint256(witness_nonce))) + witness_commitment = uint256_from_str(hash256(ser_uint256(witness_root) + ser_uint256(witness_nonce))) output_data = WITNESS_COMMITMENT_HEADER + ser_uint256(witness_commitment) return CScript([OP_RETURN, output_data]) - -# According to BIP141, blocks with witness rules active must commit to the -# hash of all in-block transactions including witness. def add_witness_commitment(block, nonce=0): + """Add a witness commitment to the block's coinbase transaction. + + According to BIP141, blocks with witness rules active must commit to the + hash of all in-block transactions including witness.""" # First calculate the merkle root of the block's # transactions, with witnesses. witness_nonce = nonce @@ -65,7 +82,6 @@ def add_witness_commitment(block, nonce=0): block.hashMerkleRoot = block.calc_merkle_root() block.rehash() - def serialize_script_num(value): r = bytearray(0) if value == 0: @@ -81,55 +97,59 @@ def serialize_script_num(value): r[-1] |= 0x80 return r -# Create a coinbase transaction, assuming no miner fees. -# If pubkey is passed in, the coinbase output will be a P2PK output; -# otherwise an anyone-can-spend output. -def create_coinbase(height, pubkey = None): +def create_coinbase(height, pubkey=None): + """Create a coinbase transaction, assuming no miner fees. + + If pubkey is passed in, the coinbase output will be a P2PK output; + otherwise an anyone-can-spend output.""" coinbase = CTransaction() - coinbase.vin.append(CTxIn(COutPoint(0, 0xffffffff), - ser_string(serialize_script_num(height)), 0xffffffff)) + coinbase.vin.append(CTxIn(COutPoint(0, 0xffffffff), + ser_string(serialize_script_num(height)), 0xffffffff)) coinbaseoutput = CTxOut() coinbaseoutput.nValue = 50 * COIN - halvings = int(height/150) # regtest + halvings = int(height / 150) # regtest coinbaseoutput.nValue >>= halvings - if (pubkey != None): + if (pubkey is not None): coinbaseoutput.scriptPubKey = CScript([pubkey, OP_CHECKSIG]) else: coinbaseoutput.scriptPubKey = CScript([OP_TRUE]) - coinbase.vout = [ coinbaseoutput ] + coinbase.vout = [coinbaseoutput] coinbase.calc_sha256() return coinbase -# Create a transaction. -# If the scriptPubKey is not specified, make it anyone-can-spend. -def create_transaction(prevtx, n, sig, value, scriptPubKey=CScript()): +def create_transaction(prevtx, n, sig, value, script_pub_key=CScript()): + """Create a transaction. + + If the script_pub_key is not specified, make it anyone-can-spend.""" tx = CTransaction() assert(n < len(prevtx.vout)) tx.vin.append(CTxIn(COutPoint(prevtx.sha256, n), sig, 0xffffffff)) - tx.vout.append(CTxOut(value, scriptPubKey)) + tx.vout.append(CTxOut(value, script_pub_key)) tx.calc_sha256() return tx -def get_legacy_sigopcount_block(block, fAccurate=True): +def get_legacy_sigopcount_block(block, accurate=True): count = 0 for tx in block.vtx: - count += get_legacy_sigopcount_tx(tx, fAccurate) + count += get_legacy_sigopcount_tx(tx, accurate) return count -def get_legacy_sigopcount_tx(tx, fAccurate=True): +def get_legacy_sigopcount_tx(tx, accurate=True): count = 0 for i in tx.vout: - count += i.scriptPubKey.GetSigOpCount(fAccurate) + count += i.scriptPubKey.GetSigOpCount(accurate) for j in tx.vin: # scriptSig might be of type bytes, so convert to CScript for the moment - count += CScript(j.scriptSig).GetSigOpCount(fAccurate) + count += CScript(j.scriptSig).GetSigOpCount(accurate) return count -# Create a scriptPubKey corresponding to either a P2WPKH output for the -# given pubkey, or a P2WSH output of a 1-of-1 multisig for the given -# pubkey. Returns the hex encoding of the scriptPubKey. def witness_script(use_p2wsh, pubkey): - if (use_p2wsh == False): + """Create a scriptPubKey for a pay-to-wtiness TxOut. + + This is either a P2WPKH output for the given pubkey, or a P2WSH output of a + 1-of-1 multisig for the given pubkey. Returns the hex encoding of the + scriptPubKey.""" + if not use_p2wsh: # P2WPKH instead pubkeyhash = hash160(hex_str_to_bytes(pubkey)) pkscript = CScript([OP_0, pubkeyhash]) @@ -140,9 +160,10 @@ def witness_script(use_p2wsh, pubkey): pkscript = CScript([OP_0, scripthash]) return bytes_to_hex_str(pkscript) -# Return a transaction (in hex) that spends the given utxo to a segwit output, -# optionally wrapping the segwit output using P2SH. def create_witness_tx(node, use_p2wsh, utxo, pubkey, encode_p2sh, amount): + """Return a transaction (in hex) that spends the given utxo to a segwit output. + + Optionally wrap the segwit output using P2SH.""" if use_p2wsh: program = CScript([OP_1, hex_str_to_bytes(pubkey), OP_1, OP_CHECKMULTISIG]) addr = script_to_p2sh_p2wsh(program) if encode_p2sh else script_to_p2wsh(program) @@ -152,12 +173,13 @@ def create_witness_tx(node, use_p2wsh, utxo, pubkey, encode_p2sh, amount): assert_equal(node.getaddressinfo(addr)['scriptPubKey'], witness_script(use_p2wsh, pubkey)) return node.createrawtransaction([utxo], {addr: amount}) -# Create a transaction spending a given utxo to a segwit output corresponding -# to the given pubkey: use_p2wsh determines whether to use P2WPKH or P2WSH; -# encode_p2sh determines whether to wrap in P2SH. -# sign=True will have the given node sign the transaction. -# insert_redeem_script will be added to the scriptSig, if given. def send_to_witness(use_p2wsh, node, utxo, pubkey, encode_p2sh, amount, sign=True, insert_redeem_script=""): + """Create a transaction spending a given utxo to a segwit output. + + The output corresponds to the given pubkey: use_p2wsh determines whether to + use P2WPKH or P2WSH; encode_p2sh determines whether to wrap in P2SH. + sign=True will have the given node sign the transaction. + insert_redeem_script will be added to the scriptSig, if given.""" tx_to_witness = create_witness_tx(node, use_p2wsh, utxo, pubkey, encode_p2sh, amount) if (sign): signed = node.signrawtransactionwithwallet(tx_to_witness) diff --git a/test/functional/test_framework/key.py b/test/functional/test_framework/key.py index aa91fb5b0d..1b3e510dc4 100644 --- a/test/functional/test_framework/key.py +++ b/test/functional/test_framework/key.py @@ -10,7 +10,6 @@ This file is modified from python-bitcoinlib. import ctypes import ctypes.util import hashlib -import sys ssl = ctypes.cdll.LoadLibrary(ctypes.util.find_library ('ssl') or 'libeay32') @@ -223,10 +222,5 @@ class CPubKey(bytes): return repr(self) def __repr__(self): - # Always have represent as b'<secret>' so test cases don't have to - # change for py2/3 - if sys.version > '3': - return '%s(%s)' % (self.__class__.__name__, super(CPubKey, self).__repr__()) - else: - return '%s(b%s)' % (self.__class__.__name__, super(CPubKey, self).__repr__()) + return '%s(%s)' % (self.__class__.__name__, super(CPubKey, self).__repr__()) diff --git a/test/functional/test_framework/mininode.py b/test/functional/test_framework/mininode.py index aba2841682..7c2125a177 100755 --- a/test/functional/test_framework/mininode.py +++ b/test/functional/test_framework/mininode.py @@ -554,13 +554,13 @@ class P2PDataStore(P2PInterface): if reject_reason is not None: wait_until(lambda: self.reject_reason_received == reject_reason, lock=mininode_lock) - def send_txs_and_test(self, txs, rpc, success=True, reject_code=None, reject_reason=None): + def send_txs_and_test(self, txs, rpc, success=True, expect_disconnect=False, reject_code=None, reject_reason=None): """Send txs to test node and test whether they're accepted to the mempool. - add all txs to our tx_store - send tx messages for all txs - - if success is True: assert that the tx is accepted to the mempool - - if success is False: assert that the tx is not accepted to the mempool + - if success is True/False: assert that the txs are/are not accepted to the mempool + - if expect_disconnect is True: Skip the sync with ping - if reject_code and reject_reason are set: assert that the correct reject message is received.""" with mininode_lock: @@ -573,7 +573,10 @@ class P2PDataStore(P2PInterface): for tx in txs: self.send_message(msg_tx(tx)) - self.sync_with_ping() + if expect_disconnect: + self.wait_for_disconnect() + else: + self.sync_with_ping() raw_mempool = rpc.getrawmempool() if success: diff --git a/test/functional/test_framework/script.py b/test/functional/test_framework/script.py index 6fe0b445da..44650d7584 100644 --- a/test/functional/test_framework/script.py +++ b/test/functional/test_framework/script.py @@ -10,15 +10,6 @@ This file is modified from python-bitcoinlib. from .mininode import CTransaction, CTxOut, sha256, hash256, uint256_from_str, ser_uint256, ser_string from binascii import hexlify import hashlib - -import sys -bchr = chr -bord = ord -if sys.version > '3': - long = int - bchr = lambda x: bytes([x]) - bord = lambda x: x - import struct from .bignum import bn2vch @@ -40,9 +31,9 @@ class CScriptOp(int): def encode_op_pushdata(d): """Encode a PUSHDATA op, returning bytes""" if len(d) < 0x4c: - return b'' + bchr(len(d)) + d # OP_PUSHDATA + return b'' + bytes([len(d)]) + d # OP_PUSHDATA elif len(d) <= 0xff: - return b'\x4c' + bchr(len(d)) + d # OP_PUSHDATA1 + return b'\x4c' + bytes([len(d)]) + d # OP_PUSHDATA1 elif len(d) <= 0xffff: return b'\x4d' + struct.pack(b'<H', len(d)) + d # OP_PUSHDATA2 elif len(d) <= 0xffffffff: @@ -388,7 +379,7 @@ class CScriptNum(): r.append(0x80 if neg else 0) elif neg: r[-1] |= 0x80 - return bytes(bchr(len(r)) + r) + return bytes([len(r)]) + r class CScript(bytes): @@ -405,17 +396,17 @@ class CScript(bytes): def __coerce_instance(cls, other): # Coerce other into bytes if isinstance(other, CScriptOp): - other = bchr(other) + other = bytes([other]) elif isinstance(other, CScriptNum): if (other.value == 0): - other = bchr(CScriptOp(OP_0)) + other = bytes([CScriptOp(OP_0)]) else: other = CScriptNum.encode(other) elif isinstance(other, int): if 0 <= other <= 16: - other = bytes(bchr(CScriptOp.encode_op_n(other))) + other = bytes([CScriptOp.encode_op_n(other)]) elif other == -1: - other = bytes(bchr(OP_1NEGATE)) + other = bytes([OP_1NEGATE]) else: other = CScriptOp.encode_op_pushdata(bn2vch(other)) elif isinstance(other, (bytes, bytearray)): @@ -458,7 +449,7 @@ class CScript(bytes): i = 0 while i < len(self): sop_idx = i - opcode = bord(self[i]) + opcode = self[i] i += 1 if opcode > OP_PUSHDATA4: @@ -474,21 +465,21 @@ class CScript(bytes): pushdata_type = 'PUSHDATA1' if i >= len(self): raise CScriptInvalidError('PUSHDATA1: missing data length') - datasize = bord(self[i]) + datasize = self[i] i += 1 elif opcode == OP_PUSHDATA2: pushdata_type = 'PUSHDATA2' if i + 1 >= len(self): raise CScriptInvalidError('PUSHDATA2: missing data length') - datasize = bord(self[i]) + (bord(self[i+1]) << 8) + datasize = self[i] + (self[i+1] << 8) i += 2 elif opcode == OP_PUSHDATA4: pushdata_type = 'PUSHDATA4' if i + 3 >= len(self): raise CScriptInvalidError('PUSHDATA4: missing data length') - datasize = bord(self[i]) + (bord(self[i+1]) << 8) + (bord(self[i+2]) << 16) + (bord(self[i+3]) << 24) + datasize = self[i] + (self[i+1] << 8) + (self[i+2] << 16) + (self[i+3] << 24) i += 4 else: diff --git a/test/functional/test_framework/socks5.py b/test/functional/test_framework/socks5.py index 4721809a3b..581de0ed5d 100644 --- a/test/functional/test_framework/socks5.py +++ b/test/functional/test_framework/socks5.py @@ -4,12 +4,14 @@ # file COPYING or http://www.opensource.org/licenses/mit-license.php. """Dummy Socks5 server for testing.""" -import socket, threading, queue +import socket +import threading +import queue import logging logger = logging.getLogger("TestFramework.socks5") -### Protocol constants +# Protocol constants class Command: CONNECT = 0x01 @@ -18,7 +20,7 @@ class AddressType: DOMAINNAME = 0x03 IPV6 = 0x04 -### Utility functions +# Utility functions def recvall(s, n): """Receive n bytes from a socket, or fail.""" rv = bytearray() @@ -30,7 +32,7 @@ def recvall(s, n): n -= len(d) return rv -### Implementation classes +# Implementation classes class Socks5Configuration(): """Proxy configuration.""" def __init__(self): @@ -141,7 +143,7 @@ class Socks5Server(): thread = threading.Thread(None, conn.handle) thread.daemon = True thread.start() - + def start(self): assert(not self.running) self.running = True diff --git a/test/functional/test_framework/test_framework.py b/test/functional/test_framework/test_framework.py index 54ff9eb038..b842e6ef4e 100755 --- a/test/functional/test_framework/test_framework.py +++ b/test/functional/test_framework/test_framework.py @@ -4,6 +4,7 @@ # file COPYING or http://www.opensource.org/licenses/mit-license.php. """Base class for RPC testing.""" +import configparser from enum import Enum import logging import optparse @@ -97,10 +98,8 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass): help="Leave bitcoinds and test.* datadir on exit or error") parser.add_option("--noshutdown", dest="noshutdown", default=False, action="store_true", help="Don't stop bitcoinds after the test execution") - parser.add_option("--srcdir", dest="srcdir", default=os.path.normpath(os.path.dirname(os.path.realpath(__file__)) + "/../../../src"), - help="Source directory containing bitcoind/bitcoin-cli (default: %default)") - parser.add_option("--cachedir", dest="cachedir", default=os.path.normpath(os.path.dirname(os.path.realpath(__file__)) + "/../../cache"), - help="Directory for caching pregenerated datadirs") + parser.add_option("--cachedir", dest="cachedir", default=os.path.abspath(os.path.dirname(os.path.realpath(__file__)) + "/../../cache"), + help="Directory for caching pregenerated datadirs (default: %default)") parser.add_option("--tmpdir", dest="tmpdir", help="Root directory for datadirs") parser.add_option("-l", "--loglevel", dest="loglevel", default="INFO", help="log events at this level and higher to the console. Can be set to DEBUG, INFO, WARNING, ERROR or CRITICAL. Passing --loglevel DEBUG will output all logs to console. Note that logs at all levels are always written to the test_framework.log file in the temporary test directory.") @@ -111,7 +110,8 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass): parser.add_option("--coveragedir", dest="coveragedir", help="Write tested RPC commands into this directory") parser.add_option("--configfile", dest="configfile", - help="Location of the test framework config file") + default=os.path.abspath(os.path.dirname(os.path.realpath(__file__)) + "/../../config.ini"), + help="Location of the test framework config file (default: %default)") parser.add_option("--pdbonfailure", dest="pdbonfailure", default=False, action="store_true", help="Attach a python debugger if test fails") parser.add_option("--usecli", dest="usecli", default=False, action="store_true", @@ -121,14 +121,19 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass): PortSeed.n = self.options.port_seed - os.environ['PATH'] = self.options.srcdir + os.pathsep + \ - self.options.srcdir + os.path.sep + "qt" + os.pathsep + \ - os.environ['PATH'] - check_json_precision() self.options.cachedir = os.path.abspath(self.options.cachedir) + config = configparser.ConfigParser() + config.read_file(open(self.options.configfile)) + self.options.bitcoind = os.getenv("BITCOIND", default=config["environment"]["BUILDDIR"] + '/src/bitcoind' + config["environment"]["EXEEXT"]) + self.options.bitcoincli = os.getenv("BITCOINCLI", default=config["environment"]["BUILDDIR"] + '/src/bitcoin-cli' + config["environment"]["EXEEXT"]) + + os.environ['PATH'] = config['environment']['BUILDDIR'] + os.pathsep + \ + config['environment']['BUILDDIR'] + os.path.sep + "qt" + os.pathsep + \ + os.environ['PATH'] + # Set up temp directory and start logging if self.options.tmpdir: self.options.tmpdir = os.path.abspath(self.options.tmpdir) @@ -246,12 +251,12 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass): if extra_args is None: extra_args = [[]] * num_nodes if binary is None: - binary = [None] * num_nodes + binary = [self.options.bitcoind] * num_nodes assert_equal(len(extra_confs), num_nodes) assert_equal(len(extra_args), num_nodes) assert_equal(len(binary), num_nodes) for i in range(num_nodes): - self.nodes.append(TestNode(i, get_datadir_path(self.options.tmpdir, i), rpchost=rpchost, timewait=timewait, binary=binary[i], stderr=None, mocktime=self.mocktime, coverage_dir=self.options.coveragedir, extra_conf=extra_confs[i], extra_args=extra_args[i], use_cli=self.options.usecli)) + self.nodes.append(TestNode(i, get_datadir_path(self.options.tmpdir, i), rpchost=rpchost, timewait=timewait, bitcoind=binary[i], bitcoin_cli=self.options.bitcoincli, mocktime=self.mocktime, coverage_dir=self.options.coveragedir, extra_conf=extra_confs[i], extra_args=extra_args[i], use_cli=self.options.usecli)) def start_node(self, i, *args, **kwargs): """Start a bitcoind""" @@ -284,9 +289,9 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass): for node in self.nodes: coverage.write_all_rpc_commands(self.options.coveragedir, node.rpc) - def stop_node(self, i): + def stop_node(self, i, expected_stderr=''): """Stop a bitcoind test node""" - self.nodes[i].stop_node() + self.nodes[i].stop_node(expected_stderr) self.nodes[i].wait_until_stopped() def stop_nodes(self): @@ -399,10 +404,10 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass): # Create cache directories, run bitcoinds: for i in range(MAX_NODES): datadir = initialize_datadir(self.options.cachedir, i) - args = [os.getenv("BITCOIND", "bitcoind"), "-datadir=" + datadir] + args = [self.options.bitcoind, "-datadir=" + datadir] if i > 0: args.append("-connect=127.0.0.1:" + str(p2p_port(0))) - self.nodes.append(TestNode(i, get_datadir_path(self.options.cachedir, i), extra_conf=["bind=127.0.0.1"], extra_args=[],rpchost=None, timewait=None, binary=None, stderr=None, mocktime=self.mocktime, coverage_dir=None)) + self.nodes.append(TestNode(i, get_datadir_path(self.options.cachedir, i), extra_conf=["bind=127.0.0.1"], extra_args=[], rpchost=None, timewait=None, bitcoind=self.options.bitcoind, bitcoin_cli=self.options.bitcoincli, mocktime=self.mocktime, coverage_dir=None)) self.nodes[i].args = args self.start_node(i) diff --git a/test/functional/test_framework/test_node.py b/test/functional/test_framework/test_node.py index 0f0d031f35..0353fc0712 100755 --- a/test/functional/test_framework/test_node.py +++ b/test/functional/test_framework/test_node.py @@ -19,7 +19,6 @@ import time from .authproxy import JSONRPCException from .util import ( append_config, - assert_equal, delete_cookie_file, get_rpc_proxy, rpc_url, @@ -57,20 +56,18 @@ class TestNode(): To make things easier for the test writer, any unrecognised messages will be dispatched to the RPC connection.""" - def __init__(self, i, datadir, rpchost, timewait, binary, stderr, mocktime, coverage_dir, extra_conf=None, extra_args=None, use_cli=False): + def __init__(self, i, datadir, rpchost, timewait, bitcoind, bitcoin_cli, mocktime, coverage_dir, extra_conf=None, extra_args=None, use_cli=False): self.index = i self.datadir = datadir + self.stdout_dir = os.path.join(self.datadir, "stdout") + self.stderr_dir = os.path.join(self.datadir, "stderr") self.rpchost = rpchost if timewait: self.rpc_timeout = timewait else: # Wait for up to 60 seconds for the RPC server to respond self.rpc_timeout = 60 - if binary is None: - self.binary = os.getenv("BITCOIND", "bitcoind") - else: - self.binary = binary - self.stderr = stderr + self.binary = bitcoind self.coverage_dir = coverage_dir if extra_conf != None: append_config(datadir, extra_conf) @@ -78,9 +75,18 @@ class TestNode(): # For those callers that need more flexibility, they can just set the args property directly. # Note that common args are set in the config file (see initialize_datadir) self.extra_args = extra_args - self.args = [self.binary, "-datadir=" + self.datadir, "-logtimemicros", "-debug", "-debugexclude=libevent", "-debugexclude=leveldb", "-mocktime=" + str(mocktime), "-uacomment=testnode%d" % i] - - self.cli = TestNodeCLI(os.getenv("BITCOINCLI", "bitcoin-cli"), self.datadir) + self.args = [ + self.binary, + "-datadir=" + self.datadir, + "-logtimemicros", + "-debug", + "-debugexclude=libevent", + "-debugexclude=leveldb", + "-mocktime=" + str(mocktime), + "-uacomment=testnode%d" % i + ] + + self.cli = TestNodeCLI(bitcoin_cli, self.datadir) self.use_cli = use_cli self.running = False @@ -93,6 +99,14 @@ class TestNode(): self.p2ps = [] + def _node_msg(self, msg: str) -> str: + """Return a modified msg that identifies this node by its index as a debugging aid.""" + return "[node %d] %s" % (self.index, msg) + + def _raise_assertion_error(self, msg: str): + """Raise an AssertionError with msg modified to identify this node.""" + raise AssertionError(self._node_msg(msg)) + def __del__(self): # Ensure that we don't leave any bitcoind processes lying around after # the test ends @@ -100,7 +114,7 @@ class TestNode(): # Should only happen on test failure # Avoid using logger, as that may have already been shutdown when # this destructor is called. - print("Cleaning up leftover process") + print(self._node_msg("Cleaning up leftover process")) self.process.kill() def __getattr__(self, name): @@ -108,20 +122,32 @@ class TestNode(): if self.use_cli: return getattr(self.cli, name) else: - assert self.rpc_connected and self.rpc is not None, "Error: no RPC connection" + assert self.rpc_connected and self.rpc is not None, self._node_msg("Error: no RPC connection") return getattr(self.rpc, name) - def start(self, extra_args=None, stderr=None, *args, **kwargs): + def start(self, extra_args=None, stdout=None, stderr=None, *args, **kwargs): """Start the node.""" if extra_args is None: extra_args = self.extra_args + + # Add a new stdout and stderr file each time bitcoind is started if stderr is None: - stderr = self.stderr + stderr = tempfile.NamedTemporaryFile(dir=self.stderr_dir, delete=False) + if stdout is None: + stdout = tempfile.NamedTemporaryFile(dir=self.stdout_dir, delete=False) + self.stderr = stderr + self.stdout = stdout + # Delete any existing cookie file -- if such a file exists (eg due to # unclean shutdown), it will get overwritten anyway by bitcoind, and # potentially interfere with our attempt to authenticate delete_cookie_file(self.datadir) - self.process = subprocess.Popen(self.args + extra_args, stderr=stderr, *args, **kwargs) + + # add environment variable LIBC_FATAL_STDERR_=1 so that libc errors are written to stderr and not the terminal + subp_env = dict(os.environ, LIBC_FATAL_STDERR_="1") + + self.process = subprocess.Popen(self.args + extra_args, env=subp_env, stdout=stdout, stderr=stderr, *args, **kwargs) + self.running = True self.log.debug("bitcoind started, waiting for RPC to come up") @@ -131,7 +157,8 @@ class TestNode(): poll_per_s = 4 for _ in range(poll_per_s * self.rpc_timeout): if self.process.poll() is not None: - raise FailedToStartError('bitcoind exited with status {} during initialization'.format(self.process.returncode)) + raise FailedToStartError(self._node_msg( + 'bitcoind exited with status {} during initialization'.format(self.process.returncode))) try: self.rpc = get_rpc_proxy(rpc_url(self.datadir, self.index, self.rpchost), self.index, timeout=self.rpc_timeout, coveragedir=self.coverage_dir) self.rpc.getblockcount() @@ -150,18 +177,17 @@ class TestNode(): if "No RPC credentials" not in str(e): raise time.sleep(1.0 / poll_per_s) - raise AssertionError("Unable to connect to bitcoind") + self._raise_assertion_error("Unable to connect to bitcoind") def get_wallet_rpc(self, wallet_name): if self.use_cli: return self.cli("-rpcwallet={}".format(wallet_name)) else: - assert self.rpc_connected - assert self.rpc + assert self.rpc_connected and self.rpc, self._node_msg("RPC not connected") wallet_path = "wallet/%s" % wallet_name return self.rpc / wallet_path - def stop_node(self): + def stop_node(self, expected_stderr=''): """Stop the node.""" if not self.running: return @@ -170,6 +196,13 @@ class TestNode(): self.stop() except http.client.CannotSendRequest: self.log.exception("Unable to stop node.") + + # Check that stderr is as expected + self.stderr.seek(0) + stderr = self.stderr.read().decode('utf-8').strip() + if stderr != expected_stderr: + raise AssertionError("Unexpected stderr {} != {}".format(stderr, expected_stderr)) + del self.p2ps[:] def is_node_stopped(self): @@ -184,7 +217,8 @@ class TestNode(): return False # process has stopped. Assert that it didn't return an error code. - assert_equal(return_code, 0) + assert return_code == 0, self._node_msg( + "Node returned non-zero exit code (%d) when stopping" % return_code) self.running = False self.process = None self.rpc_connected = False @@ -203,9 +237,10 @@ class TestNode(): Will throw if bitcoind starts without an error. Will throw if an expected_msg is provided and it does not match bitcoind's stdout.""" - with tempfile.SpooledTemporaryFile(max_size=2**16) as log_stderr: + with tempfile.NamedTemporaryFile(dir=self.stderr_dir, delete=False) as log_stderr, \ + tempfile.NamedTemporaryFile(dir=self.stdout_dir, delete=False) as log_stdout: try: - self.start(extra_args, stderr=log_stderr, *args, **kwargs) + self.start(extra_args, stdout=log_stdout, stderr=log_stderr, *args, **kwargs) self.wait_for_rpc_connection() self.stop_node() self.wait_until_stopped() @@ -219,19 +254,22 @@ class TestNode(): stderr = log_stderr.read().decode('utf-8').strip() if match == ErrorMatch.PARTIAL_REGEX: if re.search(expected_msg, stderr, flags=re.MULTILINE) is None: - raise AssertionError('Expected message "{}" does not partially match stderr:\n"{}"'.format(expected_msg, stderr)) + self._raise_assertion_error( + 'Expected message "{}" does not partially match stderr:\n"{}"'.format(expected_msg, stderr)) elif match == ErrorMatch.FULL_REGEX: if re.fullmatch(expected_msg, stderr) is None: - raise AssertionError('Expected message "{}" does not fully match stderr:\n"{}"'.format(expected_msg, stderr)) + self._raise_assertion_error( + 'Expected message "{}" does not fully match stderr:\n"{}"'.format(expected_msg, stderr)) elif match == ErrorMatch.FULL_TEXT: if expected_msg != stderr: - raise AssertionError('Expected message "{}" does not fully match stderr:\n"{}"'.format(expected_msg, stderr)) + self._raise_assertion_error( + 'Expected message "{}" does not fully match stderr:\n"{}"'.format(expected_msg, stderr)) else: if expected_msg is None: assert_msg = "bitcoind should have exited with an error" else: assert_msg = "bitcoind should have exited with expected error " + expected_msg - raise AssertionError(assert_msg) + self._raise_assertion_error(assert_msg) def node_encrypt_wallet(self, passphrase): """"Encrypts the wallet. @@ -262,7 +300,7 @@ class TestNode(): Convenience property - most tests only use a single p2p connection to each node, so this saves having to write node.p2ps[0] many times.""" - assert self.p2ps, "No p2p connection" + assert self.p2ps, self._node_msg("No p2p connection") return self.p2ps[0] def disconnect_p2ps(self): diff --git a/test/functional/test_framework/util.py b/test/functional/test_framework/util.py index f22322fbbc..e016148f70 100644 --- a/test/functional/test_framework/util.py +++ b/test/functional/test_framework/util.py @@ -294,12 +294,16 @@ def initialize_datadir(dirname, n): os.makedirs(datadir) with open(os.path.join(datadir, "bitcoin.conf"), 'w', encoding='utf8') as f: f.write("regtest=1\n") + f.write("[regtest]\n") f.write("port=" + str(p2p_port(n)) + "\n") f.write("rpcport=" + str(rpc_port(n)) + "\n") f.write("server=1\n") f.write("keypool=1\n") f.write("discover=0\n") f.write("listenonion=0\n") + f.write("printtoconsole=0\n") + os.makedirs(os.path.join(datadir, 'stderr'), exist_ok=True) + os.makedirs(os.path.join(datadir, 'stdout'), exist_ok=True) return datadir def get_datadir_path(dirname, n): @@ -348,7 +352,14 @@ def set_node_times(nodes, t): def disconnect_nodes(from_connection, node_num): for peer_id in [peer['id'] for peer in from_connection.getpeerinfo() if "testnode%d" % node_num in peer['subver']]: - from_connection.disconnectnode(nodeid=peer_id) + try: + from_connection.disconnectnode(nodeid=peer_id) + except JSONRPCException as e: + # If this node is disconnected between calculating the peer id + # and issuing the disconnect, don't worry about it. + # This avoids a race condition if we're mass-disconnecting peers. + if e.error['code'] != -29: # RPC_CLIENT_NODE_NOT_CONNECTED + raise # wait to disconnect wait_until(lambda: [peer['id'] for peer in from_connection.getpeerinfo() if "testnode%d" % node_num in peer['subver']] == [], timeout=5) @@ -555,3 +566,14 @@ def mine_large_block(node, utxos=None): fee = 100 * node.getnetworkinfo()["relayfee"] create_lots_of_big_transactions(node, txouts, utxos, num, fee=fee) node.generate(1) + +def find_vout_for_address(node, txid, addr): + """ + Locate the vout index of the given transaction sending to the + given address. Raises runtime error exception if not found. + """ + tx = node.getrawtransaction(txid, True) + for i in range(len(tx["vout"])): + if any([addr == a for a in tx["vout"][i]["scriptPubKey"]["addresses"]]): + return i + raise RuntimeError("Vout not found for address: txid=%s, addr=%s" % (txid, addr)) diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py index 518c16b5f1..dfa8c33728 100755 --- a/test/functional/test_runner.py +++ b/test/functional/test_runner.py @@ -70,7 +70,7 @@ BASE_SCRIPTS = [ 'wallet_labels.py', 'p2p_segwit.py', 'wallet_dump.py', - 'rpc_listtransactions.py', + 'wallet_listtransactions.py', # vv Tests less than 60s vv 'p2p_sendheaders.py', 'wallet_zapwallettxes.py', @@ -120,6 +120,9 @@ BASE_SCRIPTS = [ 'feature_nulldummy.py', 'mempool_accept.py', 'wallet_import_rescan.py', + 'rpc_bind.py --ipv4', + 'rpc_bind.py --ipv6', + 'rpc_bind.py --nonloopback', 'mining_basic.py', 'wallet_bumpfee.py', 'rpc_named_arguments.py', @@ -135,6 +138,7 @@ BASE_SCRIPTS = [ 'p2p_fingerprint.py', 'feature_uacomment.py', 'p2p_unrequested_blocks.py', + 'feature_includeconf.py', 'feature_logging.py', 'p2p_node_network_limited.py', 'feature_blocksdir.py', @@ -160,7 +164,6 @@ EXTENDED_SCRIPTS = [ 'p2p_timeouts.py', # vv Tests less than 60s vv 'p2p_feefilter.py', - 'rpc_bind.py', # vv Tests less than 30s vv 'feature_assumevalid.py', 'example_test.py', @@ -199,6 +202,7 @@ def main(): parser.add_argument('--keepcache', '-k', action='store_true', help='the default behavior is to flush the cache directory on startup. --keepcache retains the cache from the previous testrun.') parser.add_argument('--quiet', '-q', action='store_true', help='only print results summary and failure logs') parser.add_argument('--tmpdirprefix', '-t', default=tempfile.gettempdir(), help="Root directory for datadirs") + parser.add_argument('--failfast', action='store_true', help='stop execution after the first test failure') args, unknown_args = parser.parse_known_args() # args to be passed on always start with two dashes; tests are the remaining unknown args @@ -281,9 +285,21 @@ def main(): if not args.keepcache: shutil.rmtree("%s/test/cache" % config["environment"]["BUILDDIR"], ignore_errors=True) - run_tests(test_list, config["environment"]["SRCDIR"], config["environment"]["BUILDDIR"], config["environment"]["EXEEXT"], tmpdir, args.jobs, args.coverage, passon_args, args.combinedlogslen) + run_tests( + test_list, + config["environment"]["SRCDIR"], + config["environment"]["BUILDDIR"], + tmpdir, + jobs=args.jobs, + enable_coverage=args.coverage, + args=passon_args, + combined_logs_len=args.combinedlogslen, + failfast=args.failfast, + ) + +def run_tests(test_list, src_dir, build_dir, tmpdir, jobs=1, enable_coverage=False, args=None, combined_logs_len=0, failfast=False): + args = args or [] -def run_tests(test_list, src_dir, build_dir, exeext, tmpdir, jobs=1, enable_coverage=False, args=[], combined_logs_len=0): # Warn if bitcoind is already running (unix only) try: if subprocess.check_output(["pidof", "bitcoind"]) is not None: @@ -296,15 +312,9 @@ def run_tests(test_list, src_dir, build_dir, exeext, tmpdir, jobs=1, enable_cove if os.path.isdir(cache_dir): print("%sWARNING!%s There is a cache directory here: %s. If tests fail unexpectedly, try deleting the cache directory." % (BOLD[1], BOLD[0], cache_dir)) - #Set env vars - if "BITCOIND" not in os.environ: - os.environ["BITCOIND"] = build_dir + '/src/bitcoind' + exeext - os.environ["BITCOINCLI"] = build_dir + '/src/bitcoin-cli' + exeext - tests_dir = src_dir + '/test/functional/' - flags = ["--srcdir={}/src".format(build_dir)] + args - flags.append("--cachedir=%s" % cache_dir) + flags = ['--cachedir={}'.format(cache_dir)] + args if enable_coverage: coverage = RPCCoverage() @@ -349,6 +359,10 @@ def run_tests(test_list, src_dir, build_dir, exeext, tmpdir, jobs=1, enable_cove combined_logs, _ = subprocess.Popen([sys.executable, os.path.join(tests_dir, 'combine_logs.py'), '-c', testdir], universal_newlines=True, stdout=subprocess.PIPE).communicate() print("\n".join(deque(combined_logs.splitlines(), combined_logs_len))) + if failfast: + logging.debug("Early exiting after test failure") + break + print_results(test_results, max_len_name, (int(time.time() - start_time))) if coverage: @@ -363,6 +377,10 @@ def run_tests(test_list, src_dir, build_dir, exeext, tmpdir, jobs=1, enable_cove all_passed = all(map(lambda test_result: test_result.was_successful, test_results)) + # This will be a no-op unless failfast is True in which case there may be dangling + # processes which need to be killed. + job_queue.kill_and_join() + sys.exit(not all_passed) def print_results(test_results, max_len_name, runtime): @@ -453,6 +471,17 @@ class TestHandler: return TestResult(name, status, int(time.time() - start_time)), testdir, stdout, stderr print('.', end='', flush=True) + def kill_and_join(self): + """Send SIGKILL to all jobs and block until all have ended.""" + procs = [i[2] for i in self.jobs] + + for proc in procs: + proc.kill() + + for proc in procs: + proc.wait() + + class TestResult(): def __init__(self, name, status, time): self.name = name diff --git a/test/functional/wallet_address_types.py b/test/functional/wallet_address_types.py index 5d2428e6ef..7658d78383 100755 --- a/test/functional/wallet_address_types.py +++ b/test/functional/wallet_address_types.py @@ -280,7 +280,10 @@ class AddressTypeTest(BitcoinTestFramework): self.log.info('getrawchangeaddress defaults to addresstype if -changetype is not set and argument is absent') self.test_address(3, self.nodes[3].getrawchangeaddress(), multisig=False, typ='bech32') - self.log.info('getrawchangeaddress fails with invalid changetype argument') + self.log.info('test invalid address type arguments') + assert_raises_rpc_error(-5, "Unknown address type ''", self.nodes[3].addmultisigaddress, 2, [compressed_1, compressed_2], None, '') + assert_raises_rpc_error(-5, "Unknown address type ''", self.nodes[3].getnewaddress, None, '') + assert_raises_rpc_error(-5, "Unknown address type ''", self.nodes[3].getrawchangeaddress, '') assert_raises_rpc_error(-5, "Unknown address type 'bech23'", self.nodes[3].getrawchangeaddress, 'bech23') self.log.info("Nodes with changetype=p2sh-segwit never use a P2WPKH change output") diff --git a/test/functional/wallet_basic.py b/test/functional/wallet_basic.py index 0436aca6a4..9c58b84819 100755 --- a/test/functional/wallet_basic.py +++ b/test/functional/wallet_basic.py @@ -3,8 +3,20 @@ # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. """Test the wallet.""" +from decimal import Decimal +import time + from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import * +from test_framework.util import ( + assert_array_result, + assert_equal, + assert_fee_amount, + assert_raises_rpc_error, + connect_nodes_bi, + sync_blocks, + sync_mempools, + wait_until, +) class WalletTest(BitcoinTestFramework): def set_test_params(self): @@ -16,9 +28,9 @@ class WalletTest(BitcoinTestFramework): self.start_node(0) self.start_node(1) self.start_node(2) - connect_nodes_bi(self.nodes,0,1) - connect_nodes_bi(self.nodes,1,2) - connect_nodes_bi(self.nodes,0,2) + connect_nodes_bi(self.nodes, 0, 1) + connect_nodes_bi(self.nodes, 1, 2) + connect_nodes_bi(self.nodes, 0, 2) self.sync_all([self.nodes[0:3]]) def check_fee_amount(self, curr_balance, balance_with_fee, fee_per_byte, tx_size): @@ -111,11 +123,11 @@ class WalletTest(BitcoinTestFramework): self.nodes[2].lockunspent(True, [unspent_0]) assert_equal(len(self.nodes[2].listlockunspent()), 0) assert_raises_rpc_error(-8, "Invalid parameter, unknown transaction", - self.nodes[2].lockunspent, False, - [{"txid": "0000000000000000000000000000000000", "vout": 0}]) + self.nodes[2].lockunspent, False, + [{"txid": "0000000000000000000000000000000000", "vout": 0}]) assert_raises_rpc_error(-8, "Invalid parameter, vout index out of bounds", - self.nodes[2].lockunspent, False, - [{"txid": unspent_0["txid"], "vout": 999}]) + self.nodes[2].lockunspent, False, + [{"txid": unspent_0["txid"], "vout": 999}]) # Have node1 generate 100 blocks (so node0 can recover the fee) self.nodes[1].generate(100) @@ -123,7 +135,7 @@ class WalletTest(BitcoinTestFramework): # node0 should end up with 100 btc in block rewards plus fees, but # minus the 21 plus fees sent to node2 - assert_equal(self.nodes[0].getbalance(), 100-21) + assert_equal(self.nodes[0].getbalance(), 100 - 21) assert_equal(self.nodes[2].getbalance(), 21) # Node0 should have two unspent outputs. @@ -137,8 +149,8 @@ class WalletTest(BitcoinTestFramework): for utxo in node0utxos: inputs = [] outputs = {} - inputs.append({ "txid" : utxo["txid"], "vout" : utxo["vout"]}) - outputs[self.nodes[2].getnewaddress("from1")] = utxo["amount"] - 3 + inputs.append({"txid": utxo["txid"], "vout": utxo["vout"]}) + outputs[self.nodes[2].getnewaddress()] = utxo["amount"] - 3 raw_tx = self.nodes[0].createrawtransaction(inputs, outputs) txns_to_send.append(self.nodes[0].signrawtransactionwithwallet(raw_tx)) @@ -152,7 +164,6 @@ class WalletTest(BitcoinTestFramework): assert_equal(self.nodes[0].getbalance(), 0) assert_equal(self.nodes[2].getbalance(), 94) - assert_equal(self.nodes[2].getbalance("from1"), 94-21) # Verify that a spent output cannot be locked anymore spent_0 = {"txid": node0utxos[0]["txid"], "vout": node0utxos[0]["vout"]} @@ -177,7 +188,7 @@ class WalletTest(BitcoinTestFramework): node_0_bal = self.check_fee_amount(self.nodes[0].getbalance(), Decimal('20'), fee_per_byte, self.get_vsize(self.nodes[2].getrawtransaction(txid))) # Sendmany 10 BTC - txid = self.nodes[2].sendmany('from1', {address: 10}, 0, "", []) + txid = self.nodes[2].sendmany('', {address: 10}, 0, "", []) self.nodes[2].generate(1) self.sync_all([self.nodes[0:3]]) node_0_bal += Decimal('10') @@ -185,7 +196,7 @@ class WalletTest(BitcoinTestFramework): assert_equal(self.nodes[0].getbalance(), node_0_bal) # Sendmany 10 BTC with subtract fee from amount - txid = self.nodes[2].sendmany('from1', {address: 10}, 0, "", [address]) + txid = self.nodes[2].sendmany('', {address: 10}, 0, "", [address]) self.nodes[2].generate(1) self.sync_all([self.nodes[0:3]]) node_2_bal -= Decimal('10') @@ -214,91 +225,90 @@ class WalletTest(BitcoinTestFramework): assert_equal(self.nodes[0].getwalletinfo()["unconfirmed_balance"], 1) assert_equal(self.nodes[0].getunconfirmedbalance(), 1) - #check if we can list zero value tx as available coins - #1. create rawtx - #2. hex-changed one output to 0.0 - #3. sign and send - #4. check if recipient (node0) can list the zero value tx + # check if we can list zero value tx as available coins + # 1. create raw_tx + # 2. hex-changed one output to 0.0 + # 3. sign and send + # 4. check if recipient (node0) can list the zero value tx usp = self.nodes[1].listunspent() - inputs = [{"txid":usp[0]['txid'], "vout":usp[0]['vout']}] + inputs = [{"txid": usp[0]['txid'], "vout": usp[0]['vout']}] outputs = {self.nodes[1].getnewaddress(): 49.998, self.nodes[0].getnewaddress(): 11.11} - rawTx = self.nodes[1].createrawtransaction(inputs, outputs).replace("c0833842", "00000000") #replace 11.11 with 0.0 (int32) - decRawTx = self.nodes[1].decoderawtransaction(rawTx) - signedRawTx = self.nodes[1].signrawtransactionwithwallet(rawTx) - decRawTx = self.nodes[1].decoderawtransaction(signedRawTx['hex']) - zeroValueTxid= decRawTx['txid'] - self.nodes[1].sendrawtransaction(signedRawTx['hex']) + raw_tx = self.nodes[1].createrawtransaction(inputs, outputs).replace("c0833842", "00000000") # replace 11.11 with 0.0 (int32) + signed_raw_tx = self.nodes[1].signrawtransactionwithwallet(raw_tx) + decoded_raw_tx = self.nodes[1].decoderawtransaction(signed_raw_tx['hex']) + zero_value_txid = decoded_raw_tx['txid'] + self.nodes[1].sendrawtransaction(signed_raw_tx['hex']) self.sync_all() - self.nodes[1].generate(1) #mine a block + self.nodes[1].generate(1) # mine a block self.sync_all() - unspentTxs = self.nodes[0].listunspent() #zero value tx must be in listunspents output + unspent_txs = self.nodes[0].listunspent() # zero value tx must be in listunspents output found = False - for uTx in unspentTxs: - if uTx['txid'] == zeroValueTxid: + for uTx in unspent_txs: + if uTx['txid'] == zero_value_txid: found = True assert_equal(uTx['amount'], Decimal('0')) assert(found) - #do some -walletbroadcast tests + # do some -walletbroadcast tests self.stop_nodes() self.start_node(0, ["-walletbroadcast=0"]) self.start_node(1, ["-walletbroadcast=0"]) self.start_node(2, ["-walletbroadcast=0"]) - connect_nodes_bi(self.nodes,0,1) - connect_nodes_bi(self.nodes,1,2) - connect_nodes_bi(self.nodes,0,2) + connect_nodes_bi(self.nodes, 0, 1) + connect_nodes_bi(self.nodes, 1, 2) + connect_nodes_bi(self.nodes, 0, 2) self.sync_all([self.nodes[0:3]]) - txIdNotBroadcasted = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 2) - txObjNotBroadcasted = self.nodes[0].gettransaction(txIdNotBroadcasted) - self.nodes[1].generate(1) #mine a block, tx should not be in there + txid_not_broadcast = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 2) + tx_obj_not_broadcast = self.nodes[0].gettransaction(txid_not_broadcast) + self.nodes[1].generate(1) # mine a block, tx should not be in there self.sync_all([self.nodes[0:3]]) - assert_equal(self.nodes[2].getbalance(), node_2_bal) #should not be changed because tx was not broadcasted + assert_equal(self.nodes[2].getbalance(), node_2_bal) # should not be changed because tx was not broadcasted - #now broadcast from another node, mine a block, sync, and check the balance - self.nodes[1].sendrawtransaction(txObjNotBroadcasted['hex']) + # now broadcast from another node, mine a block, sync, and check the balance + self.nodes[1].sendrawtransaction(tx_obj_not_broadcast['hex']) self.nodes[1].generate(1) self.sync_all([self.nodes[0:3]]) node_2_bal += 2 - txObjNotBroadcasted = self.nodes[0].gettransaction(txIdNotBroadcasted) + tx_obj_not_broadcast = self.nodes[0].gettransaction(txid_not_broadcast) assert_equal(self.nodes[2].getbalance(), node_2_bal) - #create another tx - txIdNotBroadcasted = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 2) + # create another tx + self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 2) - #restart the nodes with -walletbroadcast=1 + # restart the nodes with -walletbroadcast=1 self.stop_nodes() self.start_node(0) self.start_node(1) self.start_node(2) - connect_nodes_bi(self.nodes,0,1) - connect_nodes_bi(self.nodes,1,2) - connect_nodes_bi(self.nodes,0,2) + connect_nodes_bi(self.nodes, 0, 1) + connect_nodes_bi(self.nodes, 1, 2) + connect_nodes_bi(self.nodes, 0, 2) sync_blocks(self.nodes[0:3]) self.nodes[0].generate(1) sync_blocks(self.nodes[0:3]) node_2_bal += 2 - #tx should be added to balance because after restarting the nodes tx should be broadcast + # tx should be added to balance because after restarting the nodes tx should be broadcast assert_equal(self.nodes[2].getbalance(), node_2_bal) - #send a tx with value in a string (PR#6380 +) - txId = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), "2") - txObj = self.nodes[0].gettransaction(txId) - assert_equal(txObj['amount'], Decimal('-2')) + # send a tx with value in a string (PR#6380 +) + txid = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), "2") + tx_obj = self.nodes[0].gettransaction(txid) + assert_equal(tx_obj['amount'], Decimal('-2')) - txId = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), "0.0001") - txObj = self.nodes[0].gettransaction(txId) - assert_equal(txObj['amount'], Decimal('-0.0001')) + txid = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), "0.0001") + tx_obj = self.nodes[0].gettransaction(txid) + assert_equal(tx_obj['amount'], Decimal('-0.0001')) - #check if JSON parser can handle scientific notation in strings - txId = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), "1e-4") - txObj = self.nodes[0].gettransaction(txId) - assert_equal(txObj['amount'], Decimal('-0.0001')) + # check if JSON parser can handle scientific notation in strings + txid = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), "1e-4") + tx_obj = self.nodes[0].gettransaction(txid) + assert_equal(tx_obj['amount'], Decimal('-0.0001')) # This will raise an exception because the amount type is wrong assert_raises_rpc_error(-3, "Invalid amount", self.nodes[0].sendtoaddress, self.nodes[2].getnewaddress(), "1f-4") @@ -321,8 +331,8 @@ class WalletTest(BitcoinTestFramework): # 4. Check that the unspents after import are not spendable assert_array_result(self.nodes[1].listunspent(), - {"address": address_to_import}, - {"spendable": False}) + {"address": address_to_import}, + {"spendable": False}) # 5. Import private key of the previously imported address on node1 priv_key = self.nodes[2].dumpprivkey(address_to_import) @@ -330,17 +340,17 @@ class WalletTest(BitcoinTestFramework): # 6. Check that the unspents are now spendable on node1 assert_array_result(self.nodes[1].listunspent(), - {"address": address_to_import}, - {"spendable": True}) + {"address": address_to_import}, + {"spendable": True}) # Mine a block from node0 to an address from node1 - cbAddr = self.nodes[1].getnewaddress() - blkHash = self.nodes[0].generatetoaddress(1, cbAddr)[0] - cbTxId = self.nodes[0].getblock(blkHash)['tx'][0] + coinbase_addr = self.nodes[1].getnewaddress() + block_hash = self.nodes[0].generatetoaddress(1, coinbase_addr)[0] + coinbase_txid = self.nodes[0].getblock(block_hash)['tx'][0] self.sync_all([self.nodes[0:3]]) # Check that the txid and balance is found by node1 - self.nodes[1].gettransaction(cbTxId) + self.nodes[1].gettransaction(coinbase_txid) # check if wallet or blockchain maintenance changes the balance self.sync_all([self.nodes[0:3]]) @@ -353,14 +363,14 @@ class WalletTest(BitcoinTestFramework): # - True: unicode escaped as \u.... # - False: unicode directly as UTF-8 for mode in [True, False]: - self.nodes[0].ensure_ascii = mode + self.nodes[0].rpc.ensure_ascii = mode # unicode check: Basic Multilingual Plane, Supplementary Plane respectively - for s in [u'рыба', u'𝅘𝅥𝅯']: - addr = self.nodes[0].getaccountaddress(s) - label = self.nodes[0].getaccount(addr) - assert_equal(label, s) - assert(s in self.nodes[0].listaccounts().keys()) - self.nodes[0].ensure_ascii = True # restore to default + for label in [u'рыба', u'𝅘𝅥𝅯']: + addr = self.nodes[0].getnewaddress() + self.nodes[0].setlabel(addr, label) + assert_equal(self.nodes[0].getaddressinfo(addr)['label'], label) + assert(label in self.nodes[0].listlabels()) + self.nodes[0].rpc.ensure_ascii = True # restore to default # maintenance tests maintenance = [ @@ -376,9 +386,9 @@ class WalletTest(BitcoinTestFramework): self.log.info("check " + m) self.stop_nodes() # set lower ancestor limit for later - self.start_node(0, [m, "-limitancestorcount="+str(chainlimit)]) - self.start_node(1, [m, "-limitancestorcount="+str(chainlimit)]) - self.start_node(2, [m, "-limitancestorcount="+str(chainlimit)]) + self.start_node(0, [m, "-limitancestorcount=" + str(chainlimit)]) + self.start_node(1, [m, "-limitancestorcount=" + str(chainlimit)]) + self.start_node(2, [m, "-limitancestorcount=" + str(chainlimit)]) if m == '-reindex': # reindex will leave rpc warm up "early"; Wait for it to finish wait_until(lambda: [block_count] * 3 == [self.nodes[i].getblockcount() for i in range(3)]) @@ -399,7 +409,7 @@ class WalletTest(BitcoinTestFramework): self.nodes[0].generate(1) node0_balance = self.nodes[0].getbalance() # Split into two chains - rawtx = self.nodes[0].createrawtransaction([{"txid":singletxid, "vout":0}], {chain_addrs[0]:node0_balance/2-Decimal('0.01'), chain_addrs[1]:node0_balance/2-Decimal('0.01')}) + rawtx = self.nodes[0].createrawtransaction([{"txid": singletxid, "vout": 0}], {chain_addrs[0]: node0_balance / 2 - Decimal('0.01'), chain_addrs[1]: node0_balance / 2 - Decimal('0.01')}) signedtx = self.nodes[0].signrawtransactionwithwallet(rawtx) singletxid = self.nodes[0].sendrawtransaction(signedtx["hex"]) self.nodes[0].generate(1) @@ -410,10 +420,10 @@ class WalletTest(BitcoinTestFramework): # So we should be able to generate exactly chainlimit txs for each original output sending_addr = self.nodes[1].getnewaddress() txid_list = [] - for i in range(chainlimit*2): + for i in range(chainlimit * 2): txid_list.append(self.nodes[0].sendtoaddress(sending_addr, Decimal('0.0001'))) - assert_equal(self.nodes[0].getmempoolinfo()['size'], chainlimit*2) - assert_equal(len(txid_list), chainlimit*2) + assert_equal(self.nodes[0].getmempoolinfo()['size'], chainlimit * 2) + assert_equal(len(txid_list), chainlimit * 2) # Without walletrejectlongchains, we will still generate a txid # The tx will be stored in the wallet but not accepted to the mempool @@ -421,26 +431,26 @@ class WalletTest(BitcoinTestFramework): assert(extra_txid not in self.nodes[0].getrawmempool()) assert(extra_txid in [tx["txid"] for tx in self.nodes[0].listtransactions()]) self.nodes[0].abandontransaction(extra_txid) - total_txs = len(self.nodes[0].listtransactions("*",99999)) + total_txs = len(self.nodes[0].listtransactions("*", 99999)) # Try with walletrejectlongchains # Double chain limit but require combining inputs, so we pass SelectCoinsMinConf self.stop_node(0) - self.start_node(0, extra_args=["-walletrejectlongchains", "-limitancestorcount="+str(2*chainlimit)]) + self.start_node(0, extra_args=["-walletrejectlongchains", "-limitancestorcount=" + str(2 * chainlimit)]) # wait for loadmempool timeout = 10 - while (timeout > 0 and len(self.nodes[0].getrawmempool()) < chainlimit*2): + while (timeout > 0 and len(self.nodes[0].getrawmempool()) < chainlimit * 2): time.sleep(0.5) timeout -= 0.5 - assert_equal(len(self.nodes[0].getrawmempool()), chainlimit*2) + assert_equal(len(self.nodes[0].getrawmempool()), chainlimit * 2) node0_balance = self.nodes[0].getbalance() # With walletrejectlongchains we will not create the tx and store it in our wallet. assert_raises_rpc_error(-4, "Transaction has too long of a mempool chain", self.nodes[0].sendtoaddress, sending_addr, node0_balance - Decimal('0.01')) # Verify nothing new in wallet - assert_equal(total_txs, len(self.nodes[0].listtransactions("*",99999))) + assert_equal(total_txs, len(self.nodes[0].listtransactions("*", 99999))) # Test getaddressinfo. Note that these addresses are taken from disablewallet.py assert_raises_rpc_error(-5, "Invalid address", self.nodes[0].getaddressinfo, "3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLy") diff --git a/test/functional/wallet_dump.py b/test/functional/wallet_dump.py index 997f67ec7e..01d27dabe7 100755 --- a/test/functional/wallet_dump.py +++ b/test/functional/wallet_dump.py @@ -136,7 +136,7 @@ class WalletDumpTest(BitcoinTestFramework): assert_equal(found_addr, test_addr_count) assert_equal(found_script_addr, 2) assert_equal(found_addr_chg, 90*2 + 50) # old reserve keys are marked as change now - assert_equal(found_addr_rsv, 90*2) + assert_equal(found_addr_rsv, 90*2) assert_equal(witness_addr_ret, witness_addr) # Overwriting should fail diff --git a/test/functional/wallet_hd.py b/test/functional/wallet_hd.py index 8c754807e6..97a945721f 100755 --- a/test/functional/wallet_hd.py +++ b/test/functional/wallet_hd.py @@ -11,6 +11,7 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.util import ( assert_equal, connect_nodes_bi, + assert_raises_rpc_error ) @@ -120,5 +121,39 @@ class WalletHDTest(BitcoinTestFramework): assert_equal(keypath[0:7], "m/0'/1'") + # Generate a new HD seed on node 1 and make sure it is set + orig_masterkeyid = self.nodes[1].getwalletinfo()['hdmasterkeyid'] + self.nodes[1].sethdseed() + new_masterkeyid = self.nodes[1].getwalletinfo()['hdmasterkeyid'] + assert orig_masterkeyid != new_masterkeyid + addr = self.nodes[1].getnewaddress() + assert_equal(self.nodes[1].getaddressinfo(addr)['hdkeypath'], 'm/0\'/0\'/0\'') # Make sure the new address is the first from the keypool + self.nodes[1].keypoolrefill(1) # Fill keypool with 1 key + + # Set a new HD seed on node 1 without flushing the keypool + new_seed = self.nodes[0].dumpprivkey(self.nodes[0].getnewaddress()) + orig_masterkeyid = new_masterkeyid + self.nodes[1].sethdseed(False, new_seed) + new_masterkeyid = self.nodes[1].getwalletinfo()['hdmasterkeyid'] + assert orig_masterkeyid != new_masterkeyid + addr = self.nodes[1].getnewaddress() + assert_equal(orig_masterkeyid, self.nodes[1].getaddressinfo(addr)['hdmasterkeyid']) + assert_equal(self.nodes[1].getaddressinfo(addr)['hdkeypath'], 'm/0\'/0\'/1\'') # Make sure the new address continues previous keypool + + # Check that the next address is from the new seed + self.nodes[1].keypoolrefill(1) + next_addr = self.nodes[1].getnewaddress() + assert_equal(new_masterkeyid, self.nodes[1].getaddressinfo(next_addr)['hdmasterkeyid']) + assert_equal(self.nodes[1].getaddressinfo(next_addr)['hdkeypath'], 'm/0\'/0\'/0\'') # Make sure the new address is not from previous keypool + assert next_addr != addr + + # Sethdseed parameter validity + assert_raises_rpc_error(-1, 'sethdseed', self.nodes[0].sethdseed, False, new_seed, 0) + assert_raises_rpc_error(-5, "Invalid private key", self.nodes[1].sethdseed, False, "not_wif") + assert_raises_rpc_error(-1, "JSON value is not a boolean as expected", self.nodes[1].sethdseed, "Not_bool") + assert_raises_rpc_error(-1, "JSON value is not a string as expected", self.nodes[1].sethdseed, False, True) + assert_raises_rpc_error(-5, "Already have this key", self.nodes[1].sethdseed, False, new_seed) + assert_raises_rpc_error(-5, "Already have this key", self.nodes[1].sethdseed, False, self.nodes[1].dumpprivkey(self.nodes[1].getnewaddress())) + if __name__ == '__main__': WalletHDTest().main () diff --git a/test/functional/wallet_import_rescan.py b/test/functional/wallet_import_rescan.py index b66e9b5d91..4c0ffb938b 100755 --- a/test/functional/wallet_import_rescan.py +++ b/test/functional/wallet_import_rescan.py @@ -42,16 +42,15 @@ class Variant(collections.namedtuple("Variant", "call data rescan prune")): def do_import(self, timestamp): """Call one key import RPC.""" + rescan = self.rescan == Rescan.yes if self.call == Call.single: if self.data == Data.address: - response = self.try_rpc(self.node.importaddress, self.address["address"], self.label, - self.rescan == Rescan.yes) + response = self.try_rpc(self.node.importaddress, address=self.address["address"], rescan=rescan) elif self.data == Data.pub: - response = self.try_rpc(self.node.importpubkey, self.address["pubkey"], self.label, - self.rescan == Rescan.yes) + response = self.try_rpc(self.node.importpubkey, pubkey=self.address["pubkey"], rescan=rescan) elif self.data == Data.priv: - response = self.try_rpc(self.node.importprivkey, self.key, self.label, self.rescan == Rescan.yes) + response = self.try_rpc(self.node.importprivkey, privkey=self.key, rescan=rescan) assert_equal(response, None) elif self.call == Call.multi: @@ -62,30 +61,22 @@ class Variant(collections.namedtuple("Variant", "call data rescan prune")): "timestamp": timestamp + TIMESTAMP_WINDOW + (1 if self.rescan == Rescan.late_timestamp else 0), "pubkeys": [self.address["pubkey"]] if self.data == Data.pub else [], "keys": [self.key] if self.data == Data.priv else [], - "label": self.label, "watchonly": self.data != Data.priv }], {"rescan": self.rescan in (Rescan.yes, Rescan.late_timestamp)}) assert_equal(response, [{"success": True}]) def check(self, txid=None, amount=None, confirmations=None): - """Verify that getbalance/listtransactions return expected values.""" + """Verify that listreceivedbyaddress returns expected values.""" - balance = self.node.getbalance(self.label, 0, True) - assert_equal(balance, self.expected_balance) - - txs = self.node.listtransactions(self.label, 10000, 0, True) - assert_equal(len(txs), self.expected_txs) + addresses = self.node.listreceivedbyaddress(minconf=0, include_watchonly=True, address_filter=self.address['address']) + if self.expected_txs: + assert_equal(len(addresses[0]["txids"]), self.expected_txs) if txid is not None: - tx, = [tx for tx in txs if tx["txid"] == txid] - assert_equal(tx["label"], self.label) - assert_equal(tx["address"], self.address["address"]) - assert_equal(tx["amount"], amount) - assert_equal(tx["category"], "receive") - assert_equal(tx["label"], self.label) - assert_equal(tx["txid"], txid) - assert_equal(tx["confirmations"], confirmations) - assert_equal("trusted" not in tx, True) + address, = [ad for ad in addresses if txid in ad["txids"]] + assert_equal(address["address"], self.address["address"]) + assert_equal(address["amount"], self.expected_balance) + assert_equal(address["confirmations"], confirmations) # Verify the transaction is correctly marked watchonly depending on # whether the transaction pays to an imported public key or # imported private key. The test setup ensures that transaction @@ -93,9 +84,9 @@ class Variant(collections.namedtuple("Variant", "call data rescan prune")): # involvesWatchonly will be true if either the transaction output # or inputs are watchonly). if self.data != Data.priv: - assert_equal(tx["involvesWatchonly"], True) + assert_equal(address["involvesWatchonly"], True) else: - assert_equal("involvesWatchonly" not in tx, True) + assert_equal("involvesWatchonly" not in address, True) # List of Variants for each way a key or address could be imported. @@ -130,11 +121,10 @@ class ImportRescanTest(BitcoinTestFramework): connect_nodes(self.nodes[i], 0) def run_test(self): - # Create one transaction on node 0 with a unique amount and label for + # Create one transaction on node 0 with a unique amount for # each possible type of wallet import RPC. for i, variant in enumerate(IMPORT_VARIANTS): - variant.label = "label {} {}".format(i, variant) - variant.address = self.nodes[1].getaddressinfo(self.nodes[1].getnewaddress(variant.label)) + variant.address = self.nodes[1].getaddressinfo(self.nodes[1].getnewaddress()) variant.key = self.nodes[1].dumpprivkey(variant.address["address"]) variant.initial_amount = 10 - (i + 1) / 4.0 variant.initial_txid = self.nodes[0].sendtoaddress(variant.address["address"], variant.initial_amount) diff --git a/test/functional/wallet_importprunedfunds.py b/test/functional/wallet_importprunedfunds.py index 4d349db23f..9cee9aa49a 100755 --- a/test/functional/wallet_importprunedfunds.py +++ b/test/functional/wallet_importprunedfunds.py @@ -3,38 +3,44 @@ # 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.""" +from decimal import Decimal + from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import * +from test_framework.util import ( + assert_equal, + assert_raises_rpc_error, +) class ImportPrunedFundsTest(BitcoinTestFramework): def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 2 + self.extra_args = [['-deprecatedrpc=accounts']] * 2 def run_test(self): self.log.info("Mining blocks...") self.nodes[0].generate(101) self.sync_all() - + # address address1 = self.nodes[0].getnewaddress() # pubkey address2 = self.nodes[0].getnewaddress() # privkey address3 = self.nodes[0].getnewaddress() - address3_privkey = self.nodes[0].dumpprivkey(address3) # Using privkey + address3_privkey = self.nodes[0].dumpprivkey(address3) # Using privkey - #Check only one address + # Check only one address address_info = self.nodes[0].getaddressinfo(address1) assert_equal(address_info['ismine'], True) self.sync_all() - #Node 1 sync test - assert_equal(self.nodes[1].getblockcount(),101) + # Node 1 sync test + assert_equal(self.nodes[1].getblockcount(), 101) - #Address Test - before import + # Address Test - before import address_info = self.nodes[1].getaddressinfo(address1) assert_equal(address_info['iswatchonly'], False) assert_equal(address_info['ismine'], False) @@ -47,7 +53,7 @@ class ImportPrunedFundsTest(BitcoinTestFramework): assert_equal(address_info['iswatchonly'], False) assert_equal(address_info['ismine'], False) - #Send funds to self + # Send funds to self txnid1 = self.nodes[0].sendtoaddress(address1, 0.1) self.nodes[0].generate(1) rawtxn1 = self.nodes[0].gettransaction(txnid1)['hex'] @@ -65,19 +71,19 @@ class ImportPrunedFundsTest(BitcoinTestFramework): self.sync_all() - #Import with no affiliated address + # Import with no affiliated address assert_raises_rpc_error(-5, "No addresses", self.nodes[1].importprunedfunds, rawtxn1, proof1) balance1 = self.nodes[1].getbalance("", 0, True) assert_equal(balance1, Decimal(0)) - #Import with affiliated address with no rescan + # Import with affiliated address with no rescan self.nodes[1].importaddress(address2, "add2", False) self.nodes[1].importprunedfunds(rawtxn2, proof2) balance2 = self.nodes[1].getbalance("add2", 0, True) assert_equal(balance2, Decimal('0.05')) - #Import with private key with no rescan + # Import with private key with no rescan self.nodes[1].importprivkey(privkey=address3_privkey, label="add3", rescan=False) self.nodes[1].importprunedfunds(rawtxn3, proof3) balance3 = self.nodes[1].getbalance("add3", 0, False) @@ -85,7 +91,7 @@ class ImportPrunedFundsTest(BitcoinTestFramework): balance3 = self.nodes[1].getbalance("*", 0, True) assert_equal(balance3, Decimal('0.075')) - #Addresses Test - after import + # Addresses Test - after import address_info = self.nodes[1].getaddressinfo(address1) assert_equal(address_info['iswatchonly'], False) assert_equal(address_info['ismine'], False) @@ -96,7 +102,7 @@ class ImportPrunedFundsTest(BitcoinTestFramework): assert_equal(address_info['iswatchonly'], False) assert_equal(address_info['ismine'], True) - #Remove transactions + # Remove transactions assert_raises_rpc_error(-8, "Transaction does not exist in wallet.", self.nodes[1].removeprunedfunds, txnid1) balance1 = self.nodes[1].getbalance("*", 0, True) diff --git a/test/functional/wallet_keypool.py b/test/functional/wallet_keypool.py index 9825e4d894..505014e48f 100755 --- a/test/functional/wallet_keypool.py +++ b/test/functional/wallet_keypool.py @@ -17,7 +17,7 @@ class KeyPoolTest(BitcoinTestFramework): addr_before_encrypting_data = nodes[0].getaddressinfo(addr_before_encrypting) wallet_info_old = nodes[0].getwalletinfo() assert(addr_before_encrypting_data['hdmasterkeyid'] == wallet_info_old['hdmasterkeyid']) - + # Encrypt wallet and wait to terminate nodes[0].node_encrypt_wallet('test') # Restart node 0 diff --git a/test/functional/wallet_keypool_topup.py b/test/functional/wallet_keypool_topup.py index 30a0c9a760..d657dc30c4 100755 --- a/test/functional/wallet_keypool_topup.py +++ b/test/functional/wallet_keypool_topup.py @@ -25,7 +25,7 @@ class KeypoolRestoreTest(BitcoinTestFramework): def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 2 - self.extra_args = [[], ['-keypool=100', '-keypoolmin=20']] + self.extra_args = [[], ['-keypool=100']] def run_test(self): wallet_path = os.path.join(self.nodes[1].datadir, "regtest", "wallets", "wallet.dat") diff --git a/test/functional/wallet_labels.py b/test/functional/wallet_labels.py index 90eefc0438..705dd8985e 100755 --- a/test/functional/wallet_labels.py +++ b/test/functional/wallet_labels.py @@ -6,25 +6,38 @@ RPCs tested are: - getlabeladdress - - getaddressesbyaccount + - getaddressesbyaccount/getaddressesbylabel - listaddressgroupings - setlabel - sendfrom (with account arguments) - move (with account arguments) + +Run the test twice - once using the accounts API and once using the labels API. +The accounts API test can be removed in V0.18. """ from collections import defaultdict from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import assert_equal +from test_framework.util import assert_equal, assert_raises_rpc_error class WalletLabelsTest(BitcoinTestFramework): def set_test_params(self): self.setup_clean_chain = True - self.num_nodes = 1 - self.extra_args = [[]] + self.num_nodes = 2 + self.extra_args = [['-deprecatedrpc=accounts'], []] + + def setup_network(self): + """Don't connect nodes.""" + self.setup_nodes() def run_test(self): - node = self.nodes[0] + """Run the test twice - once using the accounts API and once using the labels API.""" + self.log.info("Test accounts API") + self._run_subtest(True, self.nodes[0]) + self.log.info("Test labels API") + self._run_subtest(False, self.nodes[1]) + + def _run_subtest(self, accounts_api, node): # Check that there's no UTXO on any of the nodes assert_equal(len(node.listunspent()), 0) @@ -77,7 +90,7 @@ class WalletLabelsTest(BitcoinTestFramework): # Create labels and make sure subsequent label API calls # recognize the label/address associations. - labels = [Label(name) for name in ("a", "b", "c", "d", "e")] + labels = [Label(name, accounts_api) for name in ("a", "b", "c", "d", "e")] for label in labels: label.add_receive_address(node.getlabeladdress(label=label.name, force=True)) label.verify(node) @@ -101,21 +114,23 @@ class WalletLabelsTest(BitcoinTestFramework): # Check that sendfrom label reduces listaccounts balances. for i, label in enumerate(labels): - to_label = labels[(i+1) % len(labels)] + to_label = labels[(i + 1) % len(labels)] node.sendfrom(label.name, to_label.receive_address, amount_to_send) node.generate(1) for label in labels: label.add_receive_address(node.getlabeladdress(label.name)) label.verify(node) assert_equal(node.getreceivedbylabel(label.name), 2) - node.move(label.name, "", node.getbalance(label.name)) + if accounts_api: + node.move(label.name, "", node.getbalance(label.name)) label.verify(node) node.generate(101) expected_account_balances = {"": 5200} for label in labels: expected_account_balances[label.name] = 0 - assert_equal(node.listaccounts(), expected_account_balances) - assert_equal(node.getbalance(""), 5200) + if accounts_api: + assert_equal(node.listaccounts(), expected_account_balances) + assert_equal(node.getbalance(""), 5200) # Check that setlabel can assign a label to a new unused address. for label in labels: @@ -123,7 +138,10 @@ class WalletLabelsTest(BitcoinTestFramework): node.setlabel(address, label.name) label.add_address(address) label.verify(node) - assert(address not in node.getaddressesbyaccount("")) + if accounts_api: + assert(address not in node.getaddressesbyaccount("")) + else: + assert_raises_rpc_error(-11, "No addresses with label", node.getaddressesbylabel, "") # Check that addmultisigaddress can assign labels. for label in labels: @@ -136,8 +154,9 @@ class WalletLabelsTest(BitcoinTestFramework): label.verify(node) node.sendfrom("", multisig_address, 50) node.generate(101) - for label in labels: - assert_equal(node.getbalance(label.name), 50) + if accounts_api: + for label in labels: + assert_equal(node.getbalance(label.name), 50) # Check that setlabel can change the label of an address from a # different label. @@ -156,9 +175,10 @@ class WalletLabelsTest(BitcoinTestFramework): change_label(node, labels[2].receive_address, labels[2], labels[2]) class Label: - def __init__(self, name): + def __init__(self, name, accounts_api): # Label name self.name = name + self.accounts_api = accounts_api # Current receiving address associated with this label. self.receive_address = None # List of all addresses assigned with this label @@ -184,13 +204,16 @@ class Label: node.getaddressinfo(address)['labels'][0], {"name": self.name, "purpose": self.purpose[address]}) - assert_equal(node.getaccount(address), self.name) + if self.accounts_api: + assert_equal(node.getaccount(address), self.name) + else: + assert_equal(node.getaddressinfo(address)['label'], self.name) assert_equal( node.getaddressesbylabel(self.name), {address: {"purpose": self.purpose[address]} for address in self.addresses}) - assert_equal( - set(node.getaddressesbyaccount(self.name)), set(self.addresses)) + if self.accounts_api: + assert_equal(set(node.getaddressesbyaccount(self.name)), set(self.addresses)) def change_label(node, address, old_label, new_label): diff --git a/test/functional/wallet_listreceivedby.py b/test/functional/wallet_listreceivedby.py index 7b34febddc..7447211296 100755 --- a/test/functional/wallet_listreceivedby.py +++ b/test/functional/wallet_listreceivedby.py @@ -50,37 +50,37 @@ class ReceivedByTest(BitcoinTestFramework): {"address": empty_addr}, {"address": empty_addr, "label": "", "amount": 0, "confirmations": 0, "txids": []}) - #Test Address filtering - #Only on addr - expected = {"address":addr, "label":"", "amount":Decimal("0.1"), "confirmations":10, "txids":[txid,]} + # Test Address filtering + # Only on addr + expected = {"address": addr, "label": "", "amount": Decimal("0.1"), "confirmations": 10, "txids": [txid, ]} res = self.nodes[1].listreceivedbyaddress(minconf=0, include_empty=True, include_watchonly=True, address_filter=addr) - assert_array_result(res, {"address":addr}, expected) + assert_array_result(res, {"address": addr}, expected) assert_equal(len(res), 1) - #Error on invalid address + # Error on invalid address assert_raises_rpc_error(-4, "address_filter parameter was invalid", self.nodes[1].listreceivedbyaddress, minconf=0, include_empty=True, include_watchonly=True, address_filter="bamboozling") - #Another address receive money + # Another address receive money res = self.nodes[1].listreceivedbyaddress(0, True, True) - assert_equal(len(res), 2) #Right now 2 entries + assert_equal(len(res), 2) # Right now 2 entries other_addr = self.nodes[1].getnewaddress() txid2 = self.nodes[0].sendtoaddress(other_addr, 0.1) self.nodes[0].generate(1) self.sync_all() - #Same test as above should still pass - expected = {"address":addr, "label":"", "amount":Decimal("0.1"), "confirmations":11, "txids":[txid,]} + # Same test as above should still pass + expected = {"address": addr, "label": "", "amount": Decimal("0.1"), "confirmations": 11, "txids": [txid, ]} res = self.nodes[1].listreceivedbyaddress(0, True, True, addr) - assert_array_result(res, {"address":addr}, expected) + assert_array_result(res, {"address": addr}, expected) assert_equal(len(res), 1) - #Same test as above but with other_addr should still pass - expected = {"address":other_addr, "label":"", "amount":Decimal("0.1"), "confirmations":1, "txids":[txid2,]} + # Same test as above but with other_addr should still pass + expected = {"address": other_addr, "label": "", "amount": Decimal("0.1"), "confirmations": 1, "txids": [txid2, ]} res = self.nodes[1].listreceivedbyaddress(0, True, True, other_addr) - assert_array_result(res, {"address":other_addr}, expected) + assert_array_result(res, {"address": other_addr}, expected) assert_equal(len(res), 1) - #Should be two entries though without filter + # Should be two entries though without filter res = self.nodes[1].listreceivedbyaddress(0, True, True) - assert_equal(len(res), 3) #Became 3 entries + assert_equal(len(res), 3) # Became 3 entries - #Not on random addr - other_addr = self.nodes[0].getnewaddress() # note on node[0]! just a random addr + # Not on random addr + other_addr = self.nodes[0].getnewaddress() # note on node[0]! just a random addr res = self.nodes[1].listreceivedbyaddress(0, True, True, other_addr) assert_equal(len(res), 0) @@ -111,8 +111,9 @@ class ReceivedByTest(BitcoinTestFramework): self.log.info("listreceivedbylabel + getreceivedbylabel Test") # set pre-state - addrArr = self.nodes[1].getnewaddress() - label = self.nodes[1].getaccount(addrArr) + label = '' + address = self.nodes[1].getnewaddress() + assert_equal(self.nodes[1].getaddressinfo(address)['label'], label) received_by_label_json = [r for r in self.nodes[1].listreceivedbylabel() if r["label"] == label][0] balance_by_label = self.nodes[1].getreceivedbylabel(label) @@ -140,7 +141,8 @@ class ReceivedByTest(BitcoinTestFramework): assert_equal(balance, balance_by_label + Decimal("0.1")) # Create a new label named "mynewlabel" that has a 0 balance - self.nodes[1].getlabeladdress(label="mynewlabel", force=True) + address = self.nodes[1].getnewaddress() + self.nodes[1].setlabel(address, "mynewlabel") received_by_label_json = [r for r in self.nodes[1].listreceivedbylabel(0, True) if r["label"] == "mynewlabel"][0] # Test includeempty of listreceivedbylabel diff --git a/test/functional/wallet_listsinceblock.py b/test/functional/wallet_listsinceblock.py index 0f2434ff0d..63c179411c 100755 --- a/test/functional/wallet_listsinceblock.py +++ b/test/functional/wallet_listsinceblock.py @@ -149,26 +149,26 @@ class ListSinceBlockTest (BitcoinTestFramework): # send from nodes[1] using utxo to nodes[0] change = '%.8f' % (float(utxo['amount']) - 1.0003) - recipientDict = { + recipient_dict = { self.nodes[0].getnewaddress(): 1, self.nodes[1].getnewaddress(): change, } - utxoDicts = [{ + utxo_dicts = [{ 'txid': utxo['txid'], 'vout': utxo['vout'], }] txid1 = self.nodes[1].sendrawtransaction( self.nodes[1].signrawtransactionwithwallet( - self.nodes[1].createrawtransaction(utxoDicts, recipientDict))['hex']) + self.nodes[1].createrawtransaction(utxo_dicts, recipient_dict))['hex']) # send from nodes[2] using utxo to nodes[3] - recipientDict2 = { + recipient_dict2 = { self.nodes[3].getnewaddress(): 1, self.nodes[2].getnewaddress(): change, } self.nodes[2].sendrawtransaction( self.nodes[2].signrawtransactionwithwallet( - self.nodes[2].createrawtransaction(utxoDicts, recipientDict2))['hex']) + self.nodes[2].createrawtransaction(utxo_dicts, recipient_dict2))['hex']) # generate on both sides lastblockhash = self.nodes[1].generate(3)[2] @@ -224,16 +224,16 @@ class ListSinceBlockTest (BitcoinTestFramework): utxos = self.nodes[2].listunspent() utxo = utxos[0] change = '%.8f' % (float(utxo['amount']) - 1.0003) - recipientDict = { + recipient_dict = { self.nodes[0].getnewaddress(): 1, self.nodes[2].getnewaddress(): change, } - utxoDicts = [{ + utxo_dicts = [{ 'txid': utxo['txid'], 'vout': utxo['vout'], }] signedtxres = self.nodes[2].signrawtransactionwithwallet( - self.nodes[2].createrawtransaction(utxoDicts, recipientDict)) + self.nodes[2].createrawtransaction(utxo_dicts, recipient_dict)) assert signedtxres['complete'] signedtx = signedtxres['hex'] diff --git a/test/functional/rpc_listtransactions.py b/test/functional/wallet_listtransactions.py index 0dd7372e6b..7cf2e456cf 100755 --- a/test/functional/rpc_listtransactions.py +++ b/test/functional/wallet_listtransactions.py @@ -3,13 +3,20 @@ # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. """Test the listtransactions API.""" - -from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import * -from test_framework.mininode import CTransaction, COIN +from decimal import Decimal from io import BytesIO -def txFromHex(hexstring): +from test_framework.mininode import CTransaction, COIN +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import ( + assert_array_result, + assert_equal, + bytes_to_hex_str, + hex_str_to_bytes, + sync_mempools, +) + +def tx_from_hex(hexstring): tx = CTransaction() f = BytesIO(hex_str_to_bytes(hexstring)) tx.deserialize(f) @@ -25,61 +32,61 @@ class ListTransactionsTest(BitcoinTestFramework): txid = self.nodes[0].sendtoaddress(self.nodes[1].getnewaddress(), 0.1) self.sync_all() assert_array_result(self.nodes[0].listtransactions(), - {"txid":txid}, - {"category":"send","account":"","amount":Decimal("-0.1"),"confirmations":0}) + {"txid": txid}, + {"category": "send", "amount": Decimal("-0.1"), "confirmations": 0}) assert_array_result(self.nodes[1].listtransactions(), - {"txid":txid}, - {"category":"receive","account":"","amount":Decimal("0.1"),"confirmations":0}) + {"txid": txid}, + {"category": "receive", "amount": Decimal("0.1"), "confirmations": 0}) # mine a block, confirmations should change: self.nodes[0].generate(1) self.sync_all() assert_array_result(self.nodes[0].listtransactions(), - {"txid":txid}, - {"category":"send","account":"","amount":Decimal("-0.1"),"confirmations":1}) + {"txid": txid}, + {"category": "send", "amount": Decimal("-0.1"), "confirmations": 1}) assert_array_result(self.nodes[1].listtransactions(), - {"txid":txid}, - {"category":"receive","account":"","amount":Decimal("0.1"),"confirmations":1}) + {"txid": txid}, + {"category": "receive", "amount": Decimal("0.1"), "confirmations": 1}) # send-to-self: txid = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 0.2) assert_array_result(self.nodes[0].listtransactions(), - {"txid":txid, "category":"send"}, - {"amount":Decimal("-0.2")}) + {"txid": txid, "category": "send"}, + {"amount": Decimal("-0.2")}) assert_array_result(self.nodes[0].listtransactions(), - {"txid":txid, "category":"receive"}, - {"amount":Decimal("0.2")}) + {"txid": txid, "category": "receive"}, + {"amount": Decimal("0.2")}) # sendmany from node1: twice to self, twice to node2: - send_to = { self.nodes[0].getnewaddress() : 0.11, - self.nodes[1].getnewaddress() : 0.22, - self.nodes[0].getaccountaddress("from1") : 0.33, - self.nodes[1].getaccountaddress("toself") : 0.44 } + send_to = {self.nodes[0].getnewaddress(): 0.11, + self.nodes[1].getnewaddress(): 0.22, + self.nodes[0].getnewaddress(): 0.33, + self.nodes[1].getnewaddress(): 0.44} txid = self.nodes[1].sendmany("", send_to) self.sync_all() assert_array_result(self.nodes[1].listtransactions(), - {"category":"send","amount":Decimal("-0.11")}, - {"txid":txid} ) + {"category": "send", "amount": Decimal("-0.11")}, + {"txid": txid}) assert_array_result(self.nodes[0].listtransactions(), - {"category":"receive","amount":Decimal("0.11")}, - {"txid":txid} ) + {"category": "receive", "amount": Decimal("0.11")}, + {"txid": txid}) assert_array_result(self.nodes[1].listtransactions(), - {"category":"send","amount":Decimal("-0.22")}, - {"txid":txid} ) + {"category": "send", "amount": Decimal("-0.22")}, + {"txid": txid}) assert_array_result(self.nodes[1].listtransactions(), - {"category":"receive","amount":Decimal("0.22")}, - {"txid":txid} ) + {"category": "receive", "amount": Decimal("0.22")}, + {"txid": txid}) assert_array_result(self.nodes[1].listtransactions(), - {"category":"send","amount":Decimal("-0.33")}, - {"txid":txid} ) + {"category": "send", "amount": Decimal("-0.33")}, + {"txid": txid}) assert_array_result(self.nodes[0].listtransactions(), - {"category":"receive","amount":Decimal("0.33")}, - {"txid":txid, "account" : "from1"} ) + {"category": "receive", "amount": Decimal("0.33")}, + {"txid": txid}) assert_array_result(self.nodes[1].listtransactions(), - {"category":"send","amount":Decimal("-0.44")}, - {"txid":txid, "account" : ""} ) + {"category": "send", "amount": Decimal("-0.44")}, + {"txid": txid}) assert_array_result(self.nodes[1].listtransactions(), - {"category":"receive","amount":Decimal("0.44")}, - {"txid":txid, "account" : "toself"} ) + {"category": "receive", "amount": Decimal("0.44")}, + {"txid": txid}) pubkey = self.nodes[1].getaddressinfo(self.nodes[1].getnewaddress())['pubkey'] multisig = self.nodes[1].createmultisig(1, [pubkey]) @@ -87,10 +94,9 @@ class ListTransactionsTest(BitcoinTestFramework): txid = self.nodes[1].sendtoaddress(multisig["address"], 0.1) self.nodes[1].generate(1) self.sync_all() - assert(len(self.nodes[0].listtransactions("watchonly", 100, 0, False)) == 0) - assert_array_result(self.nodes[0].listtransactions("watchonly", 100, 0, True), - {"category":"receive","amount":Decimal("0.1")}, - {"txid":txid, "account" : "watchonly"} ) + assert not [tx for tx in self.nodes[0].listtransactions(dummy="*", count=100, skip=0, include_watchonly=False) if "label" in tx and tx["label"] == "watchonly"] + txs = [tx for tx in self.nodes[0].listtransactions(dummy="*", count=100, skip=0, include_watchonly=True) if "label" in tx and tx['label'] == 'watchonly'] + assert_array_result(txs, {"category": "receive", "amount": Decimal("0.1")}, {"txid": txid}) self.run_rbf_opt_in_test() @@ -116,9 +122,9 @@ class ListTransactionsTest(BitcoinTestFramework): # 1. Chain a few transactions that don't opt-in. txid_1 = self.nodes[0].sendtoaddress(self.nodes[1].getnewaddress(), 1) assert(not is_opt_in(self.nodes[0], txid_1)) - assert_array_result(self.nodes[0].listtransactions(), {"txid": txid_1}, {"bip125-replaceable":"no"}) + assert_array_result(self.nodes[0].listtransactions(), {"txid": txid_1}, {"bip125-replaceable": "no"}) sync_mempools(self.nodes) - assert_array_result(self.nodes[1].listtransactions(), {"txid": txid_1}, {"bip125-replaceable":"no"}) + assert_array_result(self.nodes[1].listtransactions(), {"txid": txid_1}, {"bip125-replaceable": "no"}) # Tx2 will build off txid_1, still not opting in to RBF. utxo_to_use = get_unconfirmed_utxo_entry(self.nodes[0], txid_1) @@ -128,7 +134,7 @@ class ListTransactionsTest(BitcoinTestFramework): assert_equal(utxo_to_use["safe"], False) # Create tx2 using createrawtransaction - inputs = [{"txid":utxo_to_use["txid"], "vout":utxo_to_use["vout"]}] + inputs = [{"txid": utxo_to_use["txid"], "vout": utxo_to_use["vout"]}] outputs = {self.nodes[0].getnewaddress(): 0.999} tx2 = self.nodes[1].createrawtransaction(inputs, outputs) tx2_signed = self.nodes[1].signrawtransactionwithwallet(tx2)["hex"] @@ -136,51 +142,51 @@ class ListTransactionsTest(BitcoinTestFramework): # ...and check the result assert(not is_opt_in(self.nodes[1], txid_2)) - assert_array_result(self.nodes[1].listtransactions(), {"txid": txid_2}, {"bip125-replaceable":"no"}) + assert_array_result(self.nodes[1].listtransactions(), {"txid": txid_2}, {"bip125-replaceable": "no"}) sync_mempools(self.nodes) - assert_array_result(self.nodes[0].listtransactions(), {"txid": txid_2}, {"bip125-replaceable":"no"}) + assert_array_result(self.nodes[0].listtransactions(), {"txid": txid_2}, {"bip125-replaceable": "no"}) # Tx3 will opt-in to RBF utxo_to_use = get_unconfirmed_utxo_entry(self.nodes[0], txid_2) - inputs = [{"txid": txid_2, "vout":utxo_to_use["vout"]}] + inputs = [{"txid": txid_2, "vout": utxo_to_use["vout"]}] outputs = {self.nodes[1].getnewaddress(): 0.998} tx3 = self.nodes[0].createrawtransaction(inputs, outputs) - tx3_modified = txFromHex(tx3) + tx3_modified = tx_from_hex(tx3) tx3_modified.vin[0].nSequence = 0 tx3 = bytes_to_hex_str(tx3_modified.serialize()) tx3_signed = self.nodes[0].signrawtransactionwithwallet(tx3)['hex'] txid_3 = self.nodes[0].sendrawtransaction(tx3_signed) assert(is_opt_in(self.nodes[0], txid_3)) - assert_array_result(self.nodes[0].listtransactions(), {"txid": txid_3}, {"bip125-replaceable":"yes"}) + assert_array_result(self.nodes[0].listtransactions(), {"txid": txid_3}, {"bip125-replaceable": "yes"}) sync_mempools(self.nodes) - assert_array_result(self.nodes[1].listtransactions(), {"txid": txid_3}, {"bip125-replaceable":"yes"}) + assert_array_result(self.nodes[1].listtransactions(), {"txid": txid_3}, {"bip125-replaceable": "yes"}) # Tx4 will chain off tx3. Doesn't signal itself, but depends on one # that does. utxo_to_use = get_unconfirmed_utxo_entry(self.nodes[1], txid_3) - inputs = [{"txid": txid_3, "vout":utxo_to_use["vout"]}] + inputs = [{"txid": txid_3, "vout": utxo_to_use["vout"]}] outputs = {self.nodes[0].getnewaddress(): 0.997} tx4 = self.nodes[1].createrawtransaction(inputs, outputs) tx4_signed = self.nodes[1].signrawtransactionwithwallet(tx4)["hex"] txid_4 = self.nodes[1].sendrawtransaction(tx4_signed) assert(not is_opt_in(self.nodes[1], txid_4)) - assert_array_result(self.nodes[1].listtransactions(), {"txid": txid_4}, {"bip125-replaceable":"yes"}) + assert_array_result(self.nodes[1].listtransactions(), {"txid": txid_4}, {"bip125-replaceable": "yes"}) sync_mempools(self.nodes) - assert_array_result(self.nodes[0].listtransactions(), {"txid": txid_4}, {"bip125-replaceable":"yes"}) + assert_array_result(self.nodes[0].listtransactions(), {"txid": txid_4}, {"bip125-replaceable": "yes"}) # Replace tx3, and check that tx4 becomes unknown tx3_b = tx3_modified - tx3_b.vout[0].nValue -= int(Decimal("0.004") * COIN) # bump the fee + tx3_b.vout[0].nValue -= int(Decimal("0.004") * COIN) # bump the fee tx3_b = bytes_to_hex_str(tx3_b.serialize()) tx3_b_signed = self.nodes[0].signrawtransactionwithwallet(tx3_b)['hex'] txid_3b = self.nodes[0].sendrawtransaction(tx3_b_signed, True) assert(is_opt_in(self.nodes[0], txid_3b)) - assert_array_result(self.nodes[0].listtransactions(), {"txid": txid_4}, {"bip125-replaceable":"unknown"}) + assert_array_result(self.nodes[0].listtransactions(), {"txid": txid_4}, {"bip125-replaceable": "unknown"}) sync_mempools(self.nodes) - assert_array_result(self.nodes[1].listtransactions(), {"txid": txid_4}, {"bip125-replaceable":"unknown"}) + assert_array_result(self.nodes[1].listtransactions(), {"txid": txid_4}, {"bip125-replaceable": "unknown"}) # Check gettransaction as well: for n in self.nodes[0:2]: @@ -196,7 +202,5 @@ class ListTransactionsTest(BitcoinTestFramework): assert_equal(self.nodes[0].gettransaction(txid_3b)["bip125-replaceable"], "no") assert_equal(self.nodes[0].gettransaction(txid_4)["bip125-replaceable"], "unknown") - if __name__ == '__main__': ListTransactionsTest().main() - diff --git a/test/functional/wallet_multiwallet.py b/test/functional/wallet_multiwallet.py index 0a24d34398..e0571ea8f9 100755 --- a/test/functional/wallet_multiwallet.py +++ b/test/functional/wallet_multiwallet.py @@ -91,6 +91,19 @@ class MultiWalletTest(BitcoinTestFramework): open(not_a_dir, 'a').close() self.nodes[0].assert_start_raises_init_error(['-walletdir=' + not_a_dir], 'Error: Specified -walletdir "' + not_a_dir + '" is not a directory') + self.log.info("Do not allow -zapwallettxes with multiwallet") + self.nodes[0].assert_start_raises_init_error(['-zapwallettxes', '-wallet=w1', '-wallet=w2'], "Error: -zapwallettxes is only allowed with a single wallet file") + self.nodes[0].assert_start_raises_init_error(['-zapwallettxes=1', '-wallet=w1', '-wallet=w2'], "Error: -zapwallettxes is only allowed with a single wallet file") + self.nodes[0].assert_start_raises_init_error(['-zapwallettxes=2', '-wallet=w1', '-wallet=w2'], "Error: -zapwallettxes is only allowed with a single wallet file") + + self.log.info("Do not allow -salvagewallet with multiwallet") + self.nodes[0].assert_start_raises_init_error(['-salvagewallet', '-wallet=w1', '-wallet=w2'], "Error: -salvagewallet is only allowed with a single wallet file") + self.nodes[0].assert_start_raises_init_error(['-salvagewallet=1', '-wallet=w1', '-wallet=w2'], "Error: -salvagewallet is only allowed with a single wallet file") + + self.log.info("Do not allow -upgradewallet with multiwallet") + self.nodes[0].assert_start_raises_init_error(['-upgradewallet', '-wallet=w1', '-wallet=w2'], "Error: -upgradewallet is only allowed with a single wallet file") + self.nodes[0].assert_start_raises_init_error(['-upgradewallet=1', '-wallet=w1', '-wallet=w2'], "Error: -upgradewallet is only allowed with a single wallet file") + # if wallets/ doesn't exist, datadir should be the default wallet dir wallet_dir2 = data_dir('walletdir') os.rename(wallet_dir(), wallet_dir2) @@ -150,5 +163,12 @@ class MultiWalletTest(BitcoinTestFramework): assert_equal(batch[0]["result"]["chain"], "regtest") assert_equal(batch[1]["result"]["walletname"], "w1") + self.log.info('Check for per-wallet settxfee call') + assert_equal(w1.getwalletinfo()['paytxfee'], 0) + assert_equal(w2.getwalletinfo()['paytxfee'], 0) + w2.settxfee(4.0) + assert_equal(w1.getwalletinfo()['paytxfee'], 0) + assert_equal(w2.getwalletinfo()['paytxfee'], 4.0) + if __name__ == '__main__': MultiWalletTest().main() diff --git a/test/functional/wallet_txn_clone.py b/test/functional/wallet_txn_clone.py index d742ec4618..72ae2d5db7 100755 --- a/test/functional/wallet_txn_clone.py +++ b/test/functional/wallet_txn_clone.py @@ -5,7 +5,12 @@ """Test the wallet accounts properly when there are cloned transactions with malleated scriptsigs.""" from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import * +from test_framework.util import ( + assert_equal, + connect_nodes, + disconnect_nodes, + sync_blocks, +) class TxnMallTest(BitcoinTestFramework): def set_test_params(self): @@ -25,55 +30,53 @@ class TxnMallTest(BitcoinTestFramework): def run_test(self): if self.options.segwit: - output_type="p2sh-segwit" + output_type = "p2sh-segwit" else: - output_type="legacy" + output_type = "legacy" # All nodes should start with 1,250 BTC: starting_balance = 1250 for i in range(4): assert_equal(self.nodes[i].getbalance(), starting_balance) - self.nodes[i].getnewaddress("") # bug workaround, coins generated assigned to first getnewaddress! + self.nodes[i].getnewaddress() # bug workaround, coins generated assigned to first getnewaddress! - # Assign coins to foo and bar accounts: self.nodes[0].settxfee(.001) - node0_address_foo = self.nodes[0].getnewaddress("foo", output_type) - fund_foo_txid = self.nodes[0].sendfrom("", node0_address_foo, 1219) - fund_foo_tx = self.nodes[0].gettransaction(fund_foo_txid) + node0_address1 = self.nodes[0].getnewaddress(address_type=output_type) + node0_txid1 = self.nodes[0].sendtoaddress(node0_address1, 1219) + node0_tx1 = self.nodes[0].gettransaction(node0_txid1) - node0_address_bar = self.nodes[0].getnewaddress("bar", output_type) - fund_bar_txid = self.nodes[0].sendfrom("", node0_address_bar, 29) - fund_bar_tx = self.nodes[0].gettransaction(fund_bar_txid) + node0_address2 = self.nodes[0].getnewaddress(address_type=output_type) + node0_txid2 = self.nodes[0].sendtoaddress(node0_address2, 29) + node0_tx2 = self.nodes[0].gettransaction(node0_txid2) - assert_equal(self.nodes[0].getbalance(""), - starting_balance - 1219 - 29 + fund_foo_tx["fee"] + fund_bar_tx["fee"]) + assert_equal(self.nodes[0].getbalance(), + starting_balance + node0_tx1["fee"] + node0_tx2["fee"]) # Coins are sent to node1_address - node1_address = self.nodes[1].getnewaddress("from0") + node1_address = self.nodes[1].getnewaddress() - # Send tx1, and another transaction tx2 that won't be cloned - txid1 = self.nodes[0].sendfrom("foo", node1_address, 40, 0) - txid2 = self.nodes[0].sendfrom("bar", node1_address, 20, 0) + # Send tx1, and another transaction tx2 that won't be cloned + txid1 = self.nodes[0].sendtoaddress(node1_address, 40) + txid2 = self.nodes[0].sendtoaddress(node1_address, 20) - # Construct a clone of tx1, to be malleated - rawtx1 = self.nodes[0].getrawtransaction(txid1,1) - clone_inputs = [{"txid":rawtx1["vin"][0]["txid"],"vout":rawtx1["vin"][0]["vout"]}] - clone_outputs = {rawtx1["vout"][0]["scriptPubKey"]["addresses"][0]:rawtx1["vout"][0]["value"], - rawtx1["vout"][1]["scriptPubKey"]["addresses"][0]:rawtx1["vout"][1]["value"]} + # Construct a clone of tx1, to be malleated + rawtx1 = self.nodes[0].getrawtransaction(txid1, 1) + clone_inputs = [{"txid": rawtx1["vin"][0]["txid"], "vout": rawtx1["vin"][0]["vout"]}] + clone_outputs = {rawtx1["vout"][0]["scriptPubKey"]["addresses"][0]: rawtx1["vout"][0]["value"], + rawtx1["vout"][1]["scriptPubKey"]["addresses"][0]: rawtx1["vout"][1]["value"]} clone_locktime = rawtx1["locktime"] clone_raw = self.nodes[0].createrawtransaction(clone_inputs, clone_outputs, clone_locktime) # createrawtransaction randomizes the order of its outputs, so swap them if necessary. # output 0 is at version+#inputs+input+sigstub+sequence+#outputs # 40 BTC serialized is 00286bee00000000 - pos0 = 2*(4+1+36+1+4+1) + pos0 = 2 * (4 + 1 + 36 + 1 + 4 + 1) hex40 = "00286bee00000000" - output_len = 16 + 2 + 2 * int("0x" + clone_raw[pos0 + 16 : pos0 + 16 + 2], 0) - if (rawtx1["vout"][0]["value"] == 40 and clone_raw[pos0 : pos0 + 16] != hex40 or - rawtx1["vout"][0]["value"] != 40 and clone_raw[pos0 : pos0 + 16] == hex40): - output0 = clone_raw[pos0 : pos0 + output_len] - output1 = clone_raw[pos0 + output_len : pos0 + 2 * output_len] + output_len = 16 + 2 + 2 * int("0x" + clone_raw[pos0 + 16:pos0 + 16 + 2], 0) + if (rawtx1["vout"][0]["value"] == 40 and clone_raw[pos0:pos0 + 16] != hex40 or rawtx1["vout"][0]["value"] != 40 and clone_raw[pos0:pos0 + 16] == hex40): + output0 = clone_raw[pos0:pos0 + output_len] + output1 = clone_raw[pos0 + output_len:pos0 + 2 * output_len] clone_raw = clone_raw[:pos0] + output1 + output0 + clone_raw[pos0 + 2 * output_len:] # Use a different signature hash type to sign. This creates an equivalent but malleated clone. @@ -91,27 +94,22 @@ class TxnMallTest(BitcoinTestFramework): # Node0's balance should be starting balance, plus 50BTC for another # matured block, minus tx1 and tx2 amounts, and minus transaction fees: - expected = starting_balance + fund_foo_tx["fee"] + fund_bar_tx["fee"] - if self.options.mine_block: expected += 50 + expected = starting_balance + node0_tx1["fee"] + node0_tx2["fee"] + if self.options.mine_block: + expected += 50 expected += tx1["amount"] + tx1["fee"] expected += tx2["amount"] + tx2["fee"] assert_equal(self.nodes[0].getbalance(), expected) - # foo and bar accounts should be debited: - assert_equal(self.nodes[0].getbalance("foo", 0), 1219 + tx1["amount"] + tx1["fee"]) - assert_equal(self.nodes[0].getbalance("bar", 0), 29 + tx2["amount"] + tx2["fee"]) - if self.options.mine_block: assert_equal(tx1["confirmations"], 1) assert_equal(tx2["confirmations"], 1) - # Node1's "from0" balance should be both transaction amounts: - assert_equal(self.nodes[1].getbalance("from0"), -(tx1["amount"] + tx2["amount"])) else: assert_equal(tx1["confirmations"], 0) assert_equal(tx2["confirmations"], 0) # Send clone and its parent to miner - self.nodes[2].sendrawtransaction(fund_foo_tx["hex"]) + self.nodes[2].sendrawtransaction(node0_tx1["hex"]) txid1_clone = self.nodes[2].sendrawtransaction(tx1_clone["hex"]) if self.options.segwit: assert_equal(txid1, txid1_clone) @@ -122,7 +120,7 @@ class TxnMallTest(BitcoinTestFramework): # Reconnect the split network, and sync chain: connect_nodes(self.nodes[1], 2) - self.nodes[2].sendrawtransaction(fund_bar_tx["hex"]) + self.nodes[2].sendrawtransaction(node0_tx2["hex"]) self.nodes[2].sendrawtransaction(tx2["hex"]) self.nodes[2].generate(1) # Mine another block to make sure we sync sync_blocks(self.nodes) @@ -131,7 +129,7 @@ class TxnMallTest(BitcoinTestFramework): tx1 = self.nodes[0].gettransaction(txid1) tx1_clone = self.nodes[0].gettransaction(txid1_clone) tx2 = self.nodes[0].gettransaction(txid2) - + # Verify expected confirmations assert_equal(tx1["confirmations"], -2) assert_equal(tx1_clone["confirmations"], 2) @@ -140,27 +138,9 @@ class TxnMallTest(BitcoinTestFramework): # Check node0's total balance; should be same as before the clone, + 100 BTC for 2 matured, # less possible orphaned matured subsidy expected += 100 - if (self.options.mine_block): + if (self.options.mine_block): expected -= 50 assert_equal(self.nodes[0].getbalance(), expected) - assert_equal(self.nodes[0].getbalance("*", 0), expected) - - # Check node0's individual account balances. - # "foo" should have been debited by the equivalent clone of tx1 - assert_equal(self.nodes[0].getbalance("foo"), 1219 + tx1["amount"] + tx1["fee"]) - # "bar" should have been debited by (possibly unconfirmed) tx2 - assert_equal(self.nodes[0].getbalance("bar", 0), 29 + tx2["amount"] + tx2["fee"]) - # "" should have starting balance, less funding txes, plus subsidies - assert_equal(self.nodes[0].getbalance("", 0), starting_balance - - 1219 - + fund_foo_tx["fee"] - - 29 - + fund_bar_tx["fee"] - + 100) - - # Node1's "from0" account balance - assert_equal(self.nodes[1].getbalance("from0", 0), -(tx1["amount"] + tx2["amount"])) if __name__ == '__main__': TxnMallTest().main() - diff --git a/test/functional/wallet_txn_doublespend.py b/test/functional/wallet_txn_doublespend.py index f16756eeaa..7ee60d5611 100755 --- a/test/functional/wallet_txn_doublespend.py +++ b/test/functional/wallet_txn_doublespend.py @@ -3,9 +3,16 @@ # 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.""" +from decimal import Decimal from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import * +from test_framework.util import ( + assert_equal, + connect_nodes, + disconnect_nodes, + find_output, + sync_blocks, +) class TxnMallTest(BitcoinTestFramework): def set_test_params(self): @@ -27,21 +34,21 @@ class TxnMallTest(BitcoinTestFramework): for i in range(4): assert_equal(self.nodes[i].getbalance(), starting_balance) self.nodes[i].getnewaddress("") # bug workaround, coins generated assigned to first getnewaddress! - - # Assign coins to foo and bar accounts: - node0_address_foo = self.nodes[0].getnewaddress("foo") - fund_foo_txid = self.nodes[0].sendfrom("", node0_address_foo, 1219) + + # Assign coins to foo and bar addresses: + node0_address_foo = self.nodes[0].getnewaddress() + fund_foo_txid = self.nodes[0].sendtoaddress(node0_address_foo, 1219) fund_foo_tx = self.nodes[0].gettransaction(fund_foo_txid) - node0_address_bar = self.nodes[0].getnewaddress("bar") - fund_bar_txid = self.nodes[0].sendfrom("", node0_address_bar, 29) + node0_address_bar = self.nodes[0].getnewaddress() + fund_bar_txid = self.nodes[0].sendtoaddress(node0_address_bar, 29) fund_bar_tx = self.nodes[0].gettransaction(fund_bar_txid) - assert_equal(self.nodes[0].getbalance(""), - starting_balance - 1219 - 29 + fund_foo_tx["fee"] + fund_bar_tx["fee"]) + assert_equal(self.nodes[0].getbalance(), + starting_balance + fund_foo_tx["fee"] + fund_bar_tx["fee"]) # Coins are sent to node1_address - node1_address = self.nodes[1].getnewaddress("from0") + node1_address = self.nodes[1].getnewaddress() # First: use raw transaction API to send 1240 BTC to node1_address, # but don't broadcast: @@ -62,9 +69,9 @@ class TxnMallTest(BitcoinTestFramework): assert_equal(doublespend["complete"], True) # Create two spends using 1 50 BTC coin each - txid1 = self.nodes[0].sendfrom("foo", node1_address, 40, 0) - txid2 = self.nodes[0].sendfrom("bar", node1_address, 20, 0) - + txid1 = self.nodes[0].sendtoaddress(node1_address, 40) + txid2 = self.nodes[0].sendtoaddress(node1_address, 20) + # Have node0 mine a block: if (self.options.mine_block): self.nodes[0].generate(1) @@ -76,24 +83,21 @@ class TxnMallTest(BitcoinTestFramework): # Node0's balance should be starting balance, plus 50BTC for another # matured block, minus 40, minus 20, and minus transaction fees: expected = starting_balance + fund_foo_tx["fee"] + fund_bar_tx["fee"] - if self.options.mine_block: expected += 50 + if self.options.mine_block: + expected += 50 expected += tx1["amount"] + tx1["fee"] expected += tx2["amount"] + tx2["fee"] assert_equal(self.nodes[0].getbalance(), expected) - # foo and bar accounts should be debited: - assert_equal(self.nodes[0].getbalance("foo", 0), 1219+tx1["amount"]+tx1["fee"]) - assert_equal(self.nodes[0].getbalance("bar", 0), 29+tx2["amount"]+tx2["fee"]) - if self.options.mine_block: assert_equal(tx1["confirmations"], 1) assert_equal(tx2["confirmations"], 1) - # Node1's "from0" balance should be both transaction amounts: - assert_equal(self.nodes[1].getbalance("from0"), -(tx1["amount"]+tx2["amount"])) + # Node1's balance should be both transaction amounts: + assert_equal(self.nodes[1].getbalance(), starting_balance - tx1["amount"] - tx2["amount"]) else: assert_equal(tx1["confirmations"], 0) assert_equal(tx2["confirmations"], 0) - + # Now give doublespend and its parents to miner: self.nodes[2].sendrawtransaction(fund_foo_tx["hex"]) self.nodes[2].sendrawtransaction(fund_bar_tx["hex"]) @@ -115,29 +119,14 @@ class TxnMallTest(BitcoinTestFramework): assert_equal(tx1["confirmations"], -2) assert_equal(tx2["confirmations"], -2) - # Node0's total balance should be starting balance, plus 100BTC for + # Node0's total balance should be starting balance, plus 100BTC for # two more matured blocks, minus 1240 for the double-spend, plus fees (which are # negative): expected = starting_balance + 100 - 1240 + fund_foo_tx["fee"] + fund_bar_tx["fee"] + doublespend_fee assert_equal(self.nodes[0].getbalance(), expected) - assert_equal(self.nodes[0].getbalance("*"), expected) - - # Final "" balance is starting_balance - amount moved to accounts - doublespend + subsidies + - # fees (which are negative) - assert_equal(self.nodes[0].getbalance("foo"), 1219) - assert_equal(self.nodes[0].getbalance("bar"), 29) - assert_equal(self.nodes[0].getbalance(""), starting_balance - -1219 - - 29 - -1240 - + 100 - + fund_foo_tx["fee"] - + fund_bar_tx["fee"] - + doublespend_fee) - - # Node1's "from0" account balance should be just the doublespend: - assert_equal(self.nodes[1].getbalance("from0"), 1240) + + # Node1's balance should be its initial balance (1250 for 25 block rewards) plus the doublespend: + assert_equal(self.nodes[1].getbalance(), 1250 + 1240) if __name__ == '__main__': TxnMallTest().main() - diff --git a/test/util/data/blanktxv1.json b/test/util/data/blanktxv1.json index 9fe2de649b..3d5a1cccae 100644 --- a/test/util/data/blanktxv1.json +++ b/test/util/data/blanktxv1.json @@ -4,6 +4,7 @@ "version": 1, "size": 10, "vsize": 10, + "weight": 40, "locktime": 0, "vin": [ ], diff --git a/test/util/data/blanktxv2.json b/test/util/data/blanktxv2.json index e97626e421..8374a34adc 100644 --- a/test/util/data/blanktxv2.json +++ b/test/util/data/blanktxv2.json @@ -4,6 +4,7 @@ "version": 2, "size": 10, "vsize": 10, + "weight": 40, "locktime": 0, "vin": [ ], diff --git a/test/util/data/tt-delin1-out.json b/test/util/data/tt-delin1-out.json index de647f98b6..9fc2ddc376 100644 --- a/test/util/data/tt-delin1-out.json +++ b/test/util/data/tt-delin1-out.json @@ -4,6 +4,7 @@ "version": 1, "size": 3040, "vsize": 3040, + "weight": 12160, "locktime": 0, "vin": [ { diff --git a/test/util/data/tt-delout1-out.json b/test/util/data/tt-delout1-out.json index 067ffe74e7..922d048900 100644 --- a/test/util/data/tt-delout1-out.json +++ b/test/util/data/tt-delout1-out.json @@ -4,6 +4,7 @@ "version": 1, "size": 3155, "vsize": 3155, + "weight": 12620, "locktime": 0, "vin": [ { diff --git a/test/util/data/tt-locktime317000-out.json b/test/util/data/tt-locktime317000-out.json index af7903d1dd..c97206f1ea 100644 --- a/test/util/data/tt-locktime317000-out.json +++ b/test/util/data/tt-locktime317000-out.json @@ -4,6 +4,7 @@ "version": 1, "size": 3189, "vsize": 3189, + "weight": 12756, "locktime": 317000, "vin": [ { diff --git a/test/util/data/txcreate1.json b/test/util/data/txcreate1.json index 83a86649e0..ca9eacd546 100644 --- a/test/util/data/txcreate1.json +++ b/test/util/data/txcreate1.json @@ -4,6 +4,7 @@ "version": 2, "size": 201, "vsize": 201, + "weight": 804, "locktime": 0, "vin": [ { diff --git a/test/util/data/txcreate2.json b/test/util/data/txcreate2.json index cca00f752b..ee9b9c3c17 100644 --- a/test/util/data/txcreate2.json +++ b/test/util/data/txcreate2.json @@ -4,6 +4,7 @@ "version": 2, "size": 19, "vsize": 19, + "weight": 76, "locktime": 0, "vin": [ ], diff --git a/test/util/data/txcreatedata1.json b/test/util/data/txcreatedata1.json index 15a4246ae5..39909c2e3f 100644 --- a/test/util/data/txcreatedata1.json +++ b/test/util/data/txcreatedata1.json @@ -4,6 +4,7 @@ "version": 1, "size": 176, "vsize": 176, + "weight": 704, "locktime": 0, "vin": [ { diff --git a/test/util/data/txcreatedata2.json b/test/util/data/txcreatedata2.json index cb93c27971..2958006e58 100644 --- a/test/util/data/txcreatedata2.json +++ b/test/util/data/txcreatedata2.json @@ -4,6 +4,7 @@ "version": 2, "size": 176, "vsize": 176, + "weight": 704, "locktime": 0, "vin": [ { diff --git a/test/util/data/txcreatedata_seq0.json b/test/util/data/txcreatedata_seq0.json index 4b5a7cab4a..a6656b5ad5 100644 --- a/test/util/data/txcreatedata_seq0.json +++ b/test/util/data/txcreatedata_seq0.json @@ -4,6 +4,7 @@ "version": 2, "size": 85, "vsize": 85, + "weight": 340, "locktime": 0, "vin": [ { diff --git a/test/util/data/txcreatedata_seq1.json b/test/util/data/txcreatedata_seq1.json index dea48ba373..e5980427b1 100644 --- a/test/util/data/txcreatedata_seq1.json +++ b/test/util/data/txcreatedata_seq1.json @@ -4,6 +4,7 @@ "version": 1, "size": 126, "vsize": 126, + "weight": 504, "locktime": 0, "vin": [ { diff --git a/test/util/data/txcreatemultisig1.json b/test/util/data/txcreatemultisig1.json index 72e20c8691..c32e755db1 100644 --- a/test/util/data/txcreatemultisig1.json +++ b/test/util/data/txcreatemultisig1.json @@ -4,6 +4,7 @@ "version": 1, "size": 124, "vsize": 124, + "weight": 496, "locktime": 0, "vin": [ ], diff --git a/test/util/data/txcreatemultisig2.json b/test/util/data/txcreatemultisig2.json index 7d94ce7396..f97d265894 100644 --- a/test/util/data/txcreatemultisig2.json +++ b/test/util/data/txcreatemultisig2.json @@ -4,6 +4,7 @@ "version": 1, "size": 42, "vsize": 42, + "weight": 168, "locktime": 0, "vin": [ ], diff --git a/test/util/data/txcreatemultisig3.json b/test/util/data/txcreatemultisig3.json index 6c5b49d876..b355d7b191 100644 --- a/test/util/data/txcreatemultisig3.json +++ b/test/util/data/txcreatemultisig3.json @@ -4,6 +4,7 @@ "version": 1, "size": 53, "vsize": 53, + "weight": 212, "locktime": 0, "vin": [ ], diff --git a/test/util/data/txcreatemultisig4.json b/test/util/data/txcreatemultisig4.json index 9a5d2f4a06..a00dbe3f5d 100644 --- a/test/util/data/txcreatemultisig4.json +++ b/test/util/data/txcreatemultisig4.json @@ -4,6 +4,7 @@ "version": 1, "size": 42, "vsize": 42, + "weight": 168, "locktime": 0, "vin": [ ], diff --git a/test/util/data/txcreatemultisig5.json b/test/util/data/txcreatemultisig5.json index 20e9bb077b..ea07822ddd 100644 --- a/test/util/data/txcreatemultisig5.json +++ b/test/util/data/txcreatemultisig5.json @@ -4,6 +4,7 @@ "version": 2, "size": 42, "vsize": 42, + "weight": 168, "locktime": 0, "vin": [ ], diff --git a/test/util/data/txcreateoutpubkey1.json b/test/util/data/txcreateoutpubkey1.json index 2704ed7673..32097b3ebe 100644 --- a/test/util/data/txcreateoutpubkey1.json +++ b/test/util/data/txcreateoutpubkey1.json @@ -4,6 +4,7 @@ "version": 1, "size": 54, "vsize": 54, + "weight": 216, "locktime": 0, "vin": [ ], diff --git a/test/util/data/txcreateoutpubkey2.json b/test/util/data/txcreateoutpubkey2.json index 4ba5dcb282..c0ee181ede 100644 --- a/test/util/data/txcreateoutpubkey2.json +++ b/test/util/data/txcreateoutpubkey2.json @@ -4,6 +4,7 @@ "version": 1, "size": 41, "vsize": 41, + "weight": 164, "locktime": 0, "vin": [ ], diff --git a/test/util/data/txcreateoutpubkey3.json b/test/util/data/txcreateoutpubkey3.json index 0a5d489e15..4d904df3c8 100644 --- a/test/util/data/txcreateoutpubkey3.json +++ b/test/util/data/txcreateoutpubkey3.json @@ -4,6 +4,7 @@ "version": 1, "size": 42, "vsize": 42, + "weight": 168, "locktime": 0, "vin": [ ], diff --git a/test/util/data/txcreatescript1.json b/test/util/data/txcreatescript1.json index 5072452fed..af1c4c35e2 100644 --- a/test/util/data/txcreatescript1.json +++ b/test/util/data/txcreatescript1.json @@ -4,6 +4,7 @@ "version": 1, "size": 20, "vsize": 20, + "weight": 80, "locktime": 0, "vin": [ ], diff --git a/test/util/data/txcreatescript2.json b/test/util/data/txcreatescript2.json index 94b669ffb6..32dd644579 100644 --- a/test/util/data/txcreatescript2.json +++ b/test/util/data/txcreatescript2.json @@ -4,6 +4,7 @@ "version": 1, "size": 42, "vsize": 42, + "weight": 168, "locktime": 0, "vin": [ ], diff --git a/test/util/data/txcreatescript3.json b/test/util/data/txcreatescript3.json index 31b6459214..b9192d9a82 100644 --- a/test/util/data/txcreatescript3.json +++ b/test/util/data/txcreatescript3.json @@ -4,6 +4,7 @@ "version": 1, "size": 53, "vsize": 53, + "weight": 212, "locktime": 0, "vin": [ ], diff --git a/test/util/data/txcreatescript4.json b/test/util/data/txcreatescript4.json index eecdf858b7..2271ecfa0a 100644 --- a/test/util/data/txcreatescript4.json +++ b/test/util/data/txcreatescript4.json @@ -4,6 +4,7 @@ "version": 1, "size": 42, "vsize": 42, + "weight": 168, "locktime": 0, "vin": [ ], diff --git a/test/util/data/txcreatesignv1.json b/test/util/data/txcreatesignv1.json index 92a3f76a07..64e5137f4b 100644 --- a/test/util/data/txcreatesignv1.json +++ b/test/util/data/txcreatesignv1.json @@ -4,6 +4,7 @@ "version": 1, "size": 224, "vsize": 224, + "weight": 896, "locktime": 0, "vin": [ { diff --git a/test/util/rpcauth-test.py b/test/util/rpcauth-test.py new file mode 100755 index 0000000000..2456feb102 --- /dev/null +++ b/test/util/rpcauth-test.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python3 +# Copyright (c) 2015-2018 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 share/rpcauth/rpcauth.py +""" +import base64 +import configparser +import hmac +import importlib +import os +import sys +import unittest + +class TestRPCAuth(unittest.TestCase): + def setUp(self): + config = configparser.ConfigParser() + config_path = os.path.abspath( + os.path.join(os.sep, os.path.abspath(os.path.dirname(__file__)), + "../config.ini")) + with open(config_path) as config_file: + config.read_file(config_file) + sys.path.insert(0, os.path.dirname(config['environment']['RPCAUTH'])) + self.rpcauth = importlib.import_module('rpcauth') + + def test_generate_salt(self): + self.assertLessEqual(len(self.rpcauth.generate_salt()), 32) + self.assertGreaterEqual(len(self.rpcauth.generate_salt()), 16) + + def test_generate_password(self): + password = self.rpcauth.generate_password() + expected_password = base64.urlsafe_b64encode( + base64.urlsafe_b64decode(password)).decode('utf-8') + self.assertEqual(expected_password, password) + + def test_check_password_hmac(self): + salt = self.rpcauth.generate_salt() + password = self.rpcauth.generate_password() + password_hmac = self.rpcauth.password_to_hmac(salt, password) + + m = hmac.new(bytearray(salt, 'utf-8'), + bytearray(password, 'utf-8'), 'SHA256') + expected_password_hmac = m.hexdigest() + + self.assertEqual(expected_password_hmac, password_hmac) + +if __name__ == '__main__': + unittest.main() |