aboutsummaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rwxr-xr-xtest/functional/feature_assumevalid.py18
-rwxr-xr-xtest/functional/feature_fee_estimation.py39
-rwxr-xr-xtest/functional/feature_maxtipage.py23
-rwxr-xr-xtest/functional/feature_pruning.py25
-rwxr-xr-xtest/functional/feature_taproot.py2
-rwxr-xr-xtest/functional/interface_bitcoin_cli.py4
-rwxr-xr-xtest/functional/interface_rpc.py2
-rwxr-xr-xtest/functional/rpc_packages.py1
-rwxr-xr-xtest/functional/rpc_rawtransaction.py81
-rw-r--r--test/functional/test_framework/blocktools.py5
-rwxr-xr-xtest/functional/test_framework/test_framework.py8
-rw-r--r--test/functional/test_framework/test_shell.py3
-rw-r--r--test/functional/test_framework/wallet.py4
-rwxr-xr-xtest/functional/test_runner.py103
-rwxr-xr-xtest/functional/wallet_import_rescan.py30
-rwxr-xr-xtest/functional/wallet_keypool.py3
-rwxr-xr-xtest/functional/wallet_migration.py30
-rwxr-xr-xtest/functional/wallet_pruning.py158
-rwxr-xr-xtest/functional/wallet_signer.py23
-rwxr-xr-xtest/lint/check-doc.py2
-rw-r--r--test/lint/spelling.ignore-words.txt1
21 files changed, 424 insertions, 141 deletions
diff --git a/test/functional/feature_assumevalid.py b/test/functional/feature_assumevalid.py
index 67cacaa9ce..482c29c994 100755
--- a/test/functional/feature_assumevalid.py
+++ b/test/functional/feature_assumevalid.py
@@ -83,8 +83,6 @@ class AssumeValidTest(BitcoinTestFramework):
break
def run_test(self):
- p2p0 = self.nodes[0].add_p2p_connection(BaseNode())
-
# Build the blockchain
self.tip = int(self.nodes[0].getbestblockhash(), 16)
self.block_time = self.nodes[0].getblock(self.nodes[0].getbestblockhash())['time'] + 1
@@ -139,28 +137,23 @@ class AssumeValidTest(BitcoinTestFramework):
self.block_time += 1
height += 1
- self.nodes[0].disconnect_p2ps()
-
# Start node1 and node2 with assumevalid so they accept a block with a bad signature.
self.start_node(1, extra_args=["-assumevalid=" + hex(block102.sha256)])
self.start_node(2, extra_args=["-assumevalid=" + hex(block102.sha256)])
p2p0 = self.nodes[0].add_p2p_connection(BaseNode())
- p2p1 = self.nodes[1].add_p2p_connection(BaseNode())
- p2p2 = self.nodes[2].add_p2p_connection(BaseNode())
-
- # send header lists to all three nodes
p2p0.send_header_for_blocks(self.blocks[0:2000])
p2p0.send_header_for_blocks(self.blocks[2000:])
- p2p1.send_header_for_blocks(self.blocks[0:2000])
- p2p1.send_header_for_blocks(self.blocks[2000:])
- p2p2.send_header_for_blocks(self.blocks[0:200])
# Send blocks to node0. Block 102 will be rejected.
self.send_blocks_until_disconnected(p2p0)
self.wait_until(lambda: self.nodes[0].getblockcount() >= COINBASE_MATURITY + 1)
assert_equal(self.nodes[0].getblockcount(), COINBASE_MATURITY + 1)
+ p2p1 = self.nodes[1].add_p2p_connection(BaseNode())
+ p2p1.send_header_for_blocks(self.blocks[0:2000])
+ p2p1.send_header_for_blocks(self.blocks[2000:])
+
# Send all blocks to node1. All blocks will be accepted.
for i in range(2202):
p2p1.send_message(msg_block(self.blocks[i]))
@@ -168,6 +161,9 @@ class AssumeValidTest(BitcoinTestFramework):
p2p1.sync_with_ping(960)
assert_equal(self.nodes[1].getblock(self.nodes[1].getbestblockhash())['height'], 2202)
+ p2p2 = self.nodes[2].add_p2p_connection(BaseNode())
+ p2p2.send_header_for_blocks(self.blocks[0:200])
+
# Send blocks to node2. Block 102 will be rejected.
self.send_blocks_until_disconnected(p2p2)
self.wait_until(lambda: self.nodes[2].getblockcount() >= COINBASE_MATURITY + 1)
diff --git a/test/functional/feature_fee_estimation.py b/test/functional/feature_fee_estimation.py
index 0357a6f281..619f3c08ea 100755
--- a/test/functional/feature_fee_estimation.py
+++ b/test/functional/feature_fee_estimation.py
@@ -23,7 +23,7 @@ from test_framework.wallet import MiniWallet
def small_txpuzzle_randfee(
- wallet, from_node, conflist, unconflist, amount, min_fee, fee_increment
+ wallet, from_node, conflist, unconflist, amount, min_fee, fee_increment, batch_reqs
):
"""Create and send a transaction with a random fee using MiniWallet.
@@ -57,8 +57,11 @@ def small_txpuzzle_randfee(
tx.vout[0].nValue = int((total_in - amount - fee) * COIN)
tx.vout.append(deepcopy(tx.vout[0]))
tx.vout[1].nValue = int(amount * COIN)
+ tx.rehash()
+ txid = tx.hash
+ tx_hex = tx.serialize().hex()
- txid = from_node.sendrawtransaction(hexstring=tx.serialize().hex(), maxfeerate=0)
+ batch_reqs.append(from_node.sendrawtransaction.get_request(hexstring=tx_hex, maxfeerate=0))
unconflist.append({"txid": txid, "vout": 0, "value": total_in - amount - fee})
unconflist.append({"txid": txid, "vout": 1, "value": amount})
@@ -115,13 +118,12 @@ def check_estimates(node, fees_seen):
check_smart_estimates(node, fees_seen)
-def send_tx(wallet, node, utxo, feerate):
- """Broadcast a 1in-1out transaction with a specific input and feerate (sat/vb)."""
- return wallet.send_self_transfer(
- from_node=node,
+def make_tx(wallet, utxo, feerate):
+ """Create a 1in-1out transaction with a specific input and feerate (sat/vb)."""
+ return wallet.create_self_transfer(
utxo_to_spend=utxo,
fee_rate=Decimal(feerate * 1000) / COIN,
- )['txid']
+ )
class EstimateFeeTest(BitcoinTestFramework):
@@ -156,6 +158,7 @@ class EstimateFeeTest(BitcoinTestFramework):
# resorting to tx's that depend on the mempool when those run out
for _ in range(numblocks):
random.shuffle(self.confutxo)
+ batch_sendtx_reqs = []
for _ in range(random.randrange(100 - 50, 100 + 50)):
from_index = random.randint(1, 2)
(tx_bytes, fee) = small_txpuzzle_randfee(
@@ -166,9 +169,12 @@ class EstimateFeeTest(BitcoinTestFramework):
Decimal("0.005"),
min_fee,
min_fee,
+ batch_sendtx_reqs,
)
tx_kbytes = tx_bytes / 1000.0
self.fees_per_kb.append(float(fee) / tx_kbytes)
+ for node in self.nodes:
+ node.batch(batch_sendtx_reqs)
self.sync_mempools(wait=0.1)
mined = mining_node.getblock(self.generate(mining_node, 1)[0], True)["tx"]
# update which txouts are confirmed
@@ -245,14 +251,20 @@ class EstimateFeeTest(BitcoinTestFramework):
assert_greater_than_or_equal(len(utxos), 250)
for _ in range(5):
# Broadcast 45 low fee transactions that will need to be RBF'd
+ txs = []
for _ in range(45):
u = utxos.pop(0)
- txid = send_tx(self.wallet, node, u, low_feerate)
+ tx = make_tx(self.wallet, u, low_feerate)
utxos_to_respend.append(u)
- txids_to_replace.append(txid)
+ txids_to_replace.append(tx["txid"])
+ txs.append(tx)
# Broadcast 5 low fee transaction which don't need to
for _ in range(5):
- send_tx(self.wallet, node, utxos.pop(0), low_feerate)
+ tx = make_tx(self.wallet, utxos.pop(0), low_feerate)
+ txs.append(tx)
+ batch_send_tx = [node.sendrawtransaction.get_request(tx["hex"]) for tx in txs]
+ for n in self.nodes:
+ n.batch(batch_send_tx)
# Mine the transactions on another node
self.sync_mempools(wait=0.1, nodes=[node, miner])
for txid in txids_to_replace:
@@ -261,7 +273,12 @@ class EstimateFeeTest(BitcoinTestFramework):
# RBF the low-fee transactions
while len(utxos_to_respend) > 0:
u = utxos_to_respend.pop(0)
- send_tx(self.wallet, node, u, high_feerate)
+ tx = make_tx(self.wallet, u, high_feerate)
+ node.sendrawtransaction(tx["hex"])
+ txs.append(tx)
+ dec_txs = [res["result"] for res in node.batch([node.decoderawtransaction.get_request(tx["hex"]) for tx in txs])]
+ self.wallet.scan_txs(dec_txs)
+
# Mine the last replacement txs
self.sync_mempools(wait=0.1, nodes=[node, miner])
diff --git a/test/functional/feature_maxtipage.py b/test/functional/feature_maxtipage.py
index ddc2102542..51f37ef1e0 100755
--- a/test/functional/feature_maxtipage.py
+++ b/test/functional/feature_maxtipage.py
@@ -22,23 +22,24 @@ class MaxTipAgeTest(BitcoinTestFramework):
self.setup_clean_chain = True
self.num_nodes = 2
- def test_maxtipage(self, maxtipage, set_parameter=True):
+ def test_maxtipage(self, maxtipage, set_parameter=True, test_deltas=True):
node_miner = self.nodes[0]
node_ibd = self.nodes[1]
self.restart_node(1, [f'-maxtipage={maxtipage}'] if set_parameter else None)
self.connect_nodes(0, 1)
-
- # tips older than maximum age -> stay in IBD
cur_time = int(time.time())
- node_ibd.setmocktime(cur_time)
- for delta in [5, 4, 3, 2, 1]:
- node_miner.setmocktime(cur_time - maxtipage - delta)
- self.generate(node_miner, 1)
- assert_equal(node_ibd.getblockchaininfo()['initialblockdownload'], True)
+
+ if test_deltas:
+ # tips older than maximum age -> stay in IBD
+ node_ibd.setmocktime(cur_time)
+ for delta in [5, 4, 3, 2, 1]:
+ node_miner.setmocktime(cur_time - maxtipage - delta)
+ self.generate(node_miner, 1)
+ assert_equal(node_ibd.getblockchaininfo()['initialblockdownload'], True)
# tip within maximum age -> leave IBD
- node_miner.setmocktime(cur_time - maxtipage)
+ node_miner.setmocktime(max(cur_time - maxtipage, 0))
self.generate(node_miner, 1)
assert_equal(node_ibd.getblockchaininfo()['initialblockdownload'], False)
@@ -51,6 +52,10 @@ class MaxTipAgeTest(BitcoinTestFramework):
self.log.info(f"Test IBD with maximum tip age of {hours} hours (-maxtipage={maxtipage}).")
self.test_maxtipage(maxtipage)
+ max_long_val = 9223372036854775807
+ self.log.info(f"Test IBD with highest allowable maximum tip age ({max_long_val}).")
+ self.test_maxtipage(max_long_val, test_deltas=False)
+
if __name__ == '__main__':
MaxTipAgeTest().main()
diff --git a/test/functional/feature_pruning.py b/test/functional/feature_pruning.py
index 58bc6ca67c..de1ea8a3a6 100755
--- a/test/functional/feature_pruning.py
+++ b/test/functional/feature_pruning.py
@@ -10,8 +10,11 @@ This test takes 30 mins or more (up to 2 hours)
"""
import os
-from test_framework.blocktools import create_coinbase
-from test_framework.messages import CBlock
+from test_framework.blocktools import (
+ MIN_BLOCKS_TO_KEEP,
+ create_block,
+ create_coinbase,
+)
from test_framework.script import (
CScript,
OP_NOP,
@@ -48,21 +51,7 @@ def mine_large_blocks(node, n):
previousblockhash = int(best_block["hash"], 16)
for _ in range(n):
- # Build the coinbase transaction (with large scriptPubKey)
- coinbase_tx = create_coinbase(height)
- coinbase_tx.vin[0].nSequence = 2 ** 32 - 1
- coinbase_tx.vout[0].scriptPubKey = big_script
- coinbase_tx.rehash()
-
- # Build the block
- block = CBlock()
- block.nVersion = best_block["version"]
- block.hashPrevBlock = previousblockhash
- block.nTime = mine_large_blocks.nTime
- block.nBits = int('207fffff', 16)
- block.nNonce = 0
- block.vtx = [coinbase_tx]
- block.hashMerkleRoot = block.calc_merkle_root()
+ block = create_block(hashprev=previousblockhash, ntime=mine_large_blocks.nTime, coinbase=create_coinbase(height, script_pubkey=big_script))
block.solve()
# Submit to the node
@@ -345,7 +334,7 @@ class PruneTest(BitcoinTestFramework):
assert has_block(2), "blk00002.dat is still there, should be pruned by now"
# advance the tip so blk00002.dat and blk00003.dat can be pruned (the last 288 blocks should now be in blk00004.dat)
- self.generate(node, 288, sync_fun=self.no_op)
+ self.generate(node, MIN_BLOCKS_TO_KEEP, sync_fun=self.no_op)
prune(1000)
assert not has_block(2), "blk00002.dat is still there, should be pruned by now"
assert not has_block(3), "blk00003.dat is still there, should be pruned by now"
diff --git a/test/functional/feature_taproot.py b/test/functional/feature_taproot.py
index dbe51f8f54..f6f6f3f78b 100755
--- a/test/functional/feature_taproot.py
+++ b/test/functional/feature_taproot.py
@@ -1292,7 +1292,7 @@ class TaprootTest(BitcoinTestFramework):
# It is not impossible to fit enough tapscript sigops to hit the old 80k limit without
# busting txin-level limits. We simply have to account for the p2pk outputs in all
# transactions.
- extra_output_script = CScript([OP_CHECKSIG]*((MAX_BLOCK_SIGOPS_WEIGHT - sigops_weight) // WITNESS_SCALE_FACTOR))
+ extra_output_script = CScript(bytes([OP_CHECKSIG]*((MAX_BLOCK_SIGOPS_WEIGHT - sigops_weight) // WITNESS_SCALE_FACTOR)))
coinbase_tx = create_coinbase(self.lastblockheight + 1, pubkey=cb_pubkey, extra_output_script=extra_output_script, fees=fees)
block = create_block(self.tip, coinbase_tx, self.lastblocktime + 1, txlist=txs)
diff --git a/test/functional/interface_bitcoin_cli.py b/test/functional/interface_bitcoin_cli.py
index b1369c2615..90a543b51b 100755
--- a/test/functional/interface_bitcoin_cli.py
+++ b/test/functional/interface_bitcoin_cli.py
@@ -90,6 +90,10 @@ class TestBitcoinCli(BitcoinTestFramework):
assert_raises_rpc_error(-8, "Parameter arg1 specified twice both as positional and named argument", self.nodes[0].cli.echo, 0, 1, arg1=1)
assert_raises_rpc_error(-8, "Parameter arg1 specified twice both as positional and named argument", self.nodes[0].cli.echo, 0, None, 2, arg1=1)
+ self.log.info("Test that later cli named arguments values silently overwrite earlier ones")
+ assert_equal(self.nodes[0].cli("-named", "echo", "arg0=0", "arg1=1", "arg2=2", "arg1=3").send_cli(), ['0', '3', '2'])
+ assert_raises_rpc_error(-8, "Parameter args specified multiple times", self.nodes[0].cli("-named", "echo", "args=[0,1,2,3]", "4", "5", "6", ).send_cli)
+
user, password = get_auth_cookie(self.nodes[0].datadir, self.chain)
self.log.info("Test -stdinrpcpass option")
diff --git a/test/functional/interface_rpc.py b/test/functional/interface_rpc.py
index 48082f3a17..3389746635 100755
--- a/test/functional/interface_rpc.py
+++ b/test/functional/interface_rpc.py
@@ -25,7 +25,7 @@ def expect_http_status(expected_http_status, expected_rpc_code,
def test_work_queue_getblock(node, got_exceeded_error):
while not got_exceeded_error:
try:
- node.cli('getrpcinfo').send_cli()
+ node.cli("waitfornewblock", "500").send_cli()
except subprocess.CalledProcessError as e:
assert_equal(e.output, 'error: Server response: Work queue depth exceeded\n')
got_exceeded_error.append(True)
diff --git a/test/functional/rpc_packages.py b/test/functional/rpc_packages.py
index a512a6b675..df11750438 100755
--- a/test/functional/rpc_packages.py
+++ b/test/functional/rpc_packages.py
@@ -29,6 +29,7 @@ class RPCPackagesTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 1
self.setup_clean_chain = True
+ self.extra_args = [["-whitelist=noban@127.0.0.1"]] # noban speeds up tx relay
def assert_testres_equal(self, package_hex, testres_expected):
"""Shuffle package_hex and assert that the testmempoolaccept result matches testres_expected. This should only
diff --git a/test/functional/rpc_rawtransaction.py b/test/functional/rpc_rawtransaction.py
index 15fc947eef..b87f3ad6f3 100755
--- a/test/functional/rpc_rawtransaction.py
+++ b/test/functional/rpc_rawtransaction.py
@@ -14,6 +14,7 @@ Test the following RPCs:
from collections import OrderedDict
from decimal import Decimal
+from itertools import product
from test_framework.blocktools import COINBASE_MATURITY
from test_framework.messages import (
@@ -81,6 +82,7 @@ class RawTransactionsTest(BitcoinTestFramework):
self.generate(self.nodes[0], COINBASE_MATURITY + 1)
self.getrawtransaction_tests()
+ self.getrawtransaction_verbosity_tests()
self.createrawtransaction_tests()
self.sendrawtransaction_tests()
self.sendrawtransaction_testmempoolaccept_tests()
@@ -116,6 +118,7 @@ class RawTransactionsTest(BitcoinTestFramework):
# 4. valid parameters - supply txid and 1 for verbose.
# We only check the "hex" field of the output so we don't need to update this test every time the output format changes.
assert_equal(self.nodes[n].getrawtransaction(txId, 1)["hex"], tx['hex'])
+ assert_equal(self.nodes[n].getrawtransaction(txId, 2)["hex"], tx['hex'])
# 5. valid parameters - supply txid and True for non-verbose
assert_equal(self.nodes[n].getrawtransaction(txId, True)["hex"], tx['hex'])
@@ -126,13 +129,14 @@ class RawTransactionsTest(BitcoinTestFramework):
# 6. invalid parameters - supply txid and invalid boolean values (strings) for verbose
for value in ["True", "False"]:
- assert_raises_rpc_error(-3, "not of expected type bool", self.nodes[n].getrawtransaction, txid=txId, verbose=value)
+ assert_raises_rpc_error(-3, "not of expected type number", self.nodes[n].getrawtransaction, txid=txId, verbose=value)
+ assert_raises_rpc_error(-3, "not of expected type number", self.nodes[n].getrawtransaction, txid=txId, verbosity=value)
# 7. invalid parameters - supply txid and empty array
- assert_raises_rpc_error(-3, "not of expected type bool", self.nodes[n].getrawtransaction, txId, [])
+ assert_raises_rpc_error(-3, "not of expected type number", self.nodes[n].getrawtransaction, txId, [])
# 8. invalid parameters - supply txid and empty dict
- assert_raises_rpc_error(-3, "not of expected type bool", self.nodes[n].getrawtransaction, txId, {})
+ assert_raises_rpc_error(-3, "not of expected type number", self.nodes[n].getrawtransaction, txId, {})
# Make a tx by sending, then generate 2 blocks; block1 has the tx in it
tx = self.wallet.send_self_transfer(from_node=self.nodes[2])['txid']
@@ -145,9 +149,10 @@ class RawTransactionsTest(BitcoinTestFramework):
assert_equal(gottx['in_active_chain'], True)
if n == 0:
self.log.info("Test getrawtransaction with -txindex, without blockhash: 'in_active_chain' should be absent")
- gottx = self.nodes[n].getrawtransaction(txid=tx, verbose=True)
- assert_equal(gottx['txid'], tx)
- assert 'in_active_chain' not in gottx
+ for v in [1,2]:
+ gottx = self.nodes[n].getrawtransaction(txid=tx, verbosity=v)
+ assert_equal(gottx['txid'], tx)
+ assert 'in_active_chain' not in gottx
else:
self.log.info("Test getrawtransaction without -txindex, without blockhash: expect the call to raise")
assert_raises_rpc_error(-5, err_msg, self.nodes[n].getrawtransaction, txid=tx, verbose=True)
@@ -172,6 +177,70 @@ class RawTransactionsTest(BitcoinTestFramework):
block = self.nodes[0].getblock(self.nodes[0].getblockhash(0))
assert_raises_rpc_error(-5, "The genesis block coinbase is not considered an ordinary transaction", self.nodes[0].getrawtransaction, block['merkleroot'])
+ def getrawtransaction_verbosity_tests(self):
+ tx = self.wallet.send_self_transfer(from_node=self.nodes[1])['txid']
+ [block1] = self.generate(self.nodes[1], 1)
+ fields = [
+ 'blockhash',
+ 'blocktime',
+ 'confirmations',
+ 'hash',
+ 'hex',
+ 'in_active_chain',
+ 'locktime',
+ 'size',
+ 'time',
+ 'txid',
+ 'vin',
+ 'vout',
+ 'vsize',
+ 'weight',
+ ]
+ prevout_fields = [
+ 'generated',
+ 'height',
+ 'value',
+ 'scriptPubKey',
+ ]
+ script_pub_key_fields = [
+ 'address',
+ 'asm',
+ 'hex',
+ 'type',
+ ]
+ # node 0 & 2 with verbosity 1 & 2
+ for n, v in product([0, 2], [1, 2]):
+ self.log.info(f"Test getrawtransaction_verbosity {v} {'with' if n == 0 else 'without'} -txindex, with blockhash")
+ gottx = self.nodes[n].getrawtransaction(txid=tx, verbosity=v, blockhash=block1)
+ missing_fields = set(fields).difference(gottx.keys())
+ if missing_fields:
+ raise AssertionError(f"fields {', '.join(missing_fields)} are not in transaction")
+
+ assert(len(gottx['vin']) > 0)
+ if v == 1:
+ assert('fee' not in gottx)
+ assert('prevout' not in gottx['vin'][0])
+ if v == 2:
+ assert(isinstance(gottx['fee'], Decimal))
+ assert('prevout' in gottx['vin'][0])
+ prevout = gottx['vin'][0]['prevout']
+ script_pub_key = prevout['scriptPubKey']
+
+ missing_fields = set(prevout_fields).difference(prevout.keys())
+ if missing_fields:
+ raise AssertionError(f"fields {', '.join(missing_fields)} are not in transaction")
+
+ missing_fields = set(script_pub_key_fields).difference(script_pub_key.keys())
+ if missing_fields:
+ raise AssertionError(f"fields {', '.join(missing_fields)} are not in transaction")
+
+ # check verbosity 2 without blockhash but with txindex
+ assert('fee' in self.nodes[0].getrawtransaction(txid=tx, verbosity=2))
+ # check that coinbase has no fee or does not throw any errors for verbosity 2
+ coin_base = self.nodes[1].getblock(block1)['tx'][0]
+ gottx = self.nodes[1].getrawtransaction(txid=coin_base, verbosity=2, blockhash=block1)
+ assert('fee' not in gottx)
+
def createrawtransaction_tests(self):
self.log.info("Test createrawtransaction")
# Test `createrawtransaction` required parameters
diff --git a/test/functional/test_framework/blocktools.py b/test/functional/test_framework/blocktools.py
index 574ea10356..f8e94ca6ba 100644
--- a/test/functional/test_framework/blocktools.py
+++ b/test/functional/test_framework/blocktools.py
@@ -61,6 +61,7 @@ WITNESS_COMMITMENT_HEADER = b"\xaa\x21\xa9\xed"
NORMAL_GBT_REQUEST_PARAMS = {"rules": ["segwit"]}
VERSIONBITS_LAST_OLD_BLOCK_VERSION = 4
+MIN_BLOCKS_TO_KEEP = 288
def create_block(hashprev=None, coinbase=None, ntime=None, *, version=None, tmpl=None, txlist=None):
@@ -120,7 +121,7 @@ def script_BIP34_coinbase_height(height):
return CScript([CScriptNum(height)])
-def create_coinbase(height, pubkey=None, extra_output_script=None, fees=0, nValue=50):
+def create_coinbase(height, pubkey=None, *, script_pubkey=None, extra_output_script=None, fees=0, nValue=50):
"""Create a coinbase transaction.
If pubkey is passed in, the coinbase output will be a P2PK output;
@@ -138,6 +139,8 @@ def create_coinbase(height, pubkey=None, extra_output_script=None, fees=0, nValu
coinbaseoutput.nValue += fees
if pubkey is not None:
coinbaseoutput.scriptPubKey = key_to_p2pk_script(pubkey)
+ elif script_pubkey is not None:
+ coinbaseoutput.scriptPubKey = script_pubkey
else:
coinbaseoutput.scriptPubKey = CScript([OP_TRUE])
coinbase.vout = [coinbaseoutput]
diff --git a/test/functional/test_framework/test_framework.py b/test/functional/test_framework/test_framework.py
index 7cfeae3ff6..a3f2bae2d9 100755
--- a/test/functional/test_framework/test_framework.py
+++ b/test/functional/test_framework/test_framework.py
@@ -214,11 +214,11 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
self.options.descriptors = None
elif self.options.descriptors is None:
# Some wallet is either required or optionally used by the test.
- # Prefer BDB unless it isn't available
- if self.is_bdb_compiled():
- self.options.descriptors = False
- elif self.is_sqlite_compiled():
+ # Prefer SQLite unless it isn't available
+ if self.is_sqlite_compiled():
self.options.descriptors = True
+ elif self.is_bdb_compiled():
+ self.options.descriptors = False
else:
# If neither are compiled, tests requiring a wallet will be skipped and the value of self.options.descriptors won't matter
# It still needs to exist and be None in order for tests to work however.
diff --git a/test/functional/test_framework/test_shell.py b/test/functional/test_framework/test_shell.py
index 26df128f1f..2d8935dfe6 100644
--- a/test/functional/test_framework/test_shell.py
+++ b/test/functional/test_framework/test_shell.py
@@ -16,6 +16,9 @@ class TestShell:
start a single TestShell at a time."""
class __TestShell(BitcoinTestFramework):
+ def add_options(self, parser):
+ self.add_wallet_options(parser)
+
def set_test_params(self):
pass
diff --git a/test/functional/test_framework/wallet.py b/test/functional/test_framework/wallet.py
index a94b9fe3fd..ecde329a1e 100644
--- a/test/functional/test_framework/wallet.py
+++ b/test/functional/test_framework/wallet.py
@@ -141,6 +141,10 @@ class MiniWallet:
if out['scriptPubKey']['hex'] == self._scriptPubKey.hex():
self._utxos.append(self._create_utxo(txid=tx["txid"], vout=out["n"], value=out["value"], height=0))
+ def scan_txs(self, txs):
+ for tx in txs:
+ self.scan_tx(tx)
+
def sign_tx(self, tx, fixed_length=True):
"""Sign tx that has been created by MiniWallet in P2PK mode"""
assert_equal(self._mode, MiniWalletMode.RAW_P2PK)
diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py
index 31b308546d..00e2d92f5f 100755
--- a/test/functional/test_runner.py
+++ b/test/functional/test_runner.py
@@ -85,61 +85,87 @@ EXTENDED_SCRIPTS = [
'feature_pruning.py',
'feature_dbcrash.py',
'feature_index_prune.py',
+ 'wallet_pruning.py --legacy-wallet',
]
BASE_SCRIPTS = [
# Scripts that are run by default.
# Longest test should go first, to favor running tests in parallel
- 'wallet_hd.py --legacy-wallet',
- 'wallet_hd.py --descriptors',
- 'wallet_backup.py --legacy-wallet',
- 'wallet_backup.py --descriptors',
# vv Tests less than 5m vv
+ 'feature_fee_estimation.py',
+ 'feature_taproot.py',
+ 'feature_block.py',
+ # vv Tests less than 2m vv
'mining_getblocktemplate_longpoll.py',
+ 'p2p_segwit.py',
'feature_maxuploadtarget.py',
- 'feature_block.py',
+ 'mempool_updatefromblock.py',
+ 'mempool_persist.py --descriptors',
+ # vv Tests less than 60s vv
+ 'rpc_psbt.py --legacy-wallet',
+ 'rpc_psbt.py --descriptors',
'wallet_fundrawtransaction.py --legacy-wallet',
'wallet_fundrawtransaction.py --descriptors',
- 'p2p_compactblocks.py',
- 'p2p_compactblocks_blocksonly.py',
+ 'wallet_bumpfee.py --legacy-wallet',
+ 'wallet_bumpfee.py --descriptors',
+ 'wallet_import_rescan.py --legacy-wallet',
+ 'wallet_backup.py --legacy-wallet',
+ 'wallet_backup.py --descriptors',
'feature_segwit.py --legacy-wallet',
'feature_segwit.py --descriptors',
- # vv Tests less than 2m vv
+ 'p2p_tx_download.py',
+ 'wallet_avoidreuse.py --legacy-wallet',
+ 'wallet_avoidreuse.py --descriptors',
+ 'feature_abortnode.py',
+ 'wallet_address_types.py --legacy-wallet',
+ 'wallet_address_types.py --descriptors',
'wallet_basic.py --legacy-wallet',
'wallet_basic.py --descriptors',
- 'wallet_labels.py --legacy-wallet',
- 'wallet_labels.py --descriptors',
- 'p2p_segwit.py',
+ 'feature_maxtipage.py',
+ 'wallet_multiwallet.py --legacy-wallet',
+ 'wallet_multiwallet.py --descriptors',
+ 'wallet_multiwallet.py --usecli',
+ 'p2p_dns_seeds.py',
+ 'wallet_groups.py --legacy-wallet',
+ 'wallet_groups.py --descriptors',
+ 'p2p_blockfilters.py',
+ 'feature_assumevalid.py',
+ 'wallet_taproot.py --descriptors',
+ 'feature_bip68_sequence.py',
+ 'rpc_packages.py',
+ 'rpc_bind.py --ipv4',
+ 'rpc_bind.py --ipv6',
+ 'rpc_bind.py --nonloopback',
+ 'p2p_headers_sync_with_minchainwork.py',
+ 'p2p_feefilter.py',
+ 'feature_csv_activation.py',
+ 'p2p_sendheaders.py',
+ 'wallet_listtransactions.py --legacy-wallet',
+ 'wallet_listtransactions.py --descriptors',
+ # vv Tests less than 30s vv
+ 'p2p_invalid_messages.py',
+ 'rpc_createmultisig.py',
'p2p_timeouts.py',
- 'p2p_tx_download.py',
- 'mempool_updatefromblock.py',
'wallet_dump.py --legacy-wallet',
- 'feature_taproot.py',
'rpc_signer.py',
'wallet_signer.py --descriptors',
- # vv Tests less than 60s vv
- 'p2p_sendheaders.py',
'wallet_importmulti.py --legacy-wallet',
'mempool_limit.py',
'rpc_txoutproof.py',
'wallet_listreceivedby.py --legacy-wallet',
'wallet_listreceivedby.py --descriptors',
'wallet_abandonconflict.py --legacy-wallet',
- 'p2p_dns_seeds.py',
'wallet_abandonconflict.py --descriptors',
- 'feature_csv_activation.py',
- 'wallet_address_types.py --legacy-wallet',
- 'wallet_address_types.py --descriptors',
- 'feature_bip68_sequence.py',
- 'p2p_feefilter.py',
- 'rpc_packages.py',
'feature_reindex.py',
- 'feature_abortnode.py',
- # vv Tests less than 30s vv
+ 'wallet_labels.py --legacy-wallet',
+ 'wallet_labels.py --descriptors',
+ 'p2p_compactblocks.py',
+ 'p2p_compactblocks_blocksonly.py',
+ 'wallet_hd.py --legacy-wallet',
+ 'wallet_hd.py --descriptors',
'wallet_keypool_topup.py --legacy-wallet',
'wallet_keypool_topup.py --descriptors',
'wallet_fast_rescan.py --descriptors',
- 'feature_fee_estimation.py',
'interface_zmq.py',
'rpc_invalid_address_message.py',
'interface_bitcoin_cli.py --legacy-wallet',
@@ -157,20 +183,12 @@ BASE_SCRIPTS = [
'rpc_misc.py',
'interface_rest.py',
'mempool_spend_coinbase.py',
- 'wallet_avoidreuse.py --legacy-wallet',
- 'wallet_avoidreuse.py --descriptors',
'wallet_avoid_mixing_output_types.py --descriptors',
'mempool_reorg.py',
- 'mempool_persist.py --descriptors',
'p2p_block_sync.py',
- 'wallet_multiwallet.py --legacy-wallet',
- 'wallet_multiwallet.py --descriptors',
- 'wallet_multiwallet.py --usecli',
'wallet_createwallet.py --legacy-wallet',
'wallet_createwallet.py --usecli',
'wallet_createwallet.py --descriptors',
- 'wallet_listtransactions.py --legacy-wallet',
- 'wallet_listtransactions.py --descriptors',
'wallet_watchonly.py --legacy-wallet',
'wallet_watchonly.py --usecli --legacy-wallet',
'wallet_reorgsrestore.py',
@@ -180,8 +198,6 @@ BASE_SCRIPTS = [
'interface_usdt_net.py',
'interface_usdt_utxocache.py',
'interface_usdt_validation.py',
- 'rpc_psbt.py --legacy-wallet',
- 'rpc_psbt.py --descriptors',
'rpc_users.py',
'rpc_whitelist.py',
'feature_proxy.py',
@@ -189,13 +205,10 @@ BASE_SCRIPTS = [
'wallet_signrawtransactionwithwallet.py --legacy-wallet',
'wallet_signrawtransactionwithwallet.py --descriptors',
'rpc_signrawtransactionwithkey.py',
- 'p2p_headers_sync_with_minchainwork.py',
'rpc_rawtransaction.py --legacy-wallet',
- 'wallet_groups.py --legacy-wallet',
'wallet_transactiontime_rescan.py --descriptors',
'wallet_transactiontime_rescan.py --legacy-wallet',
'p2p_addrv2_relay.py',
- 'wallet_groups.py --descriptors',
'p2p_compactblocks_hb.py',
'p2p_disconnect_ban.py',
'rpc_decodescript.py',
@@ -211,7 +224,6 @@ BASE_SCRIPTS = [
'wallet_keypool.py --descriptors',
'wallet_descriptor.py --descriptors',
'wallet_miniscript.py --descriptors',
- 'feature_maxtipage.py',
'p2p_nobloomfilter_messages.py',
'p2p_filter.py',
'rpc_setban.py',
@@ -219,9 +231,7 @@ BASE_SCRIPTS = [
'mining_prioritisetransaction.py',
'p2p_invalid_locator.py',
'p2p_invalid_block.py',
- 'p2p_invalid_messages.py',
'p2p_invalid_tx.py',
- 'feature_assumevalid.py',
'example_test.py',
'wallet_txn_doublespend.py --legacy-wallet',
'wallet_multisig_descriptor_psbt.py --descriptors',
@@ -237,7 +247,6 @@ BASE_SCRIPTS = [
'feature_rbf.py',
'mempool_packages.py',
'mempool_package_onemore.py',
- 'rpc_createmultisig.py',
'mempool_package_limits.py',
'feature_versionbits_warning.py',
'rpc_preciousblock.py',
@@ -254,18 +263,12 @@ BASE_SCRIPTS = [
'feature_nulldummy.py',
'mempool_accept.py',
'mempool_expiry.py',
- 'wallet_import_rescan.py --legacy-wallet',
'wallet_import_with_label.py --legacy-wallet',
'wallet_importdescriptors.py --descriptors',
'wallet_upgradewallet.py --legacy-wallet',
- 'rpc_bind.py --ipv4',
- 'rpc_bind.py --ipv6',
- 'rpc_bind.py --nonloopback',
'wallet_crosschain.py',
'mining_basic.py',
'feature_signet.py',
- 'wallet_bumpfee.py --legacy-wallet',
- 'wallet_bumpfee.py --descriptors',
'wallet_implicitsegwit.py --legacy-wallet',
'rpc_named_arguments.py',
'feature_startupnotify.py',
@@ -296,7 +299,6 @@ BASE_SCRIPTS = [
'wallet_sendall.py --legacy-wallet',
'wallet_sendall.py --descriptors',
'wallet_create_tx.py --descriptors',
- 'wallet_taproot.py --descriptors',
'wallet_inactive_hdchains.py --legacy-wallet',
'p2p_fingerprint.py',
'feature_uacomment.py',
@@ -309,7 +311,6 @@ BASE_SCRIPTS = [
'p2p_add_connections.py',
'feature_bind_port_discover.py',
'p2p_unrequested_blocks.py',
- 'p2p_blockfilters.py',
'p2p_message_capture.py',
'feature_includeconf.py',
'feature_addrman.py',
diff --git a/test/functional/wallet_import_rescan.py b/test/functional/wallet_import_rescan.py
index 64f8f2eb6e..4dc39e83f8 100755
--- a/test/functional/wallet_import_rescan.py
+++ b/test/functional/wallet_import_rescan.py
@@ -179,7 +179,16 @@ class ImportRescanTest(BitcoinTestFramework):
# Create one transaction on node 0 with a unique amount for
# each possible type of wallet import RPC.
+ last_variants = []
for i, variant in enumerate(IMPORT_VARIANTS):
+ if i % 10 == 0:
+ blockhash = self.generate(self.nodes[0], 1)[0]
+ conf_height = self.nodes[0].getblockcount()
+ timestamp = self.nodes[0].getblockheader(blockhash)["time"]
+ for var in last_variants:
+ var.confirmation_height = conf_height
+ var.timestamp = timestamp
+ last_variants.clear()
variant.label = "label {} {}".format(i, variant)
variant.address = self.nodes[1].getaddressinfo(self.nodes[1].getnewaddress(
label=variant.label,
@@ -188,9 +197,15 @@ class ImportRescanTest(BitcoinTestFramework):
variant.key = self.nodes[1].dumpprivkey(variant.address["address"])
variant.initial_amount = get_rand_amount()
variant.initial_txid = self.nodes[0].sendtoaddress(variant.address["address"], variant.initial_amount)
- self.generate(self.nodes[0], 1) # Generate one block for each send
- variant.confirmation_height = self.nodes[0].getblockcount()
- variant.timestamp = self.nodes[0].getblockheader(self.nodes[0].getbestblockhash())["time"]
+ last_variants.append(variant)
+
+ blockhash = self.generate(self.nodes[0], 1)[0]
+ conf_height = self.nodes[0].getblockcount()
+ timestamp = self.nodes[0].getblockheader(blockhash)["time"]
+ for var in last_variants:
+ var.confirmation_height = conf_height
+ var.timestamp = timestamp
+ last_variants.clear()
# Generate a block further in the future (past the rescan window).
assert_equal(self.nodes[0].getrawmempool(), [])
@@ -217,11 +232,14 @@ class ImportRescanTest(BitcoinTestFramework):
variant.check()
# Create new transactions sending to each address.
- for variant in IMPORT_VARIANTS:
+ for i, variant in enumerate(IMPORT_VARIANTS):
+ if i % 10 == 0:
+ blockhash = self.generate(self.nodes[0], 1)[0]
+ conf_height = self.nodes[0].getblockcount() + 1
variant.sent_amount = get_rand_amount()
variant.sent_txid = self.nodes[0].sendtoaddress(variant.address["address"], variant.sent_amount)
- self.generate(self.nodes[0], 1) # Generate one block for each send
- variant.confirmation_height = self.nodes[0].getblockcount()
+ variant.confirmation_height = conf_height
+ self.generate(self.nodes[0], 1)
assert_equal(self.nodes[0].getrawmempool(), [])
self.sync_all()
diff --git a/test/functional/wallet_keypool.py b/test/functional/wallet_keypool.py
index aa31757f35..8b485a9324 100755
--- a/test/functional/wallet_keypool.py
+++ b/test/functional/wallet_keypool.py
@@ -204,6 +204,9 @@ class KeyPoolTest(BitcoinTestFramework):
res = w2.walletcreatefundedpsbt(inputs=[], outputs=[{destination: 0.00010000}], options={"subtractFeeFromOutputs": [0], "feeRate": 0.00010, "changeAddress": addr.pop()})
assert_equal("psbt" in res, True)
+ if not self.options.descriptors:
+ msg = "Error: Private keys are disabled for this wallet"
+ assert_raises_rpc_error(-4, msg, w2.keypoolrefill, 100)
if __name__ == '__main__':
KeyPoolTest().main()
diff --git a/test/functional/wallet_migration.py b/test/functional/wallet_migration.py
index 37625e50d8..2997372bab 100755
--- a/test/functional/wallet_migration.py
+++ b/test/functional/wallet_migration.py
@@ -40,11 +40,13 @@ class WalletMigrationTest(BitcoinTestFramework):
assert_equal(file_magic, b'SQLite format 3\x00')
assert_equal(self.nodes[0].get_wallet_rpc(wallet_name).getwalletinfo()["format"], "sqlite")
- def create_legacy_wallet(self, wallet_name):
- self.nodes[0].createwallet(wallet_name=wallet_name)
+ def create_legacy_wallet(self, wallet_name, disable_private_keys=False):
+ self.nodes[0].createwallet(wallet_name=wallet_name, descriptors=False, disable_private_keys=disable_private_keys)
wallet = self.nodes[0].get_wallet_rpc(wallet_name)
- assert_equal(wallet.getwalletinfo()["descriptors"], False)
- assert_equal(wallet.getwalletinfo()["format"], "bdb")
+ info = wallet.getwalletinfo()
+ assert_equal(info["descriptors"], False)
+ assert_equal(info["format"], "bdb")
+ assert_equal(info["private_keys_enabled"], not disable_private_keys)
return wallet
def assert_addr_info_equal(self, addr_info, addr_info_old):
@@ -187,11 +189,9 @@ class WalletMigrationTest(BitcoinTestFramework):
# Some keys in multisig do not belong to this wallet
self.log.info("Test migration of a wallet that has some keys in a multisig")
- self.nodes[0].createwallet(wallet_name="multisig1")
- multisig1 = self.nodes[0].get_wallet_rpc("multisig1")
+ multisig1 = self.create_legacy_wallet("multisig1")
ms_info = multisig1.addmultisigaddress(2, [multisig1.getnewaddress(), pub1, pub2])
ms_info2 = multisig1.addmultisigaddress(2, [multisig1.getnewaddress(), pub1, pub2])
- assert_equal(multisig1.getwalletinfo()["descriptors"], False)
addr1 = ms_info["address"]
addr2 = ms_info2["address"]
@@ -256,9 +256,7 @@ class WalletMigrationTest(BitcoinTestFramework):
# Wallet with an imported address. Should be the same thing as the multisig test
self.log.info("Test migration of a wallet with watchonly imports")
- self.nodes[0].createwallet(wallet_name="imports0")
- imports0 = self.nodes[0].get_wallet_rpc("imports0")
- assert_equal(imports0.getwalletinfo()["descriptors"], False)
+ imports0 = self.create_legacy_wallet("imports0")
# Exteranl address label
imports0.setlabel(default.getnewaddress(), "external")
@@ -318,11 +316,7 @@ class WalletMigrationTest(BitcoinTestFramework):
# Migrating an actual watchonly wallet should not create a new watchonly wallet
self.log.info("Test migration of a pure watchonly wallet")
- self.nodes[0].createwallet(wallet_name="watchonly0", disable_private_keys=True)
- watchonly0 = self.nodes[0].get_wallet_rpc("watchonly0")
- info = watchonly0.getwalletinfo()
- assert_equal(info["descriptors"], False)
- assert_equal(info["private_keys_enabled"], False)
+ watchonly0 = self.create_legacy_wallet("watchonly0", disable_private_keys=True)
addr = default.getnewaddress()
desc = default.getaddressinfo(addr)["desc"]
@@ -345,11 +339,7 @@ class WalletMigrationTest(BitcoinTestFramework):
# Migrating a wallet with pubkeys added to the keypool
self.log.info("Test migration of a pure watchonly wallet with pubkeys in keypool")
- self.nodes[0].createwallet(wallet_name="watchonly1", disable_private_keys=True)
- watchonly1 = self.nodes[0].get_wallet_rpc("watchonly1")
- info = watchonly1.getwalletinfo()
- assert_equal(info["descriptors"], False)
- assert_equal(info["private_keys_enabled"], False)
+ watchonly1 = self.create_legacy_wallet("watchonly1", disable_private_keys=True)
addr1 = default.getnewaddress(address_type="bech32")
addr2 = default.getnewaddress(address_type="bech32")
diff --git a/test/functional/wallet_pruning.py b/test/functional/wallet_pruning.py
new file mode 100755
index 0000000000..6d8475ce8d
--- /dev/null
+++ b/test/functional/wallet_pruning.py
@@ -0,0 +1,158 @@
+#!/usr/bin/env python3
+# Copyright (c) 2022 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+"""Test wallet import on pruned node."""
+import os
+
+from test_framework.util import assert_equal, assert_raises_rpc_error
+from test_framework.blocktools import (
+ COINBASE_MATURITY,
+ create_block
+)
+from test_framework.blocktools import create_coinbase
+from test_framework.test_framework import BitcoinTestFramework
+
+from test_framework.script import (
+ CScript,
+ OP_RETURN,
+ OP_TRUE,
+)
+
+class WalletPruningTest(BitcoinTestFramework):
+ def add_options(self, parser):
+ self.add_wallet_options(parser, descriptors=False)
+
+ def set_test_params(self):
+ self.setup_clean_chain = True
+ self.num_nodes = 2
+ self.wallet_names = []
+ self.extra_args = [
+ [], # node dedicated to mining
+ ['-prune=550'], # node dedicated to testing pruning
+ ]
+
+ def skip_test_if_missing_module(self):
+ self.skip_if_no_wallet()
+ self.skip_if_no_bdb()
+
+ def mine_large_blocks(self, node, n):
+ # Get the block parameters for the first block
+ best_block = node.getblock(node.getbestblockhash())
+ height = int(best_block["height"]) + 1
+ self.nTime = max(self.nTime, int(best_block["time"])) + 1
+ previousblockhash = int(best_block["hash"], 16)
+ big_script = CScript([OP_RETURN] + [OP_TRUE] * 950000)
+ for _ in range(n):
+ block = create_block(hashprev=previousblockhash, ntime=self.nTime, coinbase=create_coinbase(height, script_pubkey=big_script))
+ block.solve()
+
+ # Submit to the node
+ node.submitblock(block.serialize().hex())
+
+ previousblockhash = block.sha256
+ height += 1
+
+ # Simulate 10 minutes of work time per block
+ # Important for matching a timestamp with a block +- some window
+ self.nTime += 600
+ for n in self.nodes:
+ if n.running:
+ n.setmocktime(self.nTime) # Update node's time to accept future blocks
+ self.sync_all()
+
+ def test_wallet_import_pruned(self, wallet_name):
+ self.log.info("Make sure we can import wallet when pruned and required blocks are still available")
+
+ wallet_file = wallet_name + ".dat"
+ wallet_birthheight = self.get_birthheight(wallet_file)
+
+ # Verify that the block at wallet's birthheight is available at the pruned node
+ self.nodes[1].getblock(self.nodes[1].getblockhash(wallet_birthheight))
+
+ # Import wallet into pruned node
+ self.nodes[1].createwallet(wallet_name="wallet_pruned", descriptors=False, load_on_startup=True)
+ self.nodes[1].importwallet(os.path.join(self.nodes[0].datadir, wallet_file))
+
+ # Make sure that prune node's wallet correctly accounts for balances
+ assert_equal(self.nodes[1].getbalance(), self.nodes[0].getbalance())
+
+ self.log.info("- Done")
+
+ def test_wallet_import_pruned_with_missing_blocks(self, wallet_name):
+ self.log.info("Make sure we cannot import wallet when pruned and required blocks are not available")
+
+ wallet_file = wallet_name + ".dat"
+ wallet_birthheight = self.get_birthheight(wallet_file)
+
+ # Verify that the block at wallet's birthheight is not available at the pruned node
+ assert_raises_rpc_error(-1, "Block not available (pruned data)", self.nodes[1].getblock, self.nodes[1].getblockhash(wallet_birthheight))
+
+ # Make sure wallet cannot be imported because of missing blocks
+ # This will try to rescan blocks `TIMESTAMP_WINDOW` (2h) before the wallet birthheight.
+ # There are 6 blocks an hour, so 11 blocks (excluding birthheight).
+ assert_raises_rpc_error(-4, f"Pruned blocks from height {wallet_birthheight - 11} required to import keys. Use RPC call getblockchaininfo to determine your pruned height.", self.nodes[1].importwallet, os.path.join(self.nodes[0].datadir, wallet_file))
+ self.log.info("- Done")
+
+ def get_birthheight(self, wallet_file):
+ """Gets birthheight of a wallet on node0"""
+ with open(os.path.join(self.nodes[0].datadir, wallet_file), 'r', encoding="utf8") as f:
+ for line in f:
+ if line.startswith('# * Best block at time of backup'):
+ wallet_birthheight = int(line.split(' ')[9])
+ return wallet_birthheight
+
+ def has_block(self, block_index):
+ """Checks if the pruned node has the specific blk0000*.dat file"""
+ return os.path.isfile(os.path.join(self.nodes[1].datadir, self.chain, "blocks", f"blk{block_index:05}.dat"))
+
+ def create_wallet(self, wallet_name, *, unload=False):
+ """Creates and dumps a wallet on the non-pruned node0 to be later import by the pruned node"""
+ self.nodes[0].createwallet(wallet_name=wallet_name, descriptors=False, load_on_startup=True)
+ self.nodes[0].dumpwallet(os.path.join(self.nodes[0].datadir, wallet_name + ".dat"))
+ if (unload):
+ self.nodes[0].unloadwallet(wallet_name)
+
+ def run_test(self):
+ self.nTime = 0
+ self.log.info("Warning! This test requires ~1.3GB of disk space")
+
+ self.log.info("Generating a long chain of blocks...")
+
+ # A blk*.dat file is 128MB
+ # Generate 250 light blocks
+ self.generate(self.nodes[0], 250, sync_fun=self.no_op)
+ # Generate 50MB worth of large blocks in the blk00000.dat file
+ self.mine_large_blocks(self.nodes[0], 50)
+
+ # Create a wallet which birth's block is in the blk00000.dat file
+ wallet_birthheight_1 = "wallet_birthheight_1"
+ assert_equal(self.has_block(1), False)
+ self.create_wallet(wallet_birthheight_1, unload=True)
+
+ # Generate enough large blocks to reach pruning disk limit
+ # Not pruning yet because we are still below PruneAfterHeight
+ self.mine_large_blocks(self.nodes[0], 600)
+ self.log.info("- Long chain created")
+
+ # Create a wallet with birth height > wallet_birthheight_1
+ wallet_birthheight_2 = "wallet_birthheight_2"
+ self.create_wallet(wallet_birthheight_2)
+
+ # Fund wallet to later verify that importwallet correctly accounts for balances
+ self.generatetoaddress(self.nodes[0], COINBASE_MATURITY + 1, self.nodes[0].getnewaddress(), sync_fun=self.no_op)
+
+ # We've reached pruning storage & height limit but
+ # pruning doesn't run until another chunk (blk*.dat file) is allocated.
+ # That's why we are generating another 5 large blocks
+ self.mine_large_blocks(self.nodes[0], 5)
+
+ # blk00000.dat file is now pruned from node1
+ assert_equal(self.has_block(0), False)
+
+ self.test_wallet_import_pruned(wallet_birthheight_2)
+ self.test_wallet_import_pruned_with_missing_blocks(wallet_birthheight_1)
+
+if __name__ == '__main__':
+ WalletPruningTest().main()
diff --git a/test/functional/wallet_signer.py b/test/functional/wallet_signer.py
index 9ab7154424..0b63ba8491 100755
--- a/test/functional/wallet_signer.py
+++ b/test/functional/wallet_signer.py
@@ -13,6 +13,7 @@ import platform
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
+ assert_greater_than,
assert_raises_rpc_error,
)
@@ -169,7 +170,7 @@ class WalletSignerTest(BitcoinTestFramework):
assert_equal(result[1], {'success': True})
assert_equal(mock_wallet.getwalletinfo()["txcount"], 1)
dest = self.nodes[0].getnewaddress(address_type='bech32')
- mock_psbt = mock_wallet.walletcreatefundedpsbt([], {dest:0.5}, 0, {}, True)['psbt']
+ mock_psbt = mock_wallet.walletcreatefundedpsbt([], {dest:0.5}, 0, {'replaceable': True}, True)['psbt']
mock_psbt_signed = mock_wallet.walletprocesspsbt(psbt=mock_psbt, sign=True, sighashtype="ALL", bip32derivs=True)
mock_psbt_final = mock_wallet.finalizepsbt(mock_psbt_signed["psbt"])
mock_tx = mock_psbt_final["hex"]
@@ -209,6 +210,7 @@ class WalletSignerTest(BitcoinTestFramework):
self.log.info('Test send using hww1')
+ # Don't broadcast transaction yet so the RPC returns the raw hex
res = hww.send(outputs={dest:0.5},options={"add_to_wallet": False})
assert(res["complete"])
assert_equal(res["hex"], mock_tx)
@@ -218,6 +220,25 @@ class WalletSignerTest(BitcoinTestFramework):
res = hww.sendall(recipients=[{dest:0.5}, hww.getrawchangeaddress()],options={"add_to_wallet": False})
assert(res["complete"])
assert_equal(res["hex"], mock_tx)
+ # Broadcast transaction so we can bump the fee
+ hww.sendrawtransaction(res["hex"])
+
+ self.log.info('Prepare fee bumped mock PSBT')
+
+ # Now that the transaction is broadcast, bump fee in mock wallet:
+ orig_tx_id = res["txid"]
+ mock_psbt_bumped = mock_wallet.psbtbumpfee(orig_tx_id)["psbt"]
+ mock_psbt_bumped_signed = mock_wallet.walletprocesspsbt(psbt=mock_psbt_bumped, sign=True, sighashtype="ALL", bip32derivs=True)
+
+ with open(os.path.join(self.nodes[1].cwd, "mock_psbt"), "w", encoding="utf8") as f:
+ f.write(mock_psbt_bumped_signed["psbt"])
+
+ self.log.info('Test bumpfee using hww1')
+
+ # Bump fee
+ res = hww.bumpfee(orig_tx_id)
+ assert_greater_than(res["fee"], res["origfee"])
+ assert_equal(res["errors"], [])
# # Handle error thrown by script
# self.set_mock_result(self.nodes[4], "2")
diff --git a/test/lint/check-doc.py b/test/lint/check-doc.py
index feaebc68e9..7955dd4f3e 100755
--- a/test/lint/check-doc.py
+++ b/test/lint/check-doc.py
@@ -15,7 +15,7 @@ import re
FOLDER_GREP = 'src'
FOLDER_TEST = 'src/test/'
-REGEX_ARG = r'(?:ForceSet|SoftSet|Get|Is)(?:Bool)?Args?(?:Set)?\("(-[^"]+)"'
+REGEX_ARG = r'\b(?:GetArg|GetArgs|GetBoolArg|GetIntArg|GetPathArg|IsArgSet|get_net)\("(-[^"]+)"'
REGEX_DOC = r'AddArg\("(-[^"=]+?)(?:=|")'
CMD_ROOT_DIR = '$(git rev-parse --show-toplevel)/{}'.format(FOLDER_GREP)
CMD_GREP_ARGS = r"git grep --perl-regexp '{}' -- {} ':(exclude){}'".format(REGEX_ARG, CMD_ROOT_DIR, FOLDER_TEST)
diff --git a/test/lint/spelling.ignore-words.txt b/test/lint/spelling.ignore-words.txt
index 82f18010c1..d44dd70684 100644
--- a/test/lint/spelling.ignore-words.txt
+++ b/test/lint/spelling.ignore-words.txt
@@ -4,6 +4,7 @@ blockin
bu
cachable
clen
+crypted
fo
fpr
hights