aboutsummaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rw-r--r--test/README.md2
-rw-r--r--test/functional/README.md8
-rw-r--r--test/functional/data/invalid_txs.py30
-rwxr-xr-xtest/functional/feature_assumevalid.py4
-rwxr-xr-xtest/functional/feature_block.py14
-rwxr-xr-xtest/functional/feature_cltv.py2
-rwxr-xr-xtest/functional/feature_dersig.py3
-rwxr-xr-xtest/functional/feature_fee_estimation.py17
-rwxr-xr-xtest/functional/feature_segwit.py1
-rwxr-xr-xtest/functional/mempool_accept.py5
-rwxr-xr-xtest/functional/mempool_reorg.py5
-rwxr-xr-xtest/functional/p2p_segwit.py5
-rw-r--r--test/functional/rpc_estimatefee.py51
-rwxr-xr-xtest/functional/rpc_getdescriptorinfo.py65
-rw-r--r--test/functional/test_framework/bignum.py58
-rwxr-xr-xtest/functional/test_framework/messages.py1
-rw-r--r--test/functional/test_framework/script.py309
-rwxr-xr-xtest/functional/test_runner.py2
-rwxr-xr-xtest/functional/wallet_bumpfee.py108
-rwxr-xr-xtest/functional/wallet_createwallet.py10
-rwxr-xr-xtest/functional/wallet_groups.py6
-rwxr-xr-xtest/functional/wallet_listsinceblock.py10
-rwxr-xr-xtest/functional/wallet_multiwallet.py1
-rwxr-xr-xtest/functional/wallet_resendwallettransactions.py4
-rwxr-xr-xtest/fuzz/test_runner.py21
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',