diff options
Diffstat (limited to 'test')
26 files changed, 548 insertions, 215 deletions
diff --git a/test/functional/feature_bip68_sequence.py b/test/functional/feature_bip68_sequence.py index f0bf09e172..fe6f9eade1 100755 --- a/test/functional/feature_bip68_sequence.py +++ b/test/functional/feature_bip68_sequence.py @@ -14,8 +14,8 @@ from test_framework.util import ( assert_equal, assert_greater_than, assert_raises_rpc_error, - get_bip9_status, satoshi_round, + softfork_active, ) SEQUENCE_LOCKTIME_DISABLE_FLAG = (1<<31) @@ -52,7 +52,7 @@ class BIP68Test(BitcoinTestFramework): self.log.info("Running test sequence-lock-unconfirmed-inputs") self.test_sequence_lock_unconfirmed_inputs() - self.log.info("Running test BIP68 not consensus before versionbits activation") + self.log.info("Running test BIP68 not consensus before activation") self.test_bip68_not_consensus() self.log.info("Activating BIP68 (and 112/113)") @@ -336,12 +336,12 @@ class BIP68Test(BitcoinTestFramework): self.nodes[0].invalidateblock(self.nodes[0].getblockhash(cur_height+1)) self.nodes[0].generate(10) - # Make sure that BIP68 isn't being used to validate blocks, prior to - # versionbits activation. If more blocks are mined prior to this test + # Make sure that BIP68 isn't being used to validate blocks prior to + # activation height. If more blocks are mined prior to this test # being run, then it's possible the test has activated the soft fork, and # this test should be moved to run earlier, or deleted. def test_bip68_not_consensus(self): - assert get_bip9_status(self.nodes[0], 'csv')['status'] != 'active' + assert not softfork_active(self.nodes[0], 'csv') txid = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 2) tx1 = FromHex(CTransaction(), self.nodes[0].getrawtransaction(txid)) @@ -391,9 +391,9 @@ class BIP68Test(BitcoinTestFramework): height = self.nodes[0].getblockcount() assert_greater_than(min_activation_height - height, 2) self.nodes[0].generate(min_activation_height - height - 2) - assert_equal(get_bip9_status(self.nodes[0], 'csv')['status'], "locked_in") + assert not softfork_active(self.nodes[0], 'csv') self.nodes[0].generate(1) - assert_equal(get_bip9_status(self.nodes[0], 'csv')['status'], "active") + assert softfork_active(self.nodes[0], 'csv') self.sync_blocks() # Use self.nodes[1] to test that version 2 transactions are standard. diff --git a/test/functional/feature_cltv.py b/test/functional/feature_cltv.py index af34f9f0db..e00219ca4a 100755 --- a/test/functional/feature_cltv.py +++ b/test/functional/feature_cltv.py @@ -69,14 +69,11 @@ class BIP65Test(BitcoinTestFramework): self.skip_if_no_wallet() def test_cltv_info(self, *, is_active): - assert_equal( - next(s for s in self.nodes[0].getblockchaininfo()['softforks'] if s['id'] == 'bip65'), + assert_equal(self.nodes[0].getblockchaininfo()['softforks']['bip65'], { - "id": "bip65", - "version": 4, - "reject": { - "status": is_active - } + "active": is_active, + "height": CLTV_HEIGHT, + "type": "buried", }, ) @@ -104,9 +101,9 @@ class BIP65Test(BitcoinTestFramework): block.hashMerkleRoot = block.calc_merkle_root() block.solve() - self.test_cltv_info(is_active=False) + self.test_cltv_info(is_active=False) # Not active as of current tip and next block does not need to obey rules self.nodes[0].p2p.send_and_ping(msg_block(block)) - self.test_cltv_info(is_active=False) # Not active as of current tip, but next block must obey rules + self.test_cltv_info(is_active=True) # Not active as of current tip, but next block must obey rules assert_equal(self.nodes[0].getbestblockhash(), block.hash) self.log.info("Test that blocks must now be at least version 4") @@ -155,7 +152,7 @@ class BIP65Test(BitcoinTestFramework): block.hashMerkleRoot = block.calc_merkle_root() block.solve() - self.test_cltv_info(is_active=False) # Not active as of current tip, but next block must obey rules + self.test_cltv_info(is_active=True) # Not active as of current tip, but next block must obey rules self.nodes[0].p2p.send_and_ping(msg_block(block)) self.test_cltv_info(is_active=True) # Active as of current tip assert_equal(int(self.nodes[0].getbestblockhash(), 16), block.sha256) diff --git a/test/functional/feature_config_args.py b/test/functional/feature_config_args.py index 70a824b863..b997c76025 100755 --- a/test/functional/feature_config_args.py +++ b/test/functional/feature_config_args.py @@ -109,17 +109,15 @@ class ConfArgsTest(BitcoinTestFramework): f.write("datadir=" + new_data_dir + "\n") f.write(conf_file_contents) - # Temporarily disabled, because this test would access the user's home dir (~/.bitcoin) - #self.nodes[0].assert_start_raises_init_error(['-conf=' + conf_file], 'Error reading configuration file: specified data directory "' + new_data_dir + '" does not exist.') + self.nodes[0].assert_start_raises_init_error(['-conf=' + conf_file], 'Error: Error reading configuration file: specified data directory "' + new_data_dir + '" does not exist.') # Create the directory and ensure the config file now works os.mkdir(new_data_dir) - # Temporarily disabled, because this test would access the user's home dir (~/.bitcoin) - #self.start_node(0, ['-conf='+conf_file, '-wallet=w1']) - #self.stop_node(0) - #assert os.path.exists(os.path.join(new_data_dir, 'regtest', 'blocks')) - #if self.is_wallet_compiled(): - #assert os.path.exists(os.path.join(new_data_dir, 'regtest', 'wallets', 'w1')) + self.start_node(0, ['-conf='+conf_file, '-wallet=w1']) + self.stop_node(0) + assert os.path.exists(os.path.join(new_data_dir, 'regtest', 'blocks')) + if self.is_wallet_compiled(): + assert os.path.exists(os.path.join(new_data_dir, 'regtest', 'wallets', 'w1')) # Ensure command line argument overrides datadir in conf os.mkdir(new_data_dir_2) diff --git a/test/functional/feature_csv_activation.py b/test/functional/feature_csv_activation.py index 887e9dafa3..6bd321992a 100755 --- a/test/functional/feature_csv_activation.py +++ b/test/functional/feature_csv_activation.py @@ -2,23 +2,17 @@ # Copyright (c) 2015-2019 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 activation of the first version bits soft fork. +"""Test CSV soft fork activation. This soft fork will activate the following BIPS: BIP 68 - nSequence relative lock times BIP 112 - CHECKSEQUENCEVERIFY BIP 113 - MedianTimePast semantics for nLockTime -regtest lock-in with 108/144 block signalling -activation after a further 144 blocks - mine 82 blocks whose coinbases will be used to generate inputs for our tests -mine 61 blocks to transition from DEFINED to STARTED -mine 144 blocks only 100 of which are signaling readiness in order to fail to change state this period -mine 144 blocks with 108 signaling and verify STARTED->LOCKED_IN -mine 140 blocks and seed block chain with the 82 inputs will use for our tests at height 572 -mine 3 blocks and verify still at LOCKED_IN and test that enforcement has not triggered -mine 1 block and test that enforcement has triggered (which triggers ACTIVE) +mine 345 blocks and seed block chain with the 82 inputs will use for our tests at height 427 +mine 2 blocks and verify soft fork not yet activated +mine 1 block and test that soft fork is activated (rules enforced for next block) Test BIP 113 is enforced Mine 4 blocks so next height is 580 and test BIP 68 is enforced for time and height Mine 1 block so next height is 581 and test BIP 68 now passes time but not height @@ -58,11 +52,12 @@ from test_framework.script import ( from test_framework.test_framework import BitcoinTestFramework from test_framework.util import ( assert_equal, - get_bip9_status, hex_str_to_bytes, + softfork_active, ) BASE_RELATIVE_LOCKTIME = 10 +CSV_ACTIVATION_HEIGHT = 432 SEQ_DISABLE_FLAG = 1 << 31 SEQ_RANDOM_HIGH_BIT = 1 << 25 SEQ_TYPE_FLAG = 1 << 22 @@ -148,20 +143,19 @@ class BIP68_112_113Test(BitcoinTestFramework): def skip_test_if_missing_module(self): self.skip_if_no_wallet() - def generate_blocks(self, number, version, test_blocks=None): - if test_blocks is None: - test_blocks = [] + def generate_blocks(self, number): + test_blocks = [] for i in range(number): - block = self.create_test_block([], version) + block = self.create_test_block([]) test_blocks.append(block) self.last_block_time += 600 self.tip = block.sha256 self.tipheight += 1 return test_blocks - def create_test_block(self, txs, version=536870912): + def create_test_block(self, txs): block = create_block(self.tip, create_coinbase(self.tipheight + 1), self.last_block_time + 600) - block.nVersion = version + block.nVersion = 4 block.vtx.extend(txs) block.hashMerkleRoot = block.calc_merkle_root() block.rehash() @@ -187,45 +181,14 @@ class BIP68_112_113Test(BitcoinTestFramework): self.tip = int(self.nodes[0].getbestblockhash(), 16) self.nodeaddress = self.nodes[0].getnewaddress() - self.log.info("Test that the csv softfork is DEFINED") - assert_equal(get_bip9_status(self.nodes[0], 'csv')['status'], 'defined') - test_blocks = self.generate_blocks(61, 4) - self.send_blocks(test_blocks) - - self.log.info("Advance from DEFINED to STARTED, height = 143") - assert_equal(get_bip9_status(self.nodes[0], 'csv')['status'], 'started') - - self.log.info("Fail to achieve LOCKED_IN") - # 100 out of 144 signal bit 0. Use a variety of bits to simulate multiple parallel softforks - - test_blocks = self.generate_blocks(50, 536870913) # 0x20000001 (signalling ready) - test_blocks = self.generate_blocks(20, 4, test_blocks) # 0x00000004 (signalling not) - test_blocks = self.generate_blocks(50, 536871169, test_blocks) # 0x20000101 (signalling ready) - test_blocks = self.generate_blocks(24, 536936448, test_blocks) # 0x20010000 (signalling not) - self.send_blocks(test_blocks) - - self.log.info("Failed to advance past STARTED, height = 287") - assert_equal(get_bip9_status(self.nodes[0], 'csv')['status'], 'started') - - self.log.info("Generate blocks to achieve LOCK-IN") - # 108 out of 144 signal bit 0 to achieve lock-in - # using a variety of bits to simulate multiple parallel softforks - test_blocks = self.generate_blocks(58, 536870913) # 0x20000001 (signalling ready) - test_blocks = self.generate_blocks(26, 4, test_blocks) # 0x00000004 (signalling not) - test_blocks = self.generate_blocks(50, 536871169, test_blocks) # 0x20000101 (signalling ready) - test_blocks = self.generate_blocks(10, 536936448, test_blocks) # 0x20010000 (signalling not) - self.send_blocks(test_blocks) - - self.log.info("Advanced from STARTED to LOCKED_IN, height = 431") - assert_equal(get_bip9_status(self.nodes[0], 'csv')['status'], 'locked_in') - - # Generate 140 more version 4 blocks - test_blocks = self.generate_blocks(140, 4) + # Activation height is hardcoded + test_blocks = self.generate_blocks(345) self.send_blocks(test_blocks) + assert not softfork_active(self.nodes[0], 'csv') - # Inputs at height = 572 + # Inputs at height = 431 # - # Put inputs for all tests in the chain at height 572 (tip now = 571) (time increases by 600s per block) + # Put inputs for all tests in the chain at height 431 (tip now = 430) (time increases by 600s per block) # Note we reuse inputs for v1 and v2 txs so must test these separately # 16 normal inputs bip68inputs = [] @@ -255,7 +218,7 @@ class BIP68_112_113Test(BitcoinTestFramework): bip113input = send_generic_input_tx(self.nodes[0], self.coinbase_blocks, self.nodeaddress) self.nodes[0].setmocktime(self.last_block_time + 600) - inputblockhash = self.nodes[0].generate(1)[0] # 1 block generated for inputs to be in chain at height 572 + inputblockhash = self.nodes[0].generate(1)[0] # 1 block generated for inputs to be in chain at height 431 self.nodes[0].setmocktime(0) self.tip = int(inputblockhash, 16) self.tipheight += 1 @@ -263,11 +226,12 @@ class BIP68_112_113Test(BitcoinTestFramework): assert_equal(len(self.nodes[0].getblock(inputblockhash, True)["tx"]), 82 + 1) # 2 more version 4 blocks - test_blocks = self.generate_blocks(2, 4) + test_blocks = self.generate_blocks(2) self.send_blocks(test_blocks) - self.log.info("Not yet advanced to ACTIVE, height = 574 (will activate for block 576, not 575)") - assert_equal(get_bip9_status(self.nodes[0], 'csv')['status'], 'locked_in') + assert_equal(self.tipheight, CSV_ACTIVATION_HEIGHT - 2) + self.log.info("Height = {}, CSV not yet active (will activate for block {}, not {})".format(self.tipheight, CSV_ACTIVATION_HEIGHT, CSV_ACTIVATION_HEIGHT - 1)) + assert not softfork_active(self.nodes[0], 'csv') # Test both version 1 and version 2 transactions for all tests # BIP113 test transaction will be modified before each use to put in appropriate block time @@ -340,10 +304,11 @@ class BIP68_112_113Test(BitcoinTestFramework): self.send_blocks([self.create_test_block(success_txs)]) self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash()) - # 1 more version 4 block to get us to height 575 so the fork should now be active for the next block - test_blocks = self.generate_blocks(1, 4) + # 1 more version 4 block to get us to height 432 so the fork should now be active for the next block + assert not softfork_active(self.nodes[0], 'csv') + test_blocks = self.generate_blocks(1) self.send_blocks(test_blocks) - assert_equal(get_bip9_status(self.nodes[0], 'csv')['status'], 'active') + assert softfork_active(self.nodes[0], 'csv') self.log.info("Post-Soft Fork Tests.") @@ -364,8 +329,8 @@ class BIP68_112_113Test(BitcoinTestFramework): self.send_blocks([self.create_test_block([bip113tx])]) self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash()) - # Next block height = 580 after 4 blocks of random version - test_blocks = self.generate_blocks(4, 1234) + # Next block height = 437 after 4 blocks of random version + test_blocks = self.generate_blocks(4) self.send_blocks(test_blocks) self.log.info("BIP 68 tests") @@ -392,8 +357,8 @@ class BIP68_112_113Test(BitcoinTestFramework): for tx in bip68heighttxs: self.send_blocks([self.create_test_block([tx])], success=False) - # Advance one block to 581 - test_blocks = self.generate_blocks(1, 1234) + # Advance one block to 438 + test_blocks = self.generate_blocks(1) self.send_blocks(test_blocks) # Height txs should fail and time txs should now pass 9 * 600 > 10 * 512 @@ -403,8 +368,8 @@ class BIP68_112_113Test(BitcoinTestFramework): for tx in bip68heighttxs: self.send_blocks([self.create_test_block([tx])], success=False) - # Advance one block to 582 - test_blocks = self.generate_blocks(1, 1234) + # Advance one block to 439 + test_blocks = self.generate_blocks(1) self.send_blocks(test_blocks) # All BIP 68 txs should pass diff --git a/test/functional/feature_dbcrash.py b/test/functional/feature_dbcrash.py index ff014de0e0..b86f6af4ca 100755 --- a/test/functional/feature_dbcrash.py +++ b/test/functional/feature_dbcrash.py @@ -58,7 +58,7 @@ class ChainstateWriteCrashTest(BitcoinTestFramework): self.base_args = ["-limitdescendantsize=0", "-maxmempool=0", "-rpcservertimeout=900", "-dbbatchsize=200000"] # Set different crash ratios and cache sizes. Note that not all of - # -dbcache goes to pcoinsTip. + # -dbcache goes to the in-memory coins cache. self.node0_args = ["-dbcrashratio=8", "-dbcache=4"] + self.base_args self.node1_args = ["-dbcrashratio=16", "-dbcache=8"] + self.base_args self.node2_args = ["-dbcrashratio=24", "-dbcache=16"] + self.base_args diff --git a/test/functional/feature_dersig.py b/test/functional/feature_dersig.py index 067e3be1f4..1bd9586364 100755 --- a/test/functional/feature_dersig.py +++ b/test/functional/feature_dersig.py @@ -52,14 +52,11 @@ class BIP66Test(BitcoinTestFramework): self.skip_if_no_wallet() def test_dersig_info(self, *, is_active): - assert_equal( - next(s for s in self.nodes[0].getblockchaininfo()['softforks'] if s['id'] == 'bip66'), + assert_equal(self.nodes[0].getblockchaininfo()['softforks']['bip66'], { - "id": "bip66", - "version": 3, - "reject": { - "status": is_active - } + "active": is_active, + "height": DERSIG_HEIGHT, + "type": "buried", }, ) @@ -88,9 +85,9 @@ class BIP66Test(BitcoinTestFramework): block.rehash() block.solve() - self.test_dersig_info(is_active=False) + self.test_dersig_info(is_active=False) # Not active as of current tip and next block does not need to obey rules self.nodes[0].p2p.send_and_ping(msg_block(block)) - self.test_dersig_info(is_active=False) # Not active as of current tip, but next block must obey rules + self.test_dersig_info(is_active=True) # Not active as of current tip, but next block must obey rules assert_equal(self.nodes[0].getbestblockhash(), block.hash) self.log.info("Test that blocks must now be at least version 3") @@ -144,7 +141,7 @@ class BIP66Test(BitcoinTestFramework): block.rehash() block.solve() - self.test_dersig_info(is_active=False) # Not active as of current tip, but next block must obey rules + self.test_dersig_info(is_active=True) # Not active as of current tip, but next block must obey rules self.nodes[0].p2p.send_and_ping(msg_block(block)) self.test_dersig_info(is_active=True) # Active as of current tip assert_equal(int(self.nodes[0].getbestblockhash(), 16), block.sha256) diff --git a/test/functional/feature_nulldummy.py b/test/functional/feature_nulldummy.py index 60a703c48f..250dee1528 100755 --- a/test/functional/feature_nulldummy.py +++ b/test/functional/feature_nulldummy.py @@ -41,7 +41,7 @@ class NULLDUMMYTest(BitcoinTestFramework): self.setup_clean_chain = True # This script tests NULLDUMMY activation, which is part of the 'segwit' deployment, so we go through # normal segwit activation here (and don't use the default always-on behaviour). - self.extra_args = [['-whitelist=127.0.0.1', '-vbparams=segwit:0:999999999999', '-addresstype=legacy']] + self.extra_args = [['-whitelist=127.0.0.1', '-segwitheight=432', '-addresstype=legacy']] def skip_test_if_missing_module(self): self.skip_if_no_wallet() diff --git a/test/functional/feature_segwit.py b/test/functional/feature_segwit.py index a71d4071d5..b9db618575 100755 --- a/test/functional/feature_segwit.py +++ b/test/functional/feature_segwit.py @@ -55,20 +55,20 @@ class SegWitTest(BitcoinTestFramework): [ "-acceptnonstdtxn=1", "-rpcserialversion=0", - "-vbparams=segwit:0:999999999999", + "-segwitheight=432", "-addresstype=legacy", ], [ "-acceptnonstdtxn=1", "-blockversion=4", "-rpcserialversion=1", - "-vbparams=segwit:0:999999999999", + "-segwitheight=432", "-addresstype=legacy", ], [ "-acceptnonstdtxn=1", "-blockversion=536870915", - "-vbparams=segwit:0:999999999999", + "-segwitheight=432", "-addresstype=legacy", ], ] @@ -226,6 +226,16 @@ class SegWitTest(BitcoinTestFramework): assert tx.wit.is_null() # This should not be a segwit input assert txid1 in self.nodes[0].getrawmempool() + tx1_hex = self.nodes[0].gettransaction(txid1)['hex'] + tx1 = FromHex(CTransaction(), tx1_hex) + + # Check that wtxid is properly reported in mempool entry (txid1) + assert_equal(int(self.nodes[0].getmempoolentry(txid1)["wtxid"], 16), tx1.calc_sha256(True)) + + # Check that weight and vsize are properly reported in mempool entry (txid1) + assert_equal(self.nodes[0].getmempoolentry(txid1)["vsize"], (self.nodes[0].getmempoolentry(txid1)["weight"] + 3) // 4) + assert_equal(self.nodes[0].getmempoolentry(txid1)["weight"], len(tx1.serialize_without_witness())*3 + len(tx1.serialize_with_witness())) + # Now create tx2, which will spend from txid1. tx = CTransaction() tx.vin.append(CTxIn(COutPoint(int(txid1, 16), 0), b'')) @@ -235,6 +245,13 @@ class SegWitTest(BitcoinTestFramework): tx = FromHex(CTransaction(), tx2_hex) assert not tx.wit.is_null() + # Check that wtxid is properly reported in mempool entry (txid2) + assert_equal(int(self.nodes[0].getmempoolentry(txid2)["wtxid"], 16), tx.calc_sha256(True)) + + # Check that weight and vsize are properly reported in mempool entry (txid2) + assert_equal(self.nodes[0].getmempoolentry(txid2)["vsize"], (self.nodes[0].getmempoolentry(txid2)["weight"] + 3) // 4) + assert_equal(self.nodes[0].getmempoolentry(txid2)["weight"], len(tx.serialize_without_witness())*3 + len(tx.serialize_with_witness())) + # Now create tx3, which will spend from txid2 tx = CTransaction() tx.vin.append(CTxIn(COutPoint(int(txid2, 16), 0), b"")) @@ -251,9 +268,13 @@ class SegWitTest(BitcoinTestFramework): assert txid2 in template_txids assert txid3 in template_txids - # Check that wtxid is properly reported in mempool entry + # Check that wtxid is properly reported in mempool entry (txid3) assert_equal(int(self.nodes[0].getmempoolentry(txid3)["wtxid"], 16), tx.calc_sha256(True)) + # Check that weight and vsize are properly reported in mempool entry (txid3) + assert_equal(self.nodes[0].getmempoolentry(txid3)["vsize"], (self.nodes[0].getmempoolentry(txid3)["weight"] + 3) // 4) + assert_equal(self.nodes[0].getmempoolentry(txid3)["weight"], len(tx.serialize_without_witness())*3 + len(tx.serialize_with_witness())) + # Mine a block to clear the gbt cache again. self.nodes[0].generate(1) diff --git a/test/functional/interface_zmq.py b/test/functional/interface_zmq.py index 8e58c85c15..1ba781c539 100755 --- a/test/functional/interface_zmq.py +++ b/test/functional/interface_zmq.py @@ -7,15 +7,15 @@ import struct from test_framework.address import ADDRESS_BCRT1_UNSPENDABLE from test_framework.test_framework import BitcoinTestFramework -from test_framework.messages import CTransaction -from test_framework.util import ( - assert_equal, - hash256, -) +from test_framework.messages import CTransaction, hash256 +from test_framework.util import assert_equal from io import BytesIO ADDRESS = "tcp://127.0.0.1:28332" +def hash256_reversed(byte_str): + return hash256(byte_str)[::-1] + class ZMQSubscriber: def __init__(self, socket, topic): self.sequence = 0 @@ -103,7 +103,7 @@ class ZMQTest (BitcoinTestFramework): # Should receive the generated raw block. block = self.rawblock.receive() - assert_equal(genhashes[x], hash256(block[:80]).hex()) + assert_equal(genhashes[x], hash256_reversed(block[:80]).hex()) if self.is_wallet_compiled(): self.log.info("Wait for tx from second node") @@ -116,7 +116,7 @@ class ZMQTest (BitcoinTestFramework): # Should receive the broadcasted raw transaction. hex = self.rawtx.receive() - assert_equal(payment_txid, hash256(hex).hex()) + assert_equal(payment_txid, hash256_reversed(hex).hex()) self.log.info("Test the getzmqnotifications RPC") diff --git a/test/functional/p2p_compactblocks.py b/test/functional/p2p_compactblocks.py index eb3336bd3b..7905cf5018 100755 --- a/test/functional/p2p_compactblocks.py +++ b/test/functional/p2p_compactblocks.py @@ -14,7 +14,7 @@ from test_framework.messages import BlockTransactions, BlockTransactionsRequest, from test_framework.mininode import mininode_lock, P2PInterface from test_framework.script import CScript, OP_TRUE, OP_DROP from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import assert_equal, get_bip9_status, wait_until +from test_framework.util import assert_equal, wait_until, softfork_active # TestP2PConn: A peer we use to send messages to bitcoind, and store responses. class TestP2PConn(P2PInterface): @@ -803,7 +803,7 @@ class CompactBlocksTest(BitcoinTestFramework): # We will need UTXOs to construct transactions in later tests. self.make_utxos() - assert_equal(get_bip9_status(self.nodes[0], "segwit")["status"], 'active') + assert softfork_active(self.nodes[0], "segwit") self.log.info("Testing SENDCMPCT p2p message... ") self.test_sendcmpct(self.segwit_node, old_node=self.old_node) diff --git a/test/functional/p2p_permissions.py b/test/functional/p2p_permissions.py new file mode 100755 index 0000000000..40b28d7533 --- /dev/null +++ b/test/functional/p2p_permissions.py @@ -0,0 +1,103 @@ +#!/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 p2p permission message. + +Test that permissions are correctly calculated and applied +""" + +from test_framework.test_node import ErrorMatch +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import ( + assert_equal, + connect_nodes, + p2p_port, +) + +class P2PPermissionsTests(BitcoinTestFramework): + def set_test_params(self): + self.num_nodes = 2 + self.setup_clean_chain = True + self.extra_args = [[],[]] + + def run_test(self): + self.checkpermission( + # default permissions (no specific permissions) + ["-whitelist=127.0.0.1"], + ["relay", "noban", "mempool"], + True) + + self.checkpermission( + # relay permission removed (no specific permissions) + ["-whitelist=127.0.0.1", "-whitelistrelay=0"], + ["noban", "mempool"], + True) + + self.checkpermission( + # forcerelay and relay permission added + # Legacy parameter interaction which set whitelistrelay to true + # if whitelistforcerelay is true + ["-whitelist=127.0.0.1", "-whitelistforcerelay"], + ["forcerelay", "relay", "noban", "mempool"], + True) + + # Let's make sure permissions are merged correctly + # For this, we need to use whitebind instead of bind + # by modifying the configuration file. + ip_port = "127.0.0.1:{}".format(p2p_port(1)) + self.replaceinconfig(1, "bind=127.0.0.1", "whitebind=bloomfilter,forcerelay@" + ip_port) + self.checkpermission( + ["-whitelist=noban@127.0.0.1" ], + # Check parameter interaction forcerelay should activate relay + ["noban", "bloomfilter", "forcerelay", "relay" ], + False) + self.replaceinconfig(1, "whitebind=bloomfilter,forcerelay@" + ip_port, "bind=127.0.0.1") + + self.checkpermission( + # legacy whitelistrelay should be ignored + ["-whitelist=noban,mempool@127.0.0.1", "-whitelistrelay"], + ["noban", "mempool"], + False) + + self.checkpermission( + # legacy whitelistforcerelay should be ignored + ["-whitelist=noban,mempool@127.0.0.1", "-whitelistforcerelay"], + ["noban", "mempool"], + False) + + self.checkpermission( + # missing mempool permission to be considered legacy whitelisted + ["-whitelist=noban@127.0.0.1"], + ["noban"], + False) + + self.checkpermission( + # all permission added + ["-whitelist=all@127.0.0.1"], + ["forcerelay", "noban", "mempool", "bloomfilter", "relay"], + False) + + self.stop_node(1) + self.nodes[1].assert_start_raises_init_error(["-whitelist=oopsie@127.0.0.1"], "Invalid P2P permission", match=ErrorMatch.PARTIAL_REGEX) + self.nodes[1].assert_start_raises_init_error(["-whitelist=noban@127.0.0.1:230"], "Invalid netmask specified in", match=ErrorMatch.PARTIAL_REGEX) + self.nodes[1].assert_start_raises_init_error(["-whitebind=noban@127.0.0.1/10"], "Cannot resolve -whitebind address", match=ErrorMatch.PARTIAL_REGEX) + + def checkpermission(self, args, expectedPermissions, whitelisted): + self.restart_node(1, args) + connect_nodes(self.nodes[0], 1) + peerinfo = self.nodes[1].getpeerinfo()[0] + assert_equal(peerinfo['whitelisted'], whitelisted) + assert_equal(len(expectedPermissions), len(peerinfo['permissions'])) + for p in expectedPermissions: + if not p in peerinfo['permissions']: + raise AssertionError("Expected permissions %r is not granted." % p) + + def replaceinconfig(self, nodeid, old, new): + with open(self.nodes[nodeid].bitcoinconf, encoding="utf8") as f: + newText=f.read().replace(old, new) + with open(self.nodes[nodeid].bitcoinconf, 'w', encoding="utf8") as f: + f.write(newText) + +if __name__ == '__main__': + P2PPermissionsTests().main() diff --git a/test/functional/p2p_segwit.py b/test/functional/p2p_segwit.py index dca71aec43..98f6b1d71d 100755 --- a/test/functional/p2p_segwit.py +++ b/test/functional/p2p_segwit.py @@ -76,7 +76,7 @@ from test_framework.util import ( assert_equal, connect_nodes, disconnect_nodes, - get_bip9_status, + softfork_active, hex_str_to_bytes, assert_raises_rpc_error, ) @@ -88,6 +88,8 @@ VB_TOP_BITS = 0x20000000 MAX_SIGOP_COST = 80000 +SEGWIT_HEIGHT = 120 + class UTXO(): """Used to keep track of anyone-can-spend outputs that we can use in the tests.""" def __init__(self, sha256, n, value): @@ -185,9 +187,9 @@ class SegWitTest(BitcoinTestFramework): self.num_nodes = 3 # This test tests SegWit both pre and post-activation, so use the normal BIP9 activation. self.extra_args = [ - ["-whitelist=127.0.0.1", "-acceptnonstdtxn=1", "-vbparams=segwit:0:999999999999"], - ["-whitelist=127.0.0.1", "-acceptnonstdtxn=0", "-vbparams=segwit:0:999999999999"], - ["-whitelist=127.0.0.1", "-acceptnonstdtxn=1", "-vbparams=segwit:0:0"], + ["-whitelist=127.0.0.1", "-acceptnonstdtxn=1", "-segwitheight={}".format(SEGWIT_HEIGHT)], + ["-whitelist=127.0.0.1", "-acceptnonstdtxn=0", "-segwitheight={}".format(SEGWIT_HEIGHT)], + ["-whitelist=127.0.0.1", "-acceptnonstdtxn=1", "-segwitheight=-1"] ] def skip_test_if_missing_module(self): @@ -231,26 +233,18 @@ class SegWitTest(BitcoinTestFramework): # Keep a place to store utxo's that can be used in later tests self.utxo = [] - # Segwit status 'defined' - self.segwit_status = 'defined' + self.log.info("Starting tests before segwit activation") + self.segwit_active = False self.test_non_witness_transaction() - self.test_unnecessary_witness_before_segwit_activation() self.test_v0_outputs_arent_spendable() self.test_block_relay() - self.advance_to_segwit_started() - - # Segwit status 'started' - self.test_getblocktemplate_before_lockin() - self.advance_to_segwit_lockin() - - # Segwit status 'locked_in' - self.test_unnecessary_witness_before_segwit_activation() self.test_witness_tx_relay_before_segwit_activation() - self.test_block_relay() self.test_standardness_v0() + + self.log.info("Advancing to segwit activation") self.advance_to_segwit_active() # Segwit status 'active' @@ -282,15 +276,15 @@ class SegWitTest(BitcoinTestFramework): def subtest(func): # noqa: N805 """Wraps the subtests for logging and state assertions.""" def func_wrapper(self, *args, **kwargs): - self.log.info("Subtest: {} (Segwit status = {})".format(func.__name__, self.segwit_status)) + self.log.info("Subtest: {} (Segwit active = {})".format(func.__name__, self.segwit_active)) # Assert segwit status is as expected - assert_equal(get_bip9_status(self.nodes[0], 'segwit')['status'], self.segwit_status) + assert_equal(softfork_active(self.nodes[0], 'segwit'), self.segwit_active) func(self, *args, **kwargs) # Each subtest should leave some utxos for the next subtest assert self.utxo self.sync_blocks() # Assert segwit status is as expected at end of subtest - assert_equal(get_bip9_status(self.nodes[0], 'segwit')['status'], self.segwit_status) + assert_equal(softfork_active(self.nodes[0], 'segwit'), self.segwit_active) return func_wrapper @@ -392,7 +386,7 @@ class SegWitTest(BitcoinTestFramework): # Check that we can getdata for witness blocks or regular blocks, # and the right thing happens. - if self.segwit_status != 'active': + if not self.segwit_active: # Before activation, we should be able to request old blocks with # or without witness, and they should be the same. chain_height = self.nodes[0].getblockcount() @@ -536,32 +530,18 @@ class SegWitTest(BitcoinTestFramework): self.utxo.append(UTXO(txid, 2, value)) @subtest - def advance_to_segwit_started(self): - """Mine enough blocks for segwit's vb state to be 'started'.""" - height = self.nodes[0].getblockcount() - # Will need to rewrite the tests here if we are past the first period - assert height < VB_PERIOD - 1 - # Advance to end of period, status should now be 'started' - self.nodes[0].generate(VB_PERIOD - height - 1) - assert_equal(get_bip9_status(self.nodes[0], 'segwit')['status'], 'started') - self.segwit_status = 'started' - - @subtest def test_getblocktemplate_before_lockin(self): txid = int(self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 1), 16) for node in [self.nodes[0], self.nodes[2]]: gbt_results = node.getblocktemplate({"rules": ["segwit"]}) - block_version = gbt_results['version'] if node == self.nodes[2]: # If this is a non-segwit node, we should not get a witness - # commitment, nor a version bit signalling segwit. - assert_equal(block_version & (1 << VB_WITNESS_BIT), 0) + # commitment. assert 'default_witness_commitment' not in gbt_results else: - # For segwit-aware nodes, check the version bit and the witness - # commitment are correct. - assert block_version & (1 << VB_WITNESS_BIT) != 0 + # For segwit-aware nodes, check the witness + # commitment is correct. assert 'default_witness_commitment' in gbt_results witness_commitment = gbt_results['default_witness_commitment'] @@ -571,18 +551,9 @@ class SegWitTest(BitcoinTestFramework): script = get_witness_script(witness_root, 0) assert_equal(witness_commitment, script.hex()) - @subtest - def advance_to_segwit_lockin(self): - """Mine enough blocks to lock in segwit, but don't activate.""" - height = self.nodes[0].getblockcount() - # Advance to end of period, and verify lock-in happens at the end - self.nodes[0].generate(VB_PERIOD - 1) - height = self.nodes[0].getblockcount() - assert (height % VB_PERIOD) == VB_PERIOD - 2 - assert_equal(get_bip9_status(self.nodes[0], 'segwit')['status'], 'started') + # Clear out the mempool self.nodes[0].generate(1) - assert_equal(get_bip9_status(self.nodes[0], 'segwit')['status'], 'locked_in') - self.segwit_status = 'locked_in' + self.sync_blocks() @subtest def test_witness_tx_relay_before_segwit_activation(self): @@ -686,7 +657,7 @@ class SegWitTest(BitcoinTestFramework): tx3.wit.vtxinwit.append(CTxInWitness()) tx3.wit.vtxinwit[0].scriptWitness.stack = [witness_program] tx3.rehash() - if self.segwit_status != 'active': + if not self.segwit_active: # Just check mempool acceptance, but don't add the transaction to the mempool, since witness is disallowed # in blocks and the tx is impossible to mine right now. assert_equal(self.nodes[0].testmempoolaccept([tx3.serialize_with_witness().hex()]), [{'txid': tx3.hash, 'allowed': True}]) @@ -707,12 +678,13 @@ class SegWitTest(BitcoinTestFramework): @subtest def advance_to_segwit_active(self): """Mine enough blocks to activate segwit.""" + assert not softfork_active(self.nodes[0], 'segwit') height = self.nodes[0].getblockcount() - self.nodes[0].generate(VB_PERIOD - (height % VB_PERIOD) - 2) - assert_equal(get_bip9_status(self.nodes[0], 'segwit')['status'], 'locked_in') + self.nodes[0].generate(SEGWIT_HEIGHT - height - 2) + assert not softfork_active(self.nodes[0], 'segwit') self.nodes[0].generate(1) - assert_equal(get_bip9_status(self.nodes[0], 'segwit')['status'], 'active') - self.segwit_status = 'active' + assert softfork_active(self.nodes[0], 'segwit') + self.segwit_active = True @subtest def test_p2sh_witness(self): @@ -1924,13 +1896,13 @@ class SegWitTest(BitcoinTestFramework): # Restart with the new binary self.stop_node(2) - self.start_node(2, extra_args=["-vbparams=segwit:0:999999999999"]) + self.start_node(2, extra_args=["-segwitheight={}".format(SEGWIT_HEIGHT)]) connect_nodes(self.nodes[0], 2) self.sync_blocks() # Make sure that this peer thinks segwit has activated. - assert get_bip9_status(self.nodes[2], 'segwit')['status'] == "active" + assert softfork_active(self.nodes[2], 'segwit') # Make sure this peer's blocks match those of node0. height = self.nodes[2].getblockcount() diff --git a/test/functional/rpc_blockchain.py b/test/functional/rpc_blockchain.py index facb05b54c..6c30e05084 100755 --- a/test/functional/rpc_blockchain.py +++ b/test/functional/rpc_blockchain.py @@ -78,7 +78,6 @@ class BlockchainTest(BitcoinTestFramework): keys = [ 'bestblockhash', - 'bip9_softforks', 'blocks', 'chain', 'chainwork', @@ -124,6 +123,31 @@ class BlockchainTest(BitcoinTestFramework): assert_equal(res['prune_target_size'], 576716800) assert_greater_than(res['size_on_disk'], 0) + assert_equal(res['softforks'], { + 'bip34': {'type': 'buried', 'active': False, 'height': 500}, + 'bip66': {'type': 'buried', 'active': False, 'height': 1251}, + 'bip65': {'type': 'buried', 'active': False, 'height': 1351}, + 'csv': {'type': 'buried', 'active': False, 'height': 432}, + 'segwit': {'type': 'buried', 'active': True, 'height': 0}, + 'testdummy': { + 'type': 'bip9', + 'bip9': { + 'status': 'started', + 'bit': 28, + 'startTime': 0, + 'timeout': 0x7fffffffffffffff, # testdummy does not have a timeout so is set to the max int64 value + 'since': 144, + 'statistics': { + 'period': 144, + 'threshold': 108, + 'elapsed': 57, + 'count': 57, + 'possible': True, + }, + }, + 'active': False} + }) + def _test_getchaintxstats(self): self.log.info("Test getchaintxstats") diff --git a/test/functional/rpc_deriveaddresses.py b/test/functional/rpc_deriveaddresses.py index 1984694692..42128d5767 100755 --- a/test/functional/rpc_deriveaddresses.py +++ b/test/functional/rpc_deriveaddresses.py @@ -13,14 +13,14 @@ class DeriveaddressesTest(BitcoinTestFramework): self.supports_cli = 1 def run_test(self): - assert_raises_rpc_error(-5, "Invalid descriptor", self.nodes[0].deriveaddresses, "a") + assert_raises_rpc_error(-5, "Missing checksum", self.nodes[0].deriveaddresses, "a") descriptor = "wpkh(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/1/1/0)#t6wfjs64" address = "bcrt1qjqmxmkpmxt80xz4y3746zgt0q3u3ferr34acd5" assert_equal(self.nodes[0].deriveaddresses(descriptor), [address]) descriptor = descriptor[:-9] - assert_raises_rpc_error(-5, "Invalid descriptor", self.nodes[0].deriveaddresses, descriptor) + assert_raises_rpc_error(-5, "Missing checksum", self.nodes[0].deriveaddresses, descriptor) descriptor_pubkey = "wpkh(tpubD6NzVbkrYhZ4WaWSyoBvQwbpLkojyoTZPRsgXELWz3Popb3qkjcJyJUGLnL4qHHoQvao8ESaAstxYSnhyswJ76uZPStJRJCTKvosUCJZL5B/1/1/0)#s9ga3alw" address = "bcrt1qjqmxmkpmxt80xz4y3746zgt0q3u3ferr34acd5" diff --git a/test/functional/rpc_setban.py b/test/functional/rpc_setban.py new file mode 100755 index 0000000000..a1a8196557 --- /dev/null +++ b/test/functional/rpc_setban.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python3 +# Copyright (c) 2015-2019 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. +"""Test the setban rpc call.""" + +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import ( + connect_nodes, + p2p_port +) + +class SetBanTests(BitcoinTestFramework): + def set_test_params(self): + self.num_nodes = 2 + self.setup_clean_chain = True + self.extra_args = [[],[]] + + def run_test(self): + # Node 0 connects to Node 1, check that the noban permission is not granted + connect_nodes(self.nodes[0], 1) + peerinfo = self.nodes[1].getpeerinfo()[0] + assert(not 'noban' in peerinfo['permissions']) + + # Node 0 get banned by Node 1 + self.nodes[1].setban("127.0.0.1", "add") + + # Node 0 should not be able to reconnect + with self.nodes[1].assert_debug_log(expected_msgs=['dropped (banned)\n']): + self.restart_node(1, []) + self.nodes[0].addnode("127.0.0.1:" + str(p2p_port(1)), "onetry") + + # However, node 0 should be able to reconnect if it has noban permission + self.restart_node(1, ['-whitelist=127.0.0.1']) + connect_nodes(self.nodes[0], 1) + peerinfo = self.nodes[1].getpeerinfo()[0] + assert('noban' in peerinfo['permissions']) + + # If we remove the ban, Node 0 should be able to reconnect even without noban permission + self.nodes[1].setban("127.0.0.1", "remove") + self.restart_node(1, []) + connect_nodes(self.nodes[0], 1) + peerinfo = self.nodes[1].getpeerinfo()[0] + assert(not 'noban' in peerinfo['permissions']) + +if __name__ == '__main__': + SetBanTests().main() diff --git a/test/functional/test_framework/address.py b/test/functional/test_framework/address.py index f36cffe957..194f2f061b 100644 --- a/test/functional/test_framework/address.py +++ b/test/functional/test_framework/address.py @@ -4,6 +4,8 @@ # file COPYING or http://www.opensource.org/licenses/mit-license.php. """Encode and decode BASE58, P2PKH and P2SH addresses.""" +import enum + from .script import hash256, hash160, sha256, CScript, OP_0 from .util import hex_str_to_bytes @@ -11,6 +13,13 @@ from . import segwit_addr ADDRESS_BCRT1_UNSPENDABLE = 'bcrt1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq3xueyj' + +class AddressType(enum.Enum): + bech32 = 'bech32' + p2sh_segwit = 'p2sh-segwit' + legacy = 'legacy' # P2PKH + + chars = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz' diff --git a/test/functional/test_framework/test_node.py b/test/functional/test_framework/test_node.py index cac5281764..df027397d2 100755 --- a/test/functional/test_framework/test_node.py +++ b/test/functional/test_framework/test_node.py @@ -68,6 +68,7 @@ class TestNode(): self.index = i self.datadir = datadir + self.bitcoinconf = os.path.join(self.datadir, "bitcoin.conf") self.stdout_dir = os.path.join(self.datadir, "stdout") self.stderr_dir = os.path.join(self.datadir, "stderr") self.chain = chain diff --git a/test/functional/test_framework/util.py b/test/functional/test_framework/util.py index 8730157c74..821e1cd3c5 100644 --- a/test/functional/test_framework/util.py +++ b/test/functional/test_framework/util.py @@ -7,7 +7,6 @@ from base64 import b64encode from binascii import unhexlify from decimal import Decimal, ROUND_DOWN -import hashlib import inspect import json import logging @@ -183,12 +182,6 @@ def check_json_precision(): def count_bytes(hex_string): return len(bytearray.fromhex(hex_string)) -def hash256(byte_str): - sha256 = hashlib.sha256() - sha256.update(byte_str) - sha256d = hashlib.sha256() - sha256d.update(sha256.digest()) - return sha256d.digest()[::-1] def hex_str_to_bytes(hex_str): return unhexlify(hex_str.encode('ascii')) @@ -300,6 +293,7 @@ def initialize_datadir(dirname, n, chain): f.write("discover=0\n") f.write("listenonion=0\n") f.write("printtoconsole=0\n") + f.write("upnp=0\n") os.makedirs(os.path.join(datadir, 'stderr'), exist_ok=True) os.makedirs(os.path.join(datadir, 'stdout'), exist_ok=True) return datadir @@ -342,9 +336,9 @@ def delete_cookie_file(datadir, chain): logger.debug("Deleting leftover cookie file") os.remove(os.path.join(datadir, chain, ".cookie")) -def get_bip9_status(node, key): - info = node.getblockchaininfo() - return info['bip9_softforks'][key] +def softfork_active(node, key): + """Return whether a softfork is active.""" + return node.getblockchaininfo()['softforks'][key]['active'] def set_node_times(nodes, t): for node in nodes: diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py index 79efa6131c..ad5673e03a 100755 --- a/test/functional/test_runner.py +++ b/test/functional/test_runner.py @@ -129,6 +129,8 @@ BASE_SCRIPTS = [ 'wallet_multiwallet.py --usecli', 'wallet_createwallet.py', 'wallet_createwallet.py --usecli', + 'wallet_watchonly.py', + 'wallet_watchonly.py --usecli', 'interface_http.py', 'interface_rpc.py', 'rpc_psbt.py', @@ -144,6 +146,7 @@ BASE_SCRIPTS = [ 'rpc_net.py', 'wallet_keypool.py', 'p2p_mempool.py', + 'rpc_setban.py', 'p2p_blocksonly.py', 'mining_prioritisetransaction.py', 'p2p_invalid_locator.py', @@ -200,6 +203,7 @@ BASE_SCRIPTS = [ 'rpc_scantxoutset.py', 'feature_logging.py', 'p2p_node_network_limited.py', + 'p2p_permissions.py', 'feature_blocksdir.py', 'feature_config_args.py', 'rpc_help.py', @@ -227,6 +231,7 @@ def main(): epilog=''' Help text and arguments for individual test script:''', formatter_class=argparse.RawTextHelpFormatter) + parser.add_argument('--ansi', action='store_true', default=sys.stdout.isatty(), help="Use ANSI colors and dots in output (enabled by default when standard output is a TTY)") parser.add_argument('--combinedlogslen', '-c', type=int, default=0, metavar='n', help='On failure, print a log (of length n lines) to the console, combined from the test framework and all test nodes.') parser.add_argument('--coverage', action='store_true', help='generate a basic coverage report for the RPC interface') parser.add_argument('--ci', action='store_true', help='Run checks and code that are usually only enabled in a continuous integration environment') @@ -239,7 +244,14 @@ def main(): 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') parser.add_argument('--filter', help='filter scripts to run by regular expression') + args, unknown_args = parser.parse_known_args() + if not args.ansi: + global BOLD, GREEN, RED, GREY + BOLD = ("", "") + GREEN = ("", "") + RED = ("", "") + GREY = ("", "") # args to be passed on always start with two dashes; tests are the remaining unknown args tests = [arg for arg in unknown_args if arg[:2] != "--"] @@ -341,9 +353,10 @@ def main(): combined_logs_len=args.combinedlogslen, failfast=args.failfast, runs_ci=args.ci, + use_term_control=args.ansi, ) -def run_tests(*, test_list, src_dir, build_dir, tmpdir, jobs=1, enable_coverage=False, args=None, combined_logs_len=0, failfast=False, runs_ci): +def run_tests(*, test_list, src_dir, build_dir, tmpdir, jobs=1, enable_coverage=False, args=None, combined_logs_len=0, failfast=False, runs_ci, use_term_control): args = args or [] # Warn if bitcoind is already running (unix only) @@ -385,6 +398,7 @@ def run_tests(*, test_list, src_dir, build_dir, tmpdir, jobs=1, enable_coverage= test_list=test_list, flags=flags, timeout_duration=40 * 60 if runs_ci else float('inf'), # in seconds + use_term_control=use_term_control, ) start_time = time.time() test_results = [] @@ -468,7 +482,7 @@ class TestHandler: Trigger the test scripts passed in via the list. """ - def __init__(self, *, num_tests_parallel, tests_dir, tmpdir, test_list, flags, timeout_duration): + def __init__(self, *, num_tests_parallel, tests_dir, tmpdir, test_list, flags, timeout_duration, use_term_control): assert num_tests_parallel >= 1 self.num_jobs = num_tests_parallel self.tests_dir = tests_dir @@ -478,6 +492,7 @@ class TestHandler: self.flags = flags self.num_running = 0 self.jobs = [] + self.use_term_control = use_term_control def get_next(self): while self.num_running < self.num_jobs and self.test_list: @@ -529,11 +544,13 @@ class TestHandler: status = "Failed" self.num_running -= 1 self.jobs.remove(job) - clearline = '\r' + (' ' * dot_count) + '\r' - print(clearline, end='', flush=True) + if self.use_term_control: + clearline = '\r' + (' ' * dot_count) + '\r' + print(clearline, end='', flush=True) dot_count = 0 return TestResult(name, status, int(time.time() - start_time)), testdir, stdout, stderr - print('.', end='', flush=True) + if self.use_term_control: + print('.', end='', flush=True) dot_count += 1 def kill_and_join(self): diff --git a/test/functional/wallet_address_types.py b/test/functional/wallet_address_types.py index a40613dfc7..4e4ed8f26b 100755 --- a/test/functional/wallet_address_types.py +++ b/test/functional/wallet_address_types.py @@ -175,6 +175,10 @@ class AddressTypeTest(BitcoinTestFramework): assert info['desc'] == descsum_create(info['desc'][:-9]) # Verify that stripping the checksum and feeding it to getdescriptorinfo roundtrips assert info['desc'] == self.nodes[0].getdescriptorinfo(info['desc'][:-9])['descriptor'] + assert_equal(info['desc'][-8:], self.nodes[0].getdescriptorinfo(info['desc'][:-9])['checksum']) + # Verify that keeping the checksum and feeding it to getdescriptorinfo roundtrips + assert info['desc'] == self.nodes[0].getdescriptorinfo(info['desc'])['descriptor'] + assert_equal(info['desc'][-8:], self.nodes[0].getdescriptorinfo(info['desc'])['checksum']) if not multisig and typ == 'legacy': # P2PKH diff --git a/test/functional/wallet_import_rescan.py b/test/functional/wallet_import_rescan.py index 47c97f62bf..4e20892596 100755 --- a/test/functional/wallet_import_rescan.py +++ b/test/functional/wallet_import_rescan.py @@ -20,6 +20,7 @@ happened previously. """ from test_framework.test_framework import BitcoinTestFramework +from test_framework.address import AddressType from test_framework.util import ( connect_nodes, assert_equal, @@ -27,21 +28,30 @@ from test_framework.util import ( ) import collections +from decimal import Decimal import enum import itertools +import random Call = enum.Enum("Call", "single multiaddress multiscript") Data = enum.Enum("Data", "address pub priv") Rescan = enum.Enum("Rescan", "no yes late_timestamp") -class Variant(collections.namedtuple("Variant", "call data rescan prune")): +class Variant(collections.namedtuple("Variant", "call data address_type rescan prune")): """Helper for importing one key and verifying scanned transactions.""" def do_import(self, timestamp): """Call one key import RPC.""" rescan = self.rescan == Rescan.yes + assert_equal(self.address["solvable"], True) + assert_equal(self.address["isscript"], self.address_type == AddressType.p2sh_segwit) + assert_equal(self.address["iswitness"], self.address_type == AddressType.bech32) + if self.address["isscript"]: + assert_equal(self.address["embedded"]["isscript"], False) + assert_equal(self.address["embedded"]["iswitness"], True) + if self.call == Call.single: if self.data == Data.address: response = self.node.importaddress(address=self.address["address"], label=self.label, rescan=rescan) @@ -52,7 +62,7 @@ class Variant(collections.namedtuple("Variant", "call data rescan prune")): assert_equal(response, None) elif self.call in (Call.multiaddress, Call.multiscript): - response = self.node.importmulti([{ + request = { "scriptPubKey": { "address": self.address["address"] } if self.call == Call.multiaddress else self.address["scriptPubKey"], @@ -61,13 +71,21 @@ class Variant(collections.namedtuple("Variant", "call data rescan prune")): "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)}) + } + if self.address_type == AddressType.p2sh_segwit and self.data != Data.address: + # We need solving data when providing a pubkey or privkey as data + request.update({"redeemscript": self.address['embedded']['scriptPubKey']}) + response = self.node.importmulti( + requests=[request], + options={"rescan": self.rescan in (Rescan.yes, Rescan.late_timestamp)}, + ) assert_equal(response, [{"success": True}]) - def check(self, txid=None, amount=None, confirmations=None): + def check(self, txid=None, amount=None, confirmation_height=None): """Verify that listtransactions/listreceivedbyaddress return expected values.""" txs = self.node.listtransactions(label=self.label, count=10000, include_watchonly=True) + current_height = self.node.getblockcount() assert_equal(len(txs), self.expected_txs) addresses = self.node.listreceivedbyaddress(minconf=0, include_watchonly=True, address_filter=self.address['address']) @@ -82,13 +100,13 @@ class Variant(collections.namedtuple("Variant", "call data rescan prune")): assert_equal(tx["category"], "receive") assert_equal(tx["label"], self.label) assert_equal(tx["txid"], txid) - assert_equal(tx["confirmations"], confirmations) + assert_equal(tx["confirmations"], 1 + current_height - confirmation_height) 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) + assert_equal(address["confirmations"], 1 + current_height - confirmation_height) # 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 @@ -102,7 +120,7 @@ class Variant(collections.namedtuple("Variant", "call data rescan prune")): # List of Variants for each way a key or address could be imported. -IMPORT_VARIANTS = [Variant(*variants) for variants in itertools.product(Call, Data, Rescan, (False, True))] +IMPORT_VARIANTS = [Variant(*variants) for variants in itertools.product(Call, Data, AddressType, Rescan, (False, True))] # List of nodes to import keys to. Half the nodes will have pruning disabled, # half will have it enabled. Different nodes will be used for imports that are @@ -116,6 +134,13 @@ IMPORT_NODES = [ImportNode(*fields) for fields in itertools.product((False, True # Rescans start at the earliest block up to 2 hours before the key timestamp. TIMESTAMP_WINDOW = 2 * 60 * 60 +AMOUNT_DUST = 0.00000546 + + +def get_rand_amount(): + r = random.uniform(AMOUNT_DUST, 1) + return Decimal(str(round(r, 8))) + class ImportRescanTest(BitcoinTestFramework): def set_test_params(self): @@ -125,12 +150,12 @@ class ImportRescanTest(BitcoinTestFramework): self.skip_if_no_wallet() def setup_network(self): - extra_args = [["-addresstype=legacy"] for _ in range(self.num_nodes)] + self.extra_args = [[]] * self.num_nodes for i, import_node in enumerate(IMPORT_NODES, 2): if import_node.prune: - extra_args[i] += ["-prune=1"] + self.extra_args[i] += ["-prune=1"] - self.add_nodes(self.num_nodes, extra_args=extra_args) + self.add_nodes(self.num_nodes, extra_args=self.extra_args) # Import keys with pruning disabled self.start_nodes(extra_args=[[]] * self.num_nodes) @@ -147,17 +172,23 @@ class ImportRescanTest(BitcoinTestFramework): # 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( + label=variant.label, + address_type=variant.address_type.value, + )) variant.key = self.nodes[1].dumpprivkey(variant.address["address"]) - variant.initial_amount = 1 - (i + 1) / 64 + variant.initial_amount = get_rand_amount() variant.initial_txid = self.nodes[0].sendtoaddress(variant.address["address"], variant.initial_amount) + self.nodes[0].generate(1) # Generate one block for each send + variant.confirmation_height = self.nodes[0].getblockcount() + variant.timestamp = self.nodes[0].getblockheader(self.nodes[0].getbestblockhash())["time"] - # Generate a block containing the initial transactions, then another - # block further in the future (past the rescan window). - self.nodes[0].generate(1) + # Generate a block further in the future (past the rescan window). assert_equal(self.nodes[0].getrawmempool(), []) - timestamp = self.nodes[0].getblockheader(self.nodes[0].getbestblockhash())["time"] - set_node_times(self.nodes, timestamp + TIMESTAMP_WINDOW + 1) + set_node_times( + self.nodes, + self.nodes[0].getblockheader(self.nodes[0].getbestblockhash())["time"] + TIMESTAMP_WINDOW + 1, + ) self.nodes[0].generate(1) self.sync_all() @@ -167,11 +198,11 @@ class ImportRescanTest(BitcoinTestFramework): self.log.info('Run import for variant {}'.format(variant)) expect_rescan = variant.rescan == Rescan.yes variant.node = self.nodes[2 + IMPORT_NODES.index(ImportNode(variant.prune, expect_rescan))] - variant.do_import(timestamp) + variant.do_import(variant.timestamp) if expect_rescan: variant.expected_balance = variant.initial_amount variant.expected_txs = 1 - variant.check(variant.initial_txid, variant.initial_amount, 2) + variant.check(variant.initial_txid, variant.initial_amount, variant.confirmation_height) else: variant.expected_balance = 0 variant.expected_txs = 0 @@ -179,11 +210,11 @@ class ImportRescanTest(BitcoinTestFramework): # Create new transactions sending to each address. for i, variant in enumerate(IMPORT_VARIANTS): - variant.sent_amount = 1 - (2 * i + 1) / 128 + variant.sent_amount = get_rand_amount() variant.sent_txid = self.nodes[0].sendtoaddress(variant.address["address"], variant.sent_amount) + self.nodes[0].generate(1) # Generate one block for each send + variant.confirmation_height = self.nodes[0].getblockcount() - # Generate a block containing the new transactions. - self.nodes[0].generate(1) assert_equal(self.nodes[0].getrawmempool(), []) self.sync_all() @@ -192,7 +223,7 @@ class ImportRescanTest(BitcoinTestFramework): self.log.info('Run check for variant {}'.format(variant)) variant.expected_balance += variant.sent_amount variant.expected_txs += 1 - variant.check(variant.sent_txid, variant.sent_amount, 1) + variant.check(variant.sent_txid, variant.sent_amount, variant.confirmation_height) if __name__ == "__main__": ImportRescanTest().main() diff --git a/test/functional/wallet_importmulti.py b/test/functional/wallet_importmulti.py index e19c7919a9..e4a4ab1f35 100755 --- a/test/functional/wallet_importmulti.py +++ b/test/functional/wallet_importmulti.py @@ -552,7 +552,7 @@ class ImportMultiTest(BitcoinTestFramework): "keys": [key.privkey]}, success=False, error_code=-5, - error_message="Descriptor is invalid") + error_message="Missing checksum") # Test importing of a P2SH-P2WPKH address via descriptor + private key key = get_key(self.nodes[0]) diff --git a/test/functional/wallet_watchonly.py b/test/functional/wallet_watchonly.py new file mode 100644 index 0000000000..be8d7714fb --- /dev/null +++ b/test/functional/wallet_watchonly.py @@ -0,0 +1,107 @@ +#!/usr/bin/env python3 +# Copyright (c) 2018-2019 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. +"""Test createwallet arguments. +""" + +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import ( + assert_equal, + assert_raises_rpc_error +) + + +class CreateWalletWatchonlyTest(BitcoinTestFramework): + def set_test_params(self): + self.setup_clean_chain = False + self.num_nodes = 1 + self.supports_cli = True + + def skip_test_if_missing_module(self): + self.skip_if_no_wallet() + + def run_test(self): + node = self.nodes[0] + + self.nodes[0].createwallet(wallet_name='default') + def_wallet = node.get_wallet_rpc('default') + + a1 = def_wallet.getnewaddress() + wo_change = def_wallet.getnewaddress() + wo_addr = def_wallet.getnewaddress() + + self.nodes[0].createwallet(wallet_name='wo', disable_private_keys=True) + wo_wallet = node.get_wallet_rpc('wo') + + wo_wallet.importpubkey(pubkey=def_wallet.getaddressinfo(wo_addr)['pubkey']) + wo_wallet.importpubkey(pubkey=def_wallet.getaddressinfo(wo_change)['pubkey']) + + # generate some btc for testing + node.generatetoaddress(101, a1) + + # send 1 btc to our watch-only address + txid = def_wallet.sendtoaddress(wo_addr, 1) + self.nodes[0].generate(1) + + # getbalance + self.log.info('include_watchonly should default to true for watch-only wallets') + self.log.info('Testing getbalance watch-only defaults') + assert_equal(wo_wallet.getbalance(), 1) + assert_equal(len(wo_wallet.listtransactions()), 1) + assert_equal(wo_wallet.getbalance(include_watchonly=False), 0) + + self.log.info('Testing listreceivedbyaddress watch-only defaults') + result = wo_wallet.listreceivedbyaddress() + assert_equal(len(result), 1) + assert_equal(result[0]["involvesWatchonly"], True) + result = wo_wallet.listreceivedbyaddress(include_watchonly=False) + assert_equal(len(result), 0) + + self.log.info('Testing listreceivedbylabel watch-only defaults') + result = wo_wallet.listreceivedbylabel() + assert_equal(len(result), 1) + assert_equal(result[0]["involvesWatchonly"], True) + result = wo_wallet.listreceivedbylabel(include_watchonly=False) + assert_equal(len(result), 0) + + self.log.info('Testing listtransactions watch-only defaults') + result = wo_wallet.listtransactions() + assert_equal(len(result), 1) + assert_equal(result[0]["involvesWatchonly"], True) + result = wo_wallet.listtransactions(include_watchonly=False) + assert_equal(len(result), 0) + + self.log.info('Testing listsinceblock watch-only defaults') + result = wo_wallet.listsinceblock() + assert_equal(len(result["transactions"]), 1) + assert_equal(result["transactions"][0]["involvesWatchonly"], True) + result = wo_wallet.listsinceblock(include_watchonly=False) + assert_equal(len(result["transactions"]), 0) + + self.log.info('Testing gettransaction watch-only defaults') + result = wo_wallet.gettransaction(txid) + assert_equal(result["details"][0]["involvesWatchonly"], True) + result = wo_wallet.gettransaction(txid=txid, include_watchonly=False) + assert_equal(len(result["details"]), 0) + + self.log.info('Testing walletcreatefundedpsbt watch-only defaults') + inputs = [] + outputs = [{a1: 0.5}] + options = {'changeAddress': wo_change} + no_wo_options = {'changeAddress': wo_change, 'includeWatching': False} + + result = wo_wallet.walletcreatefundedpsbt(inputs=inputs, outputs=outputs, options=options) + assert_equal("psbt" in result, True) + assert_raises_rpc_error(-4, "Insufficient funds", wo_wallet.walletcreatefundedpsbt, inputs, outputs, 0, no_wo_options) + + self.log.info('Testing fundrawtransaction watch-only defaults') + rawtx = wo_wallet.createrawtransaction(inputs=inputs, outputs=outputs) + result = wo_wallet.fundrawtransaction(hexstring=rawtx, options=options) + assert_equal("hex" in result, True) + assert_raises_rpc_error(-4, "Insufficient funds", wo_wallet.fundrawtransaction, rawtx, no_wo_options) + + + +if __name__ == '__main__': + CreateWalletWatchonlyTest().main() diff --git a/test/lint/lint-python-dead-code-whitelist b/test/lint/lint-python-dead-code-whitelist new file mode 100644 index 0000000000..2522c8fa1c --- /dev/null +++ b/test/lint/lint-python-dead-code-whitelist @@ -0,0 +1,45 @@ +BadInputOutpointIndex # unused class (test/functional/data/invalid_txs.py) +_.carbon_path # unused attribute (contrib/macdeploy/custom_dsstore.py) +connection_lost # unused function (test/functional/test_framework/mininode.py) +connection_made # unused function (test/functional/test_framework/mininode.py) +_.converter # unused attribute (test/functional/test_framework/test_framework.py) +_.daemon # unused attribute (test/functional/test_framework/socks5.py) +data_received # unused function (test/functional/test_framework/mininode.py) +DuplicateInput # unused class (test/functional/data/invalid_txs.py) +_.filename # unused attribute (contrib/macdeploy/custom_dsstore.py) +InvalidOPIFConstruction # unused class (test/functional/data/invalid_txs.py) +_.is_compressed # unused property (test/functional/test_framework/key.py) +legacy # unused variable (test/functional/test_framework/address.py) +msg_generic # unused class (test/functional/test_framework/messages.py) +NonexistentInput # unused class (test/functional/data/invalid_txs.py) +on_addr # unused function (test/functional/test_framework/mininode.py) +on_blocktxn # unused function (test/functional/test_framework/mininode.py) +on_block # unused function (test/functional/test_framework/mininode.py) +on_cmpctblock # unused function (test/functional/test_framework/mininode.py) +on_feefilter # unused function (test/functional/test_framework/mininode.py) +on_getaddr # unused function (test/functional/test_framework/mininode.py) +on_getblocks # unused function (test/functional/test_framework/mininode.py) +on_getblocktxn # unused function (test/functional/test_framework/mininode.py) +on_getdata # unused function (test/functional/test_framework/mininode.py) +on_getheaders # unused function (test/functional/test_framework/mininode.py) +on_headers # unused function (test/functional/test_framework/mininode.py) +on_inv # unused function (test/functional/test_framework/mininode.py) +on_mempool # unused function (test/functional/test_framework/mininode.py) +on_notfound # unused function (test/functional/test_framework/mininode.py) +on_ping # unused function (test/functional/test_framework/mininode.py) +on_pong # unused function (test/functional/test_framework/mininode.py) +on_reject # unused function (test/functional/test_framework/mininode.py) +on_sendcmpct # unused function (test/functional/test_framework/mininode.py) +on_sendheaders # unused function (test/functional/test_framework/mininode.py) +on_tx # unused function (test/functional/test_framework/mininode.py) +on_verack # unused function (test/functional/test_framework/mininode.py) +on_version # unused function (test/functional/test_framework/mininode.py) +_.optionxform # unused attribute (test/util/bitcoin-util-test.py) +OutputMissing # unused class (test/functional/data/invalid_txs.py) +_.posix_path # unused attribute (contrib/macdeploy/custom_dsstore.py) +profile_with_perf # unused function (test/functional/test_framework/test_node.py) +SizeTooSmall # unused class (test/functional/data/invalid_txs.py) +SpendNegative # unused class (test/functional/data/invalid_txs.py) +SpendTooMuch # unused class (test/functional/data/invalid_txs.py) +TooManySigops # unused class (test/functional/data/invalid_txs.py) +verify_ecdsa # unused function (test/functional/test_framework/key.py) diff --git a/test/lint/lint-python-dead-code.sh b/test/lint/lint-python-dead-code.sh index 588ba428d7..77bf5990a7 100755 --- a/test/lint/lint-python-dead-code.sh +++ b/test/lint/lint-python-dead-code.sh @@ -15,5 +15,5 @@ fi vulture \ --min-confidence 60 \ - --ignore-names "argtypes,connection_lost,connection_made,converter,data_received,daemon,errcheck,is_compressed,is_valid,verify_ecdsa,msg_generic,on_*,optionxform,restype,profile_with_perf" \ - $(git ls-files -- "*.py" ":(exclude)contrib/" ":(exclude)test/functional/data/invalid_txs.py") + $(git rev-parse --show-toplevel) \ + $(dirname "${BASH_SOURCE[0]}")/lint-python-dead-code-whitelist diff --git a/test/lint/lint-spelling.ignore-words.txt b/test/lint/lint-spelling.ignore-words.txt index a25de2435b..b08837c1d4 100644 --- a/test/lint/lint-spelling.ignore-words.txt +++ b/test/lint/lint-spelling.ignore-words.txt @@ -12,3 +12,4 @@ cachable errorstring keyserver homogenous +setban |