diff options
Diffstat (limited to 'test')
25 files changed, 465 insertions, 277 deletions
diff --git a/test/README.md b/test/README.md index c3e4ae9ad2..e1dab92a06 100644 --- a/test/README.md +++ b/test/README.md @@ -145,7 +145,7 @@ levels using the logger included in the test_framework, e.g. `test_framework.log` and no logs are output to the console. - when run directly, *all* logs are written to `test_framework.log` and INFO level and above are output to the console. -- when run on Travis, no logs are output to the console. However, if a test +- when run by [our CI (Continuous Integration)](/ci/README.md), no logs are output to the console. However, if a test fails, the `test_framework.log` and bitcoind `debug.log`s will all be dumped to the console to help troubleshooting. diff --git a/test/functional/README.md b/test/functional/README.md index 77a9ce9acb..004e0afb1d 100644 --- a/test/functional/README.md +++ b/test/functional/README.md @@ -51,10 +51,13 @@ don't have test cases for. #### General test-writing advice +- Instead of inline comments or no test documentation at all, log the comments to the test log, e.g. + `self.log.info('Create enough transactions to fill a block')`. Logs make the test code easier to read and the test + logic easier [to debug](/test/README.md#test-logging). - Set `self.num_nodes` to the minimum number of nodes necessary for the test. Having additional unrequired nodes adds to the execution time of the test as well as memory/CPU/disk requirements (which is important when running tests in - parallel or on Travis). + parallel). - Avoid stop-starting the nodes multiple times during the test if possible. A stop-start takes several seconds, so doing it several times blows up the runtime of the test. @@ -131,9 +134,6 @@ Utilities for manipulating transaction scripts (originally from python-bitcoinli #### [key.py](test_framework/key.py) Test-only secp256k1 elliptic curve implementation -#### [bignum.py](test_framework/bignum.py) -Helpers for script.py - #### [blocktools.py](test_framework/blocktools.py) Helper functions for creating blocks and transactions. diff --git a/test/functional/data/invalid_txs.py b/test/functional/data/invalid_txs.py index 99c88bbcc0..ce14998fd1 100644 --- a/test/functional/data/invalid_txs.py +++ b/test/functional/data/invalid_txs.py @@ -21,7 +21,13 @@ Invalid tx cases not covered here can be found by running: """ import abc -from test_framework.messages import CTransaction, CTxIn, CTxOut, COutPoint +from test_framework.messages import ( + COutPoint, + CTransaction, + CTxIn, + CTxOut, + MAX_MONEY, +) from test_framework import script as sc from test_framework.blocktools import create_tx_with_script, MAX_BLOCK_SIGOPS from test_framework.script import ( @@ -166,7 +172,7 @@ class SpendTooMuch(BadTxTemplate): self.spend_tx, 0, script_pub_key=basic_p2sh, amount=(self.spend_avail + 1)) -class SpendNegative(BadTxTemplate): +class CreateNegative(BadTxTemplate): reject_reason = 'bad-txns-vout-negative' expect_disconnect = True @@ -174,6 +180,25 @@ class SpendNegative(BadTxTemplate): return create_tx_with_script(self.spend_tx, 0, amount=-1) +class CreateTooLarge(BadTxTemplate): + reject_reason = 'bad-txns-vout-toolarge' + expect_disconnect = True + + def get_tx(self): + return create_tx_with_script(self.spend_tx, 0, amount=MAX_MONEY + 1) + + +class CreateSumTooLarge(BadTxTemplate): + reject_reason = 'bad-txns-txouttotal-toolarge' + expect_disconnect = True + + def get_tx(self): + tx = create_tx_with_script(self.spend_tx, 0, amount=MAX_MONEY) + tx.vout = [tx.vout[0]] * 2 + tx.calc_sha256() + return tx + + class InvalidOPIFConstruction(BadTxTemplate): reject_reason = "mandatory-script-verify-flag-failed (Invalid OP_IF construction)" expect_disconnect = True @@ -237,4 +262,3 @@ DisabledOpcodeTemplates = [getDisabledOpcodeTemplate(opcode) for opcode in [ def iter_all_templates(): """Iterate through all bad transaction template types.""" return BadTxTemplate.__subclasses__() - diff --git a/test/functional/feature_assumevalid.py b/test/functional/feature_assumevalid.py index 1b434c4485..ef4d9411c5 100755 --- a/test/functional/feature_assumevalid.py +++ b/test/functional/feature_assumevalid.py @@ -47,16 +47,19 @@ from test_framework.script import (CScript, OP_TRUE) from test_framework.test_framework import BitcoinTestFramework from test_framework.util import assert_equal + class BaseNode(P2PInterface): def send_header_for_blocks(self, new_blocks): headers_message = msg_headers() headers_message.headers = [CBlockHeader(b) for b in new_blocks] self.send_message(headers_message) + class AssumeValidTest(BitcoinTestFramework): def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 3 + self.rpc_timeout = 120 def setup_network(self): self.add_nodes(3) @@ -187,5 +190,6 @@ class AssumeValidTest(BitcoinTestFramework): self.send_blocks_until_disconnected(p2p2) self.assert_blockchain_height(self.nodes[2], 101) + if __name__ == '__main__': AssumeValidTest().main() diff --git a/test/functional/feature_block.py b/test/functional/feature_block.py index 38bf2faf89..9e8e11e64f 100755 --- a/test/functional/feature_block.py +++ b/test/functional/feature_block.py @@ -630,17 +630,19 @@ class FullBlockTest(BitcoinTestFramework): self.log.info("Reject a block with invalid work") self.move_tip(44) - b47 = self.next_block(47, solve=False) + b47 = self.next_block(47) target = uint256_from_compact(b47.nBits) - while b47.sha256 < target: + while b47.sha256 <= target: + # Rehash nonces until an invalid too-high-hash block is found. b47.nNonce += 1 b47.rehash() self.send_blocks([b47], False, force_send=True, reject_reason='high-hash', reconnect=True) self.log.info("Reject a block with a timestamp >2 hours in the future") self.move_tip(44) - b48 = self.next_block(48, solve=False) + b48 = self.next_block(48) b48.nTime = int(time.time()) + 60 * 60 * 3 + # Header timestamp has changed. Re-solve the block. b48.solve() self.send_blocks([b48], False, force_send=True, reject_reason='time-too-new') @@ -1321,7 +1323,7 @@ class FullBlockTest(BitcoinTestFramework): tx.rehash() return tx - def next_block(self, number, spend=None, additional_coinbase_value=0, script=CScript([OP_TRUE]), solve=True, *, version=1): + def next_block(self, number, spend=None, additional_coinbase_value=0, script=CScript([OP_TRUE]), *, version=1): if self.tip is None: base_block_hash = self.genesis_hash block_time = int(time.time()) + 1 @@ -1343,8 +1345,8 @@ class FullBlockTest(BitcoinTestFramework): self.sign_tx(tx, spend) self.add_transactions_to_block(block, [tx]) block.hashMerkleRoot = block.calc_merkle_root() - if solve: - block.solve() + # Block is created. Find a valid nonce. + block.solve() self.tip = block self.block_heights[block.sha256] = height assert number not in self.blocks diff --git a/test/functional/feature_cltv.py b/test/functional/feature_cltv.py index e2b347f925..073ed8d7c7 100755 --- a/test/functional/feature_cltv.py +++ b/test/functional/feature_cltv.py @@ -60,7 +60,7 @@ class BIP65Test(BitcoinTestFramework): '-acceptnonstdtxn=1', # cltv_invalidate is nonstandard ]] self.setup_clean_chain = True - self.rpc_timeout = 120 + self.rpc_timeout = 480 def skip_test_if_missing_module(self): self.skip_if_no_wallet() diff --git a/test/functional/feature_dersig.py b/test/functional/feature_dersig.py index 9628945a80..38cdf0501e 100755 --- a/test/functional/feature_dersig.py +++ b/test/functional/feature_dersig.py @@ -36,7 +36,6 @@ def unDERify(tx): tx.vin[0].scriptSig = CScript(newscript) - class BIP66Test(BitcoinTestFramework): def set_test_params(self): self.num_nodes = 1 @@ -45,7 +44,7 @@ class BIP66Test(BitcoinTestFramework): '-par=1', # Use only one script thread to get the exact log msg for testing ]] self.setup_clean_chain = True - self.rpc_timeout = 120 + self.rpc_timeout = 240 def skip_test_if_missing_module(self): self.skip_if_no_wallet() diff --git a/test/functional/feature_fee_estimation.py b/test/functional/feature_fee_estimation.py index 1b33724594..5128485ec0 100755 --- a/test/functional/feature_fee_estimation.py +++ b/test/functional/feature_fee_estimation.py @@ -99,8 +99,20 @@ def split_inputs(from_node, txins, txouts, initial_split=False): txouts.append({"txid": txid, "vout": 0, "amount": half_change}) txouts.append({"txid": txid, "vout": 1, "amount": rem_change}) +def check_raw_estimates(node, fees_seen): + """Call estimaterawfee and verify that the estimates meet certain invariants.""" -def check_estimates(node, fees_seen): + delta = 1.0e-6 # account for rounding error + for i in range(1, 26): + for _, e in node.estimaterawfee(i).items(): + feerate = float(e["feerate"]) + assert_greater_than(feerate, 0) + + if feerate + delta < min(fees_seen) or feerate - delta > max(fees_seen): + raise AssertionError("Estimated fee (%f) out of range (%f,%f)" + % (feerate, min(fees_seen), max(fees_seen))) + +def check_smart_estimates(node, fees_seen): """Call estimatesmartfee and verify that the estimates meet certain invariants.""" delta = 1.0e-6 # account for rounding error @@ -123,6 +135,9 @@ def check_estimates(node, fees_seen): else: assert_greater_than_or_equal(i + 1, e["blocks"]) +def check_estimates(node, fees_seen): + check_raw_estimates(node, fees_seen) + check_smart_estimates(node, fees_seen) class EstimateFeeTest(BitcoinTestFramework): def set_test_params(self): diff --git a/test/functional/feature_segwit.py b/test/functional/feature_segwit.py index 82c7e55245..909a43c8d9 100755 --- a/test/functional/feature_segwit.py +++ b/test/functional/feature_segwit.py @@ -72,6 +72,7 @@ class SegWitTest(BitcoinTestFramework): "-addresstype=legacy", ], ] + self.rpc_timeout = 120 def skip_test_if_missing_module(self): self.skip_if_no_wallet() diff --git a/test/functional/mempool_accept.py b/test/functional/mempool_accept.py index 900cabccda..9ade22a7eb 100755 --- a/test/functional/mempool_accept.py +++ b/test/functional/mempool_accept.py @@ -16,6 +16,7 @@ from test_framework.messages import ( CTransaction, CTxOut, MAX_BLOCK_BASE_SIZE, + MAX_MONEY, ) from test_framework.script import ( hash160, @@ -220,7 +221,7 @@ class MempoolAcceptanceTest(BitcoinTestFramework): # The following two validations prevent overflow of the output amounts (see CVE-2010-5139). self.log.info('A transaction with too large output value') tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference))) - tx.vout[0].nValue = 21000000 * COIN + 1 + tx.vout[0].nValue = MAX_MONEY + 1 self.check_mempool_result( result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': 'bad-txns-vout-toolarge'}], rawtxs=[tx.serialize().hex()], @@ -229,7 +230,7 @@ class MempoolAcceptanceTest(BitcoinTestFramework): self.log.info('A transaction with too large sum of output values') tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference))) tx.vout = [tx.vout[0]] * 2 - tx.vout[0].nValue = 21000000 * COIN + tx.vout[0].nValue = MAX_MONEY self.check_mempool_result( result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': 'bad-txns-txouttotal-toolarge'}], rawtxs=[tx.serialize().hex()], diff --git a/test/functional/mempool_reorg.py b/test/functional/mempool_reorg.py index 3b148d5cf0..d797dff134 100755 --- a/test/functional/mempool_reorg.py +++ b/test/functional/mempool_reorg.py @@ -76,7 +76,7 @@ class MempoolCoinbaseTest(BitcoinTestFramework): spend_101_id = self.nodes[0].sendrawtransaction(spend_101_raw) spend_102_1_id = self.nodes[0].sendrawtransaction(spend_102_1_raw) - self.sync_all(timeout=360) + self.sync_all(timeout=720) assert_equal(set(self.nodes[0].getrawmempool()), {spend_101_id, spend_102_1_id, timelock_tx_id}) @@ -91,10 +91,11 @@ class MempoolCoinbaseTest(BitcoinTestFramework): for node in self.nodes: node.invalidateblock(new_blocks[0]) - self.sync_all(timeout=360) + self.sync_all(timeout=720) # mempool should be empty. assert_equal(set(self.nodes[0].getrawmempool()), set()) + if __name__ == '__main__': MempoolCoinbaseTest().main() diff --git a/test/functional/p2p_segwit.py b/test/functional/p2p_segwit.py index ad5a124680..785c476e19 100755 --- a/test/functional/p2p_segwit.py +++ b/test/functional/p2p_segwit.py @@ -147,6 +147,11 @@ class TestP2PConn(P2PInterface): super().__init__() self.getdataset = set() + # Avoid sending out msg_getdata in the mininode thread as a reply to invs. + # They are not needed and would only lead to races because we send msg_getdata out in the test thread + def on_inv(self, message): + pass + def on_getdata(self, message): for inv in message.inv: self.getdataset.add(inv.hash) diff --git a/test/functional/rpc_estimatefee.py b/test/functional/rpc_estimatefee.py new file mode 100644 index 0000000000..8bdecfc8cd --- /dev/null +++ b/test/functional/rpc_estimatefee.py @@ -0,0 +1,51 @@ +#!/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. +"""Test the estimatefee RPCs. + +Test the following RPCs: + - estimatesmartfee + - estimaterawfee +""" + +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import assert_raises_rpc_error + +class EstimateFeeTest(BitcoinTestFramework): + def set_test_params(self): + self.setup_clean_chain = False + self.num_nodes = 1 + + def run_test(self): + # missing required params + assert_raises_rpc_error(-1, "estimatesmartfee", self.nodes[0].estimatesmartfee) + assert_raises_rpc_error(-1, "estimaterawfee", self.nodes[0].estimaterawfee) + + # wrong type for conf_target + assert_raises_rpc_error(-3, "Expected type number, got string", self.nodes[0].estimatesmartfee, 'foo') + assert_raises_rpc_error(-3, "Expected type number, got string", self.nodes[0].estimaterawfee, 'foo') + + # wrong type for estimatesmartfee(estimate_mode) + assert_raises_rpc_error(-3, "Expected type string, got number", self.nodes[0].estimatesmartfee, 1, 1) + assert_raises_rpc_error(-8, "Invalid estimate_mode parameter", self.nodes[0].estimatesmartfee, 1, 'foo') + + # wrong type for estimaterawfee(threshold) + assert_raises_rpc_error(-3, "Expected type number, got string", self.nodes[0].estimaterawfee, 1, 'foo') + + # extra params + assert_raises_rpc_error(-1, "estimatesmartfee", self.nodes[0].estimatesmartfee, 1, 'ECONOMICAL', 1) + assert_raises_rpc_error(-1, "estimaterawfee", self.nodes[0].estimaterawfee, 1, 1, 1) + + # valid calls + self.nodes[0].estimatesmartfee(1) + # self.nodes[0].estimatesmartfee(1, None) + self.nodes[0].estimatesmartfee(1, 'ECONOMICAL') + + self.nodes[0].estimaterawfee(1) + self.nodes[0].estimaterawfee(1, None) + self.nodes[0].estimaterawfee(1, 1) + + +if __name__ == '__main__': + EstimateFeeTest().main() diff --git a/test/functional/rpc_getdescriptorinfo.py b/test/functional/rpc_getdescriptorinfo.py new file mode 100755 index 0000000000..977dc805ef --- /dev/null +++ b/test/functional/rpc_getdescriptorinfo.py @@ -0,0 +1,65 @@ +#!/usr/bin/env python3 +# Copyright (c) 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 getdescriptorinfo RPC. +""" + +from test_framework.test_framework import BitcoinTestFramework +from test_framework.descriptors import descsum_create +from test_framework.util import ( + assert_equal, + assert_raises_rpc_error, +) + + +class DescriptorTest(BitcoinTestFramework): + def set_test_params(self): + self.num_nodes = 1 + self.extra_args = [["-disablewallet"]] + + def test_desc(self, desc, isrange, issolvable, hasprivatekeys): + info = self.nodes[0].getdescriptorinfo(desc) + assert_equal(info, self.nodes[0].getdescriptorinfo(descsum_create(desc))) + assert_equal(info['descriptor'], descsum_create(desc)) + assert_equal(info['isrange'], isrange) + assert_equal(info['issolvable'], issolvable) + assert_equal(info['hasprivatekeys'], hasprivatekeys) + + def run_test(self): + assert_raises_rpc_error(-1, 'getdescriptorinfo', self.nodes[0].getdescriptorinfo) + assert_raises_rpc_error(-3, 'Expected type string', self.nodes[0].getdescriptorinfo, 1) + assert_raises_rpc_error(-5, 'is not a valid descriptor function', self.nodes[0].getdescriptorinfo, '') + + # P2PK output with the specified public key. + self.test_desc('pk(0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798)', isrange=False, issolvable=True, hasprivatekeys=False) + # P2PKH output with the specified public key. + self.test_desc('pkh(02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5)', isrange=False, issolvable=True, hasprivatekeys=False) + # P2WPKH output with the specified public key. + self.test_desc('wpkh(02f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9)', isrange=False, issolvable=True, hasprivatekeys=False) + # P2SH-P2WPKH output with the specified public key. + self.test_desc('sh(wpkh(03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556))', isrange=False, issolvable=True, hasprivatekeys=False) + # Any P2PK, P2PKH, P2WPKH, or P2SH-P2WPKH output with the specified public key. + self.test_desc('combo(0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798)', isrange=False, issolvable=True, hasprivatekeys=False) + # An (overly complicated) P2SH-P2WSH-P2PKH output with the specified public key. + self.test_desc('sh(wsh(pkh(02e493dbf1c10d80f3581e4904930b1404cc6c13900ee0758474fa94abe8c4cd13)))', isrange=False, issolvable=True, hasprivatekeys=False) + # A bare *1-of-2* multisig output with keys in the specified order. + self.test_desc('multi(1,022f8bde4d1a07209355b4a7250a5c5128e88b84bddc619ab7cba8d569b240efe4,025cbdf0646e5db4eaa398f365f2ea7a0e3d419b7e0330e39ce92bddedcac4f9bc)', isrange=False, issolvable=True, hasprivatekeys=False) + # A P2SH *2-of-2* multisig output with keys in the specified order. + self.test_desc('sh(multi(2,022f01e5e15cca351daff3843fb70f3c2f0a1bdd05e5af888a67784ef3e10a2a01,03acd484e2f0c7f65309ad178a9f559abde09796974c57e714c35f110dfc27ccbe))', isrange=False, issolvable=True, hasprivatekeys=False) + # A P2WSH *2-of-3* multisig output with keys in the specified order. + self.test_desc('wsh(multi(2,03a0434d9e47f3c86235477c7b1ae6ae5d3442d49b1943c2b752a68e2a47e247c7,03774ae7f858a9411e5ef4246b70c65aac5649980be5c17891bbec17895da008cb,03d01115d548e7561b15c38f004d734633687cf4419620095bc5b0f47070afe85a))', isrange=False, issolvable=True, hasprivatekeys=False) + # A P2SH-P2WSH *1-of-3* multisig output with keys in the specified order. + self.test_desc('sh(wsh(multi(1,03f28773c2d975288bc7d1d205c3748651b075fbc6610e58cddeeddf8f19405aa8,03499fdf9e895e719cfd64e67f07d38e3226aa7b63678949e6e49b241a60e823e4,02d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e)))', isrange=False, issolvable=True, hasprivatekeys=False) + # A P2PK output with the public key of the specified xpub. + self.test_desc('pk(tpubD6NzVbkrYhZ4WaWSyoBvQwbpLkojyoTZPRsgXELWz3Popb3qkjcJyJUGLnL4qHHoQvao8ESaAstxYSnhyswJ76uZPStJRJCTKvosUCJZL5B)', isrange=False, issolvable=True, hasprivatekeys=False) + # A P2PKH output with child key *1'/2* of the specified xpub. + self.test_desc("pkh(tpubD6NzVbkrYhZ4WaWSyoBvQwbpLkojyoTZPRsgXELWz3Popb3qkjcJyJUGLnL4qHHoQvao8ESaAstxYSnhyswJ76uZPStJRJCTKvosUCJZL5B/1'/2)", isrange=False, issolvable=True, hasprivatekeys=False) + # A set of P2PKH outputs, but additionally specifies that the specified xpub is a child of a master with fingerprint `d34db33f`, and derived using path `44'/0'/0'`. + self.test_desc("pkh([d34db33f/44'/0'/0']tpubD6NzVbkrYhZ4WaWSyoBvQwbpLkojyoTZPRsgXELWz3Popb3qkjcJyJUGLnL4qHHoQvao8ESaAstxYSnhyswJ76uZPStJRJCTKvosUCJZL5B/1/*)", isrange=True, issolvable=True, hasprivatekeys=False) + # A set of *1-of-2* P2WSH multisig outputs where the first multisig key is the *1/0/`i`* child of the first specified xpub and the second multisig key is the *0/0/`i`* child of the second specified xpub, and `i` is any number in a configurable range (`0-1000` by default). + self.test_desc("wsh(multi(1,tpubD6NzVbkrYhZ4WaWSyoBvQwbpLkojyoTZPRsgXELWz3Popb3qkjcJyJUGLnL4qHHoQvao8ESaAstxYSnhyswJ76uZPStJRJCTKvosUCJZL5B/1/0/*,tpubD6NzVbkrYhZ4WaWSyoBvQwbpLkojyoTZPRsgXELWz3Popb3qkjcJyJUGLnL4qHHoQvao8ESaAstxYSnhyswJ76uZPStJRJCTKvosUCJZL5B/0/0/*))", isrange=True, issolvable=True, hasprivatekeys=False) + + +if __name__ == '__main__': + DescriptorTest().main() diff --git a/test/functional/test_framework/bignum.py b/test/functional/test_framework/bignum.py deleted file mode 100644 index db5ccd62c2..0000000000 --- a/test/functional/test_framework/bignum.py +++ /dev/null @@ -1,58 +0,0 @@ -#!/usr/bin/env python3 -# -# Distributed under the MIT software license, see the accompanying -# file COPYING or http://www.opensource.org/licenses/mit-license.php. -"""Big number routines. - -This file is copied from python-bitcoinlib. -""" - -import struct - - -# generic big endian MPI format - -def bn_bytes(v, have_ext=False): - ext = 0 - if have_ext: - ext = 1 - return ((v.bit_length()+7)//8) + ext - -def bn2bin(v): - s = bytearray() - i = bn_bytes(v) - while i > 0: - s.append((v >> ((i-1) * 8)) & 0xff) - i -= 1 - return s - -def bn2mpi(v): - have_ext = False - if v.bit_length() > 0: - have_ext = (v.bit_length() & 0x07) == 0 - - neg = False - if v < 0: - neg = True - v = -v - - s = struct.pack(b">I", bn_bytes(v, have_ext)) - ext = bytearray() - if have_ext: - ext.append(0) - v_bin = bn2bin(v) - if neg: - if have_ext: - ext[0] |= 0x80 - else: - v_bin[0] |= 0x80 - return s + ext + v_bin - -# bitcoin-specific little endian format, with implicit size -def mpi2vch(s): - r = s[4:] # strip size - r = r[::-1] # reverse string, converting BE->LE - return r - -def bn2vch(v): - return bytes(mpi2vch(bn2mpi(v))) diff --git a/test/functional/test_framework/messages.py b/test/functional/test_framework/messages.py index 4f7a9a8b13..285a3fbbf4 100755 --- a/test/functional/test_framework/messages.py +++ b/test/functional/test_framework/messages.py @@ -39,6 +39,7 @@ MAX_LOCATOR_SZ = 101 MAX_BLOCK_BASE_SIZE = 1000000 COIN = 100000000 # 1 btc in satoshis +MAX_MONEY = 21000000 * COIN BIP125_SEQUENCE_NUMBER = 0xfffffffd # Sequence number that is BIP 125 opt-in and BIP 68-opt-out diff --git a/test/functional/test_framework/script.py b/test/functional/test_framework/script.py index 51aa9057f7..f14191fe51 100644 --- a/test/functional/test_framework/script.py +++ b/test/functional/test_framework/script.py @@ -6,21 +6,59 @@ This file is modified from python-bitcoinlib. """ - -from .messages import CTransaction, CTxOut, sha256, hash256, uint256_from_str, ser_uint256, ser_string - import hashlib import struct -from .bignum import bn2vch +from .messages import ( + CTransaction, + CTxOut, + hash256, + ser_string, + ser_uint256, + sha256, + uint256_from_str, +) MAX_SCRIPT_ELEMENT_SIZE = 520 - OPCODE_NAMES = {} def hash160(s): return hashlib.new('ripemd160', sha256(s)).digest() +def bn2vch(v): + """Convert number to bitcoin-specific little endian format.""" + # The top bit is used to indicate the sign of the number. If there + # isn't a spare bit in the bit length, add an extension byte. + have_ext = False + ext = bytearray() + if v.bit_length() > 0: + have_ext = (v.bit_length() & 0x07) == 0 + ext.append(0) + + # Is the number negative? + neg = False + if v < 0: + neg = True + v = -v + + # Convert the int to bytes + v_bin = bytearray() + bytes_len = (v.bit_length() + 7) // 8 + for i in range(bytes_len, 0, -1): + v_bin.append((v >> ((i - 1) * 8)) & 0xff) + + # Add the sign bit if necessary + if neg: + if have_ext: + ext[0] |= 0x80 + else: + v_bin[0] |= 0x80 + + v_bytes = ext + v_bin + # Reverse bytes ordering for LE + v_bytes.reverse() + + return bytes(v_bytes) _opcode_instances = [] class CScriptOp(int): @@ -31,13 +69,13 @@ class CScriptOp(int): def encode_op_pushdata(d): """Encode a PUSHDATA op, returning bytes""" if len(d) < 0x4c: - return b'' + bytes([len(d)]) + d # OP_PUSHDATA + return b'' + bytes([len(d)]) + d # OP_PUSHDATA elif len(d) <= 0xff: - return b'\x4c' + bytes([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 + return b'\x4d' + struct.pack(b'<H', len(d)) + d # OP_PUSHDATA2 elif len(d) <= 0xffffffff: - return b'\x4e' + struct.pack(b'<I', len(d)) + d # OP_PUSHDATA4 + return b'\x4e' + struct.pack(b'<I', len(d)) + d # OP_PUSHDATA4 else: raise ValueError("Data too long to encode in a PUSHDATA op") @@ -50,7 +88,7 @@ class CScriptOp(int): if n == 0: return OP_0 else: - return CScriptOp(OP_1 + n-1) + return CScriptOp(OP_1 + n - 1) def decode_op_n(self): """Decode a small integer opcode, returning an integer""" @@ -60,7 +98,7 @@ class CScriptOp(int): if not (self == OP_0 or OP_1 <= self <= OP_16): raise ValueError('op %r is not an OP_N' % self) - return int(self - OP_1+1) + return int(self - OP_1 + 1) def is_small_int(self): """Return true if the op pushes a small integer to the stack""" @@ -87,7 +125,7 @@ class CScriptOp(int): return _opcode_instances[n] # Populate opcode instance table -for n in range(0xff+1): +for n in range(0xff + 1): CScriptOp(n) @@ -100,7 +138,7 @@ OP_PUSHDATA4 = CScriptOp(0x4e) OP_1NEGATE = CScriptOp(0x4f) OP_RESERVED = CScriptOp(0x50) OP_1 = CScriptOp(0x51) -OP_TRUE=OP_1 +OP_TRUE = OP_1 OP_2 = CScriptOp(0x52) OP_3 = CScriptOp(0x53) OP_4 = CScriptOp(0x54) @@ -232,122 +270,122 @@ OP_PUBKEY = CScriptOp(0xfe) OP_INVALIDOPCODE = CScriptOp(0xff) OPCODE_NAMES.update({ - OP_0 : 'OP_0', - OP_PUSHDATA1 : 'OP_PUSHDATA1', - OP_PUSHDATA2 : 'OP_PUSHDATA2', - OP_PUSHDATA4 : 'OP_PUSHDATA4', - OP_1NEGATE : 'OP_1NEGATE', - OP_RESERVED : 'OP_RESERVED', - OP_1 : 'OP_1', - OP_2 : 'OP_2', - OP_3 : 'OP_3', - OP_4 : 'OP_4', - OP_5 : 'OP_5', - OP_6 : 'OP_6', - OP_7 : 'OP_7', - OP_8 : 'OP_8', - OP_9 : 'OP_9', - OP_10 : 'OP_10', - OP_11 : 'OP_11', - OP_12 : 'OP_12', - OP_13 : 'OP_13', - OP_14 : 'OP_14', - OP_15 : 'OP_15', - OP_16 : 'OP_16', - OP_NOP : 'OP_NOP', - OP_VER : 'OP_VER', - OP_IF : 'OP_IF', - OP_NOTIF : 'OP_NOTIF', - OP_VERIF : 'OP_VERIF', - OP_VERNOTIF : 'OP_VERNOTIF', - OP_ELSE : 'OP_ELSE', - OP_ENDIF : 'OP_ENDIF', - OP_VERIFY : 'OP_VERIFY', - OP_RETURN : 'OP_RETURN', - OP_TOALTSTACK : 'OP_TOALTSTACK', - OP_FROMALTSTACK : 'OP_FROMALTSTACK', - OP_2DROP : 'OP_2DROP', - OP_2DUP : 'OP_2DUP', - OP_3DUP : 'OP_3DUP', - OP_2OVER : 'OP_2OVER', - OP_2ROT : 'OP_2ROT', - OP_2SWAP : 'OP_2SWAP', - OP_IFDUP : 'OP_IFDUP', - OP_DEPTH : 'OP_DEPTH', - OP_DROP : 'OP_DROP', - OP_DUP : 'OP_DUP', - OP_NIP : 'OP_NIP', - OP_OVER : 'OP_OVER', - OP_PICK : 'OP_PICK', - OP_ROLL : 'OP_ROLL', - OP_ROT : 'OP_ROT', - OP_SWAP : 'OP_SWAP', - OP_TUCK : 'OP_TUCK', - OP_CAT : 'OP_CAT', - OP_SUBSTR : 'OP_SUBSTR', - OP_LEFT : 'OP_LEFT', - OP_RIGHT : 'OP_RIGHT', - OP_SIZE : 'OP_SIZE', - OP_INVERT : 'OP_INVERT', - OP_AND : 'OP_AND', - OP_OR : 'OP_OR', - OP_XOR : 'OP_XOR', - OP_EQUAL : 'OP_EQUAL', - OP_EQUALVERIFY : 'OP_EQUALVERIFY', - OP_RESERVED1 : 'OP_RESERVED1', - OP_RESERVED2 : 'OP_RESERVED2', - OP_1ADD : 'OP_1ADD', - OP_1SUB : 'OP_1SUB', - OP_2MUL : 'OP_2MUL', - OP_2DIV : 'OP_2DIV', - OP_NEGATE : 'OP_NEGATE', - OP_ABS : 'OP_ABS', - OP_NOT : 'OP_NOT', - OP_0NOTEQUAL : 'OP_0NOTEQUAL', - OP_ADD : 'OP_ADD', - OP_SUB : 'OP_SUB', - OP_MUL : 'OP_MUL', - OP_DIV : 'OP_DIV', - OP_MOD : 'OP_MOD', - OP_LSHIFT : 'OP_LSHIFT', - OP_RSHIFT : 'OP_RSHIFT', - OP_BOOLAND : 'OP_BOOLAND', - OP_BOOLOR : 'OP_BOOLOR', - OP_NUMEQUAL : 'OP_NUMEQUAL', - OP_NUMEQUALVERIFY : 'OP_NUMEQUALVERIFY', - OP_NUMNOTEQUAL : 'OP_NUMNOTEQUAL', - OP_LESSTHAN : 'OP_LESSTHAN', - OP_GREATERTHAN : 'OP_GREATERTHAN', - OP_LESSTHANOREQUAL : 'OP_LESSTHANOREQUAL', - OP_GREATERTHANOREQUAL : 'OP_GREATERTHANOREQUAL', - OP_MIN : 'OP_MIN', - OP_MAX : 'OP_MAX', - OP_WITHIN : 'OP_WITHIN', - OP_RIPEMD160 : 'OP_RIPEMD160', - OP_SHA1 : 'OP_SHA1', - OP_SHA256 : 'OP_SHA256', - OP_HASH160 : 'OP_HASH160', - OP_HASH256 : 'OP_HASH256', - OP_CODESEPARATOR : 'OP_CODESEPARATOR', - OP_CHECKSIG : 'OP_CHECKSIG', - OP_CHECKSIGVERIFY : 'OP_CHECKSIGVERIFY', - OP_CHECKMULTISIG : 'OP_CHECKMULTISIG', - OP_CHECKMULTISIGVERIFY : 'OP_CHECKMULTISIGVERIFY', - OP_NOP1 : 'OP_NOP1', - OP_CHECKLOCKTIMEVERIFY : 'OP_CHECKLOCKTIMEVERIFY', - OP_CHECKSEQUENCEVERIFY : 'OP_CHECKSEQUENCEVERIFY', - OP_NOP4 : 'OP_NOP4', - OP_NOP5 : 'OP_NOP5', - OP_NOP6 : 'OP_NOP6', - OP_NOP7 : 'OP_NOP7', - OP_NOP8 : 'OP_NOP8', - OP_NOP9 : 'OP_NOP9', - OP_NOP10 : 'OP_NOP10', - OP_SMALLINTEGER : 'OP_SMALLINTEGER', - OP_PUBKEYS : 'OP_PUBKEYS', - OP_PUBKEYHASH : 'OP_PUBKEYHASH', - OP_PUBKEY : 'OP_PUBKEY', - OP_INVALIDOPCODE : 'OP_INVALIDOPCODE', + OP_0: 'OP_0', + OP_PUSHDATA1: 'OP_PUSHDATA1', + OP_PUSHDATA2: 'OP_PUSHDATA2', + OP_PUSHDATA4: 'OP_PUSHDATA4', + OP_1NEGATE: 'OP_1NEGATE', + OP_RESERVED: 'OP_RESERVED', + OP_1: 'OP_1', + OP_2: 'OP_2', + OP_3: 'OP_3', + OP_4: 'OP_4', + OP_5: 'OP_5', + OP_6: 'OP_6', + OP_7: 'OP_7', + OP_8: 'OP_8', + OP_9: 'OP_9', + OP_10: 'OP_10', + OP_11: 'OP_11', + OP_12: 'OP_12', + OP_13: 'OP_13', + OP_14: 'OP_14', + OP_15: 'OP_15', + OP_16: 'OP_16', + OP_NOP: 'OP_NOP', + OP_VER: 'OP_VER', + OP_IF: 'OP_IF', + OP_NOTIF: 'OP_NOTIF', + OP_VERIF: 'OP_VERIF', + OP_VERNOTIF: 'OP_VERNOTIF', + OP_ELSE: 'OP_ELSE', + OP_ENDIF: 'OP_ENDIF', + OP_VERIFY: 'OP_VERIFY', + OP_RETURN: 'OP_RETURN', + OP_TOALTSTACK: 'OP_TOALTSTACK', + OP_FROMALTSTACK: 'OP_FROMALTSTACK', + OP_2DROP: 'OP_2DROP', + OP_2DUP: 'OP_2DUP', + OP_3DUP: 'OP_3DUP', + OP_2OVER: 'OP_2OVER', + OP_2ROT: 'OP_2ROT', + OP_2SWAP: 'OP_2SWAP', + OP_IFDUP: 'OP_IFDUP', + OP_DEPTH: 'OP_DEPTH', + OP_DROP: 'OP_DROP', + OP_DUP: 'OP_DUP', + OP_NIP: 'OP_NIP', + OP_OVER: 'OP_OVER', + OP_PICK: 'OP_PICK', + OP_ROLL: 'OP_ROLL', + OP_ROT: 'OP_ROT', + OP_SWAP: 'OP_SWAP', + OP_TUCK: 'OP_TUCK', + OP_CAT: 'OP_CAT', + OP_SUBSTR: 'OP_SUBSTR', + OP_LEFT: 'OP_LEFT', + OP_RIGHT: 'OP_RIGHT', + OP_SIZE: 'OP_SIZE', + OP_INVERT: 'OP_INVERT', + OP_AND: 'OP_AND', + OP_OR: 'OP_OR', + OP_XOR: 'OP_XOR', + OP_EQUAL: 'OP_EQUAL', + OP_EQUALVERIFY: 'OP_EQUALVERIFY', + OP_RESERVED1: 'OP_RESERVED1', + OP_RESERVED2: 'OP_RESERVED2', + OP_1ADD: 'OP_1ADD', + OP_1SUB: 'OP_1SUB', + OP_2MUL: 'OP_2MUL', + OP_2DIV: 'OP_2DIV', + OP_NEGATE: 'OP_NEGATE', + OP_ABS: 'OP_ABS', + OP_NOT: 'OP_NOT', + OP_0NOTEQUAL: 'OP_0NOTEQUAL', + OP_ADD: 'OP_ADD', + OP_SUB: 'OP_SUB', + OP_MUL: 'OP_MUL', + OP_DIV: 'OP_DIV', + OP_MOD: 'OP_MOD', + OP_LSHIFT: 'OP_LSHIFT', + OP_RSHIFT: 'OP_RSHIFT', + OP_BOOLAND: 'OP_BOOLAND', + OP_BOOLOR: 'OP_BOOLOR', + OP_NUMEQUAL: 'OP_NUMEQUAL', + OP_NUMEQUALVERIFY: 'OP_NUMEQUALVERIFY', + OP_NUMNOTEQUAL: 'OP_NUMNOTEQUAL', + OP_LESSTHAN: 'OP_LESSTHAN', + OP_GREATERTHAN: 'OP_GREATERTHAN', + OP_LESSTHANOREQUAL: 'OP_LESSTHANOREQUAL', + OP_GREATERTHANOREQUAL: 'OP_GREATERTHANOREQUAL', + OP_MIN: 'OP_MIN', + OP_MAX: 'OP_MAX', + OP_WITHIN: 'OP_WITHIN', + OP_RIPEMD160: 'OP_RIPEMD160', + OP_SHA1: 'OP_SHA1', + OP_SHA256: 'OP_SHA256', + OP_HASH160: 'OP_HASH160', + OP_HASH256: 'OP_HASH256', + OP_CODESEPARATOR: 'OP_CODESEPARATOR', + OP_CHECKSIG: 'OP_CHECKSIG', + OP_CHECKSIGVERIFY: 'OP_CHECKSIGVERIFY', + OP_CHECKMULTISIG: 'OP_CHECKMULTISIG', + OP_CHECKMULTISIGVERIFY: 'OP_CHECKMULTISIGVERIFY', + OP_NOP1: 'OP_NOP1', + OP_CHECKLOCKTIMEVERIFY: 'OP_CHECKLOCKTIMEVERIFY', + OP_CHECKSEQUENCEVERIFY: 'OP_CHECKSEQUENCEVERIFY', + OP_NOP4: 'OP_NOP4', + OP_NOP5: 'OP_NOP5', + OP_NOP6: 'OP_NOP6', + OP_NOP7: 'OP_NOP7', + OP_NOP8: 'OP_NOP8', + OP_NOP9: 'OP_NOP9', + OP_NOP10: 'OP_NOP10', + OP_SMALLINTEGER: 'OP_SMALLINTEGER', + OP_PUBKEYS: 'OP_PUBKEYS', + OP_PUBKEYHASH: 'OP_PUBKEYHASH', + OP_PUBKEY: 'OP_PUBKEY', + OP_INVALIDOPCODE: 'OP_INVALIDOPCODE', }) class CScriptInvalidError(Exception): @@ -392,10 +430,10 @@ class CScriptNum: if len(value) == 0: return result for i, byte in enumerate(value): - result |= int(byte) << 8*i + result |= int(byte) << 8 * i if value[-1] >= 0x80: # Mask for all but the highest result bit - num_mask = (2**(len(value)*8) - 1) >> 1 + num_mask = (2**(len(value) * 8) - 1) >> 1 result &= num_mask result *= -1 return result @@ -493,21 +531,20 @@ class CScript(bytes): pushdata_type = 'PUSHDATA2' if i + 1 >= len(self): raise CScriptInvalidError('PUSHDATA2: missing data length') - datasize = self[i] + (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 = self[i] + (self[i+1] << 8) + (self[i+2] << 16) + (self[i+3] << 24) + datasize = self[i] + (self[i + 1] << 8) + (self[i + 2] << 16) + (self[i + 3] << 24) i += 4 else: - assert False # shouldn't happen - + assert False # shouldn't happen - data = bytes(self[i:i+datasize]) + data = bytes(self[i:i + datasize]) # Check for truncation if len(data) < datasize: diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py index 2036d20852..9d87b5638c 100755 --- a/test/functional/test_runner.py +++ b/test/functional/test_runner.py @@ -196,6 +196,7 @@ BASE_SCRIPTS = [ 'wallet_fallbackfee.py', 'rpc_dumptxoutset.py', 'feature_minchainwork.py', + 'rpc_estimatefee.py', 'rpc_getblockstats.py', 'wallet_create_tx.py', 'p2p_fingerprint.py', @@ -217,6 +218,7 @@ BASE_SCRIPTS = [ 'feature_config_args.py', 'rpc_getaddressinfo_labels_purpose_deprecation.py', 'rpc_getaddressinfo_label_deprecation.py', + 'rpc_getdescriptorinfo.py', 'rpc_help.py', 'feature_help.py', 'feature_shutdown.py', diff --git a/test/functional/wallet_bumpfee.py b/test/functional/wallet_bumpfee.py index 4eb0d19a4f..336e246e33 100755 --- a/test/functional/wallet_bumpfee.py +++ b/test/functional/wallet_bumpfee.py @@ -71,28 +71,29 @@ class BumpFeeTest(BitcoinTestFramework): test_simple_bumpfee_succeeds(self, "default", rbf_node, peer_node, dest_address) test_simple_bumpfee_succeeds(self, "fee_rate", rbf_node, peer_node, dest_address) test_feerate_args(self, rbf_node, peer_node, dest_address) - test_segwit_bumpfee_succeeds(rbf_node, dest_address) - test_nonrbf_bumpfee_fails(peer_node, dest_address) - test_notmine_bumpfee_fails(rbf_node, peer_node, dest_address) - test_bumpfee_with_descendant_fails(rbf_node, rbf_node_address, dest_address) - test_small_output_fails(rbf_node, dest_address) - test_dust_to_fee(rbf_node, dest_address) - test_settxfee(rbf_node, dest_address) + test_segwit_bumpfee_succeeds(self, rbf_node, dest_address) + test_nonrbf_bumpfee_fails(self, peer_node, dest_address) + test_notmine_bumpfee_fails(self, rbf_node, peer_node, dest_address) + test_bumpfee_with_descendant_fails(self, rbf_node, rbf_node_address, dest_address) + test_small_output_fails(self, rbf_node, dest_address) + test_dust_to_fee(self, rbf_node, dest_address) + test_settxfee(self, rbf_node, dest_address) test_watchonly_psbt(self, peer_node, rbf_node, dest_address) - test_rebumping(rbf_node, dest_address) - test_rebumping_not_replaceable(rbf_node, dest_address) - test_unconfirmed_not_spendable(rbf_node, rbf_node_address) - test_bumpfee_metadata(rbf_node, dest_address) - test_locked_wallet_fails(rbf_node, dest_address) - test_change_script_match(rbf_node, dest_address) + test_rebumping(self, rbf_node, dest_address) + test_rebumping_not_replaceable(self, rbf_node, dest_address) + test_unconfirmed_not_spendable(self, rbf_node, rbf_node_address) + test_bumpfee_metadata(self, rbf_node, dest_address) + test_locked_wallet_fails(self, rbf_node, dest_address) + test_change_script_match(self, rbf_node, dest_address) test_maxtxfee_fails(self, rbf_node, dest_address) # These tests wipe out a number of utxos that are expected in other tests - test_small_output_with_feerate_succeeds(rbf_node, dest_address) - test_no_more_inputs_fails(rbf_node, dest_address) + test_small_output_with_feerate_succeeds(self, rbf_node, dest_address) + test_no_more_inputs_fails(self, rbf_node, dest_address) self.log.info("Success") def test_simple_bumpfee_succeeds(self, mode, rbf_node, peer_node, dest_address): + self.log.info('Test simple bumpfee') rbfid = spend_one_input(rbf_node, dest_address) rbftx = rbf_node.gettransaction(rbfid) self.sync_mempools((rbf_node, peer_node)) @@ -119,6 +120,7 @@ def test_simple_bumpfee_succeeds(self, mode, rbf_node, peer_node, dest_address): assert_equal(bumpedwtx["replaces_txid"], rbfid) def test_feerate_args(self, rbf_node, peer_node, dest_address): + self.log.info('Test feerate args') rbfid = spend_one_input(rbf_node, dest_address) self.sync_mempools((rbf_node, peer_node)) assert rbfid in rbf_node.getrawmempool() and rbfid in peer_node.getrawmempool() @@ -135,7 +137,8 @@ def test_feerate_args(self, rbf_node, peer_node, dest_address): assert_raises_rpc_error(-4, "is too high (cannot be higher than", rbf_node.bumpfee, rbfid, {"fee_rate":1}) -def test_segwit_bumpfee_succeeds(rbf_node, dest_address): +def test_segwit_bumpfee_succeeds(self, rbf_node, dest_address): + self.log.info('Test that segwit-sourcing bumpfee works') # Create a transaction with segwit output, then create an RBF transaction # which spends it, and make sure bumpfee can be called on it. @@ -165,14 +168,14 @@ def test_segwit_bumpfee_succeeds(rbf_node, dest_address): assert rbfid not in rbf_node.getrawmempool() -def test_nonrbf_bumpfee_fails(peer_node, dest_address): - # cannot replace a non RBF transaction (from node which did not enable RBF) +def test_nonrbf_bumpfee_fails(self, peer_node, dest_address): + self.log.info('Test that we cannot replace a non RBF transaction') not_rbfid = peer_node.sendtoaddress(dest_address, Decimal("0.00090000")) assert_raises_rpc_error(-4, "not BIP 125 replaceable", peer_node.bumpfee, not_rbfid) -def test_notmine_bumpfee_fails(rbf_node, peer_node, dest_address): - # cannot bump fee unless the tx has only inputs that we own. +def test_notmine_bumpfee_fails(self, rbf_node, peer_node, dest_address): + self.log.info('Test that it cannot bump fee if non-owned inputs are included') # here, the rbftx has a peer_node coin and then adds a rbf_node input # Note that this test depends upon the RPC code checking input ownership prior to change outputs # (since it can't use fundrawtransaction, it lacks a proper change output) @@ -192,8 +195,8 @@ def test_notmine_bumpfee_fails(rbf_node, peer_node, dest_address): rbf_node.bumpfee, rbfid) -def test_bumpfee_with_descendant_fails(rbf_node, rbf_node_address, dest_address): - # cannot bump fee if the transaction has a descendant +def test_bumpfee_with_descendant_fails(self, rbf_node, rbf_node_address, dest_address): + self.log.info('Test that fee cannot be bumped when it has descendant') # parent is send-to-self, so we don't have to check which output is change when creating the child tx parent_id = spend_one_input(rbf_node, rbf_node_address) tx = rbf_node.createrawtransaction([{"txid": parent_id, "vout": 0}], {dest_address: 0.00020000}) @@ -201,7 +204,8 @@ def test_bumpfee_with_descendant_fails(rbf_node, rbf_node_address, dest_address) rbf_node.sendrawtransaction(tx["hex"]) assert_raises_rpc_error(-8, "Transaction has descendants in the wallet", rbf_node.bumpfee, parent_id) -def test_small_output_fails(rbf_node, dest_address): +def test_small_output_fails(self, rbf_node, dest_address): + self.log.info('Test totalFee bump with small output fails') # cannot bump fee with a too-small output rbfid = spend_one_input(rbf_node, dest_address) rbf_node.bumpfee(rbfid, {"totalFee": 50000}) @@ -209,7 +213,8 @@ def test_small_output_fails(rbf_node, dest_address): rbfid = spend_one_input(rbf_node, dest_address) assert_raises_rpc_error(-4, "Change output is too small", rbf_node.bumpfee, rbfid, {"totalFee": 50001}) -def test_small_output_with_feerate_succeeds(rbf_node, dest_address): +def test_small_output_with_feerate_succeeds(self, rbf_node, dest_address): + self.log.info('Testing small output with feerate bump succeeds') # Make sure additional inputs exist rbf_node.generatetoaddress(101, rbf_node.getnewaddress()) @@ -217,9 +222,9 @@ def test_small_output_with_feerate_succeeds(rbf_node, dest_address): input_list = rbf_node.getrawtransaction(rbfid, 1)["vin"] assert_equal(len(input_list), 1) original_txin = input_list[0] - # Keep bumping until we out-spend change output + self.log.info('Keep bumping until transaction fee out-spends non-destination value') tx_fee = 0 - while tx_fee < Decimal("0.0005"): + while True: input_list = rbf_node.getrawtransaction(rbfid, 1)["vin"] new_item = list(input_list)[0] assert_equal(len(input_list), 1) @@ -231,7 +236,11 @@ def test_small_output_with_feerate_succeeds(rbf_node, dest_address): assert rbfid not in raw_pool assert rbfid_new in raw_pool rbfid = rbfid_new - tx_fee = rbfid_new_details["origfee"] + tx_fee = rbfid_new_details["fee"] + + # Total value from input not going to destination + if tx_fee > Decimal('0.00050000'): + break # input(s) have been added final_input_list = rbf_node.getrawtransaction(rbfid, 1)["vin"] @@ -244,8 +253,8 @@ def test_small_output_with_feerate_succeeds(rbf_node, dest_address): rbf_node.generatetoaddress(1, rbf_node.getnewaddress()) assert_equal(rbf_node.gettransaction(rbfid)["confirmations"], 1) -def test_dust_to_fee(rbf_node, dest_address): - # check that if output is reduced to dust, it will be converted to fee +def test_dust_to_fee(self, rbf_node, dest_address): + self.log.info('Test that bumped output that is dust is dropped to fee') # the bumped tx sets fee=49,900, but it converts to 50,000 rbfid = spend_one_input(rbf_node, dest_address) fulltx = rbf_node.getrawtransaction(rbfid, 1) @@ -257,7 +266,8 @@ def test_dust_to_fee(rbf_node, dest_address): assert_equal(len(full_bumped_tx["vout"]), 1) # change output is eliminated -def test_settxfee(rbf_node, dest_address): +def test_settxfee(self, rbf_node, dest_address): + self.log.info('Test settxfee') assert_raises_rpc_error(-8, "txfee cannot be less than min relay tx fee", rbf_node.settxfee, Decimal('0.000005')) assert_raises_rpc_error(-8, "txfee cannot be less than wallet min fee", rbf_node.settxfee, Decimal('0.000015')) # check that bumpfee reacts correctly to the use of settxfee (paytxfee) @@ -272,17 +282,19 @@ def test_settxfee(rbf_node, dest_address): rbf_node.settxfee(Decimal("0.00000000")) # unset paytxfee -def test_maxtxfee_fails(test, rbf_node, dest_address): +def test_maxtxfee_fails(self, rbf_node, dest_address): + self.log.info('Test that bumpfee fails when it hits -matxfee') # size of bumped transaction (p2wpkh, 1 input, 2 outputs): 141 vbytes # expected bumping feerate of 20 sats/vbyte => 141*20 sats = 0.00002820 btc - test.restart_node(1, ['-maxtxfee=0.000025'] + test.extra_args[1]) + self.restart_node(1, ['-maxtxfee=0.000025'] + self.extra_args[1]) rbf_node.walletpassphrase(WALLET_PASSPHRASE, WALLET_PASSPHRASE_TIMEOUT) rbfid = spend_one_input(rbf_node, dest_address) assert_raises_rpc_error(-4, "Unable to create transaction: Fee exceeds maximum configured by -maxtxfee", rbf_node.bumpfee, rbfid) - test.restart_node(1, test.extra_args[1]) + self.restart_node(1, self.extra_args[1]) rbf_node.walletpassphrase(WALLET_PASSPHRASE, WALLET_PASSPHRASE_TIMEOUT) -def test_watchonly_psbt(test, peer_node, rbf_node, dest_address): +def test_watchonly_psbt(self, peer_node, rbf_node, dest_address): + self.log.info('Test that PSBT is returned for bumpfee in watchonly wallets') priv_rec_desc = "wpkh([00000001/84'/1'/0']tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/0/*)#rweraev0" pub_rec_desc = rbf_node.getdescriptorinfo(priv_rec_desc)["descriptor"] priv_change_desc = "wpkh([00000001/84'/1'/0']tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/1/*)#j6uzqvuh" @@ -334,7 +346,7 @@ def test_watchonly_psbt(test, peer_node, rbf_node, dest_address): funding_address2 = watcher.getnewaddress(address_type='bech32') peer_node.sendmany("", {funding_address1: 0.001, funding_address2: 0.001}) peer_node.generate(1) - test.sync_all() + self.sync_all() # Create single-input PSBT for transaction to be bumped psbt = watcher.walletcreatefundedpsbt([], {dest_address:0.0005}, 0, {"feeRate": 0.00001}, True)['psbt'] @@ -363,24 +375,24 @@ def test_watchonly_psbt(test, peer_node, rbf_node, dest_address): rbf_node.unloadwallet("watcher") rbf_node.unloadwallet("signer") -def test_rebumping(rbf_node, dest_address): - # check that re-bumping the original tx fails, but bumping the bumper succeeds +def test_rebumping(self, rbf_node, dest_address): + self.log.info('Test that re-bumping the original tx fails, but bumping successor works') rbfid = spend_one_input(rbf_node, dest_address) bumped = rbf_node.bumpfee(rbfid, {"totalFee": 2000}) assert_raises_rpc_error(-4, "already bumped", rbf_node.bumpfee, rbfid, {"totalFee": 3000}) rbf_node.bumpfee(bumped["txid"], {"totalFee": 3000}) -def test_rebumping_not_replaceable(rbf_node, dest_address): - # check that re-bumping a non-replaceable bump tx fails +def test_rebumping_not_replaceable(self, rbf_node, dest_address): + self.log.info('Test that re-bumping non-replaceable fails') rbfid = spend_one_input(rbf_node, dest_address) bumped = rbf_node.bumpfee(rbfid, {"totalFee": 10000, "replaceable": False}) assert_raises_rpc_error(-4, "Transaction is not BIP 125 replaceable", rbf_node.bumpfee, bumped["txid"], {"totalFee": 20000}) -def test_unconfirmed_not_spendable(rbf_node, rbf_node_address): - # check that unconfirmed outputs from bumped transactions are not spendable +def test_unconfirmed_not_spendable(self, rbf_node, rbf_node_address): + self.log.info('Test that unconfirmed outputs from bumped txns are not spendable') rbfid = spend_one_input(rbf_node, rbf_node_address) rbftx = rbf_node.gettransaction(rbfid)["hex"] assert rbfid in rbf_node.getrawmempool() @@ -418,7 +430,8 @@ def test_unconfirmed_not_spendable(rbf_node, rbf_node_address): if t["txid"] == rbfid and t["address"] == rbf_node_address and t["spendable"]), 1) -def test_bumpfee_metadata(rbf_node, dest_address): +def test_bumpfee_metadata(self, rbf_node, dest_address): + self.log.info('Test that bumped txn metadata persists to new txn record') assert(rbf_node.getbalance() < 49) rbf_node.generatetoaddress(101, rbf_node.getnewaddress()) rbfid = rbf_node.sendtoaddress(dest_address, 49, "comment value", "to value") @@ -428,15 +441,17 @@ def test_bumpfee_metadata(rbf_node, dest_address): assert_equal(bumped_wtx["to"], "to value") -def test_locked_wallet_fails(rbf_node, dest_address): +def test_locked_wallet_fails(self, rbf_node, dest_address): + self.log.info('Test that locked wallet cannot bump txn') rbfid = spend_one_input(rbf_node, dest_address) rbf_node.walletlock() assert_raises_rpc_error(-13, "Please enter the wallet passphrase with walletpassphrase first.", rbf_node.bumpfee, rbfid) rbf_node.walletpassphrase(WALLET_PASSPHRASE, WALLET_PASSPHRASE_TIMEOUT) -def test_change_script_match(rbf_node, dest_address): - """Test that the same change addresses is used for the replacement transaction when possible.""" +def test_change_script_match(self, rbf_node, dest_address): + self.log.info('Test that the same change addresses is used for the replacement transaction when possible.') + def get_change_address(tx): tx_details = rbf_node.getrawtransaction(tx, 1) txout_addresses = [txout['scriptPubKey']['addresses'][0] for txout in tx_details["vout"]] @@ -480,7 +495,8 @@ def submit_block_with_tx(node, tx): node.submitblock(block.serialize().hex()) return block -def test_no_more_inputs_fails(rbf_node, dest_address): +def test_no_more_inputs_fails(self, rbf_node, dest_address): + self.log.info('Test that bumpfee fails when there are no available confirmed outputs') # feerate rbf requires confirmed outputs when change output doesn't exist or is insufficient rbf_node.generatetoaddress(1, dest_address) # spend all funds, no change output diff --git a/test/functional/wallet_createwallet.py b/test/functional/wallet_createwallet.py index 048b3127ff..b24d312e27 100755 --- a/test/functional/wallet_createwallet.py +++ b/test/functional/wallet_createwallet.py @@ -79,7 +79,7 @@ class CreateWalletTest(BitcoinTestFramework): assert_raises_rpc_error(-4, "Error: This wallet has no available keys", w4.getnewaddress) assert_raises_rpc_error(-4, "Error: This wallet has no available keys", w4.getrawchangeaddress) # Now set a seed and it should work. Wallet should also be encrypted - w4.walletpassphrase('pass', 2) + w4.walletpassphrase('pass', 60) w4.sethdseed() w4.getnewaddress() w4.getrawchangeaddress() @@ -99,7 +99,7 @@ class CreateWalletTest(BitcoinTestFramework): self.nodes[0].createwallet(wallet_name='wblank', disable_private_keys=False, blank=True, passphrase='thisisapassphrase') wblank = node.get_wallet_rpc('wblank') assert_raises_rpc_error(-13, "Error: Please enter the wallet passphrase with walletpassphrase first.", wblank.signmessage, "needanargument", "test") - wblank.walletpassphrase('thisisapassphrase', 10) + wblank.walletpassphrase('thisisapassphrase', 60) assert_raises_rpc_error(-4, "Error: This wallet has no available keys", wblank.getnewaddress) assert_raises_rpc_error(-4, "Error: This wallet has no available keys", wblank.getrawchangeaddress) @@ -108,7 +108,7 @@ class CreateWalletTest(BitcoinTestFramework): self.nodes[0].createwallet(wallet_name='w6', disable_private_keys=False, blank=False, passphrase='thisisapassphrase') w6 = node.get_wallet_rpc('w6') assert_raises_rpc_error(-13, "Error: Please enter the wallet passphrase with walletpassphrase first.", w6.signmessage, "needanargument", "test") - w6.walletpassphrase('thisisapassphrase', 10) + w6.walletpassphrase('thisisapassphrase', 60) w6.signmessage(w6.getnewaddress('', 'legacy'), "test") w6.keypoolrefill(1) # There should only be 1 key @@ -119,12 +119,12 @@ class CreateWalletTest(BitcoinTestFramework): resp = self.nodes[0].createwallet(wallet_name='w7', disable_private_keys=False, blank=False, passphrase='') assert_equal(resp['warning'], 'Empty string given as passphrase, wallet will not be encrypted.') w7 = node.get_wallet_rpc('w7') - assert_raises_rpc_error(-15, 'Error: running with an unencrypted wallet, but walletpassphrase was called.', w7.walletpassphrase, '', 10) + assert_raises_rpc_error(-15, 'Error: running with an unencrypted wallet, but walletpassphrase was called.', w7.walletpassphrase, '', 60) self.log.info('Test making a wallet with avoid reuse flag') self.nodes[0].createwallet('w8', False, False, '', True) # Use positional arguments to check for bug where avoid_reuse could not be set for wallets without needing them to be encrypted w8 = node.get_wallet_rpc('w8') - assert_raises_rpc_error(-15, 'Error: running with an unencrypted wallet, but walletpassphrase was called.', w7.walletpassphrase, '', 10) + assert_raises_rpc_error(-15, 'Error: running with an unencrypted wallet, but walletpassphrase was called.', w7.walletpassphrase, '', 60) assert_equal(w8.getwalletinfo()["avoid_reuse"], True) self.log.info('Using a passphrase with private keys disabled returns error') diff --git a/test/functional/wallet_groups.py b/test/functional/wallet_groups.py index f2fa1d3e40..261a43472b 100755 --- a/test/functional/wallet_groups.py +++ b/test/functional/wallet_groups.py @@ -11,12 +11,13 @@ from test_framework.util import ( assert_equal, ) + class WalletGroupTest(BitcoinTestFramework): def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 3 self.extra_args = [[], [], ['-avoidpartialspends']] - self.rpc_timeout = 240 + self.rpc_timeout = 480 def skip_test_if_missing_module(self): self.skip_if_no_wallet() @@ -87,5 +88,6 @@ class WalletGroupTest(BitcoinTestFramework): # is way too big. assert self.nodes[2].sendtoaddress(address=addr2[0], amount=5) + if __name__ == '__main__': - WalletGroupTest().main () + WalletGroupTest().main() diff --git a/test/functional/wallet_listsinceblock.py b/test/functional/wallet_listsinceblock.py index 6f248c9bd3..229eda9806 100755 --- a/test/functional/wallet_listsinceblock.py +++ b/test/functional/wallet_listsinceblock.py @@ -2,7 +2,7 @@ # Copyright (c) 2017-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 listsincelast RPC.""" +"""Test the listsinceblock RPC.""" from test_framework.test_framework import BitcoinTestFramework from test_framework.messages import BIP125_SEQUENCE_NUMBER @@ -38,6 +38,7 @@ class ListSinceBlockTest(BitcoinTestFramework): self.double_spends_filtered() def test_no_blockhash(self): + self.log.info("Test no blockhash") txid = self.nodes[2].sendtoaddress(self.nodes[0].getnewaddress(), 1) blockhash, = self.nodes[2].generate(1) blockheight = self.nodes[2].getblockheader(blockhash)['height'] @@ -63,6 +64,7 @@ class ListSinceBlockTest(BitcoinTestFramework): "transactions": txs}) def test_invalid_blockhash(self): + self.log.info("Test invalid blockhash") assert_raises_rpc_error(-5, "Block not found", self.nodes[0].listsinceblock, "42759cde25462784395a337460bde75f58e73d3f08bd31fdc3507cbac856a2c4") assert_raises_rpc_error(-5, "Block not found", self.nodes[0].listsinceblock, @@ -100,6 +102,7 @@ class ListSinceBlockTest(BitcoinTestFramework): This test only checks that [tx0] is present. ''' + self.log.info("Test reorg") # Split network into two self.split_network() @@ -110,7 +113,7 @@ class ListSinceBlockTest(BitcoinTestFramework): # generate on both sides lastblockhash = self.nodes[1].generate(6)[5] self.nodes[2].generate(7) - self.log.info('lastblockhash=%s' % (lastblockhash)) + self.log.debug('lastblockhash={}'.format(lastblockhash)) self.sync_all(self.nodes[:2]) self.sync_all(self.nodes[2:]) @@ -155,6 +158,7 @@ class ListSinceBlockTest(BitcoinTestFramework): until the fork point, and to include all transactions that relate to the node wallet. ''' + self.log.info("Test double spend") self.sync_all() @@ -234,6 +238,7 @@ class ListSinceBlockTest(BitcoinTestFramework): 3. It is listed with a confirmation count of 2 (bb3, bb4), not 3 (aa1, aa2, aa3). ''' + self.log.info("Test double send") self.sync_all() @@ -302,6 +307,7 @@ class ListSinceBlockTest(BitcoinTestFramework): `listsinceblock` was returning conflicted transactions even if they occurred before the specified cutoff blockhash ''' + self.log.info("Test spends filtered") spending_node = self.nodes[2] dest_address = spending_node.getnewaddress() diff --git a/test/functional/wallet_multiwallet.py b/test/functional/wallet_multiwallet.py index f2fa41b647..78ead514a5 100755 --- a/test/functional/wallet_multiwallet.py +++ b/test/functional/wallet_multiwallet.py @@ -24,6 +24,7 @@ class MultiWalletTest(BitcoinTestFramework): def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 2 + self.rpc_timeout = 120 def skip_test_if_missing_module(self): self.skip_if_no_wallet() diff --git a/test/functional/wallet_resendwallettransactions.py b/test/functional/wallet_resendwallettransactions.py index 91d26e9cb3..d122e3db52 100755 --- a/test/functional/wallet_resendwallettransactions.py +++ b/test/functional/wallet_resendwallettransactions.py @@ -12,6 +12,7 @@ from test_framework.mininode import P2PInterface, mininode_lock from test_framework.test_framework import BitcoinTestFramework from test_framework.util import assert_equal, wait_until + class P2PStoreTxInvs(P2PInterface): def __init__(self): super().__init__() @@ -24,6 +25,7 @@ class P2PStoreTxInvs(P2PInterface): # save txid self.tx_invs_received[i.hash] += 1 + class ResendWalletTransactionsTest(BitcoinTestFramework): def set_test_params(self): self.num_nodes = 1 @@ -63,6 +65,7 @@ class ResendWalletTransactionsTest(BitcoinTestFramework): node.submitblock(ToHex(block)) # Transaction should not be rebroadcast + node.syncwithvalidationinterfacequeue() node.p2ps[1].sync_with_ping() assert_equal(node.p2ps[1].tx_invs_received[txid], 0) @@ -72,5 +75,6 @@ class ResendWalletTransactionsTest(BitcoinTestFramework): node.setmocktime(rebroadcast_time) wait_until(lambda: node.p2ps[1].tx_invs_received[txid] >= 1, lock=mininode_lock) + if __name__ == '__main__': ResendWalletTransactionsTest().main() diff --git a/test/fuzz/test_runner.py b/test/fuzz/test_runner.py index 1492932f2a..1786c39c36 100755 --- a/test/fuzz/test_runner.py +++ b/test/fuzz/test_runner.py @@ -12,10 +12,6 @@ import sys import subprocess import logging -# Fuzzers known to lack a seed corpus in https://github.com/bitcoin-core/qa-assets/tree/master/fuzz_seed_corpus -FUZZERS_MISSING_CORPORA = [ -] - def main(): parser = argparse.ArgumentParser( @@ -96,6 +92,20 @@ def main(): logging.info("{} of {} detected fuzz target(s) selected: {}".format(len(test_list_selection), len(test_list_all), " ".join(test_list_selection))) + test_list_seedless = [] + for t in test_list_selection: + corpus_path = os.path.join(args.seed_dir, t) + if not os.path.exists(corpus_path) or len(os.listdir(corpus_path)) == 0: + test_list_seedless.append(t) + test_list_seedless.sort() + if test_list_seedless: + logging.info( + "Fuzzing harnesses lacking a seed corpus: {}".format( + " ".join(test_list_seedless) + ) + ) + logging.info("Please consider adding a fuzz seed corpus at https://github.com/bitcoin-core/qa-assets") + try: help_output = subprocess.run( args=[ @@ -149,8 +159,7 @@ def merge_inputs(*, corpus, test_list, build_dir, merge_dir): def run_once(*, corpus, test_list, build_dir, use_valgrind): for t in test_list: corpus_path = os.path.join(corpus, t) - if t in FUZZERS_MISSING_CORPORA: - os.makedirs(corpus_path, exist_ok=True) + os.makedirs(corpus_path, exist_ok=True) args = [ os.path.join(build_dir, 'src', 'test', 'fuzz', t), '-runs=1', |