aboutsummaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rwxr-xr-xtest/functional/feature_bip68_sequence.py105
-rwxr-xr-xtest/functional/feature_config_args.py6
-rwxr-xr-xtest/functional/feature_dbcrash.py1
-rwxr-xr-xtest/functional/feature_fee_estimation.py1
-rwxr-xr-xtest/functional/feature_notifications.py10
-rwxr-xr-xtest/functional/feature_pruning.py17
-rwxr-xr-xtest/functional/feature_rbf.py9
-rwxr-xr-xtest/functional/feature_txindex_compatibility.py1
-rwxr-xr-xtest/functional/interface_rest.py1
-rwxr-xr-xtest/functional/interface_usdt_utxocache.py1
-rwxr-xr-xtest/functional/interface_zmq.py1
-rwxr-xr-xtest/functional/mempool_accept.py1
-rwxr-xr-xtest/functional/mempool_datacarrier.py1
-rwxr-xr-xtest/functional/mempool_dust.py115
-rwxr-xr-xtest/functional/mempool_expiry.py9
-rwxr-xr-xtest/functional/mempool_limit.py1
-rwxr-xr-xtest/functional/mempool_package_limits.py12
-rwxr-xr-xtest/functional/mempool_package_onemore.py1
-rwxr-xr-xtest/functional/mempool_packages.py118
-rwxr-xr-xtest/functional/mempool_persist.py1
-rwxr-xr-xtest/functional/mempool_reorg.py1
-rwxr-xr-xtest/functional/mempool_resurrect.py6
-rwxr-xr-xtest/functional/mempool_spend_coinbase.py1
-rwxr-xr-xtest/functional/mempool_unbroadcast.py1
-rwxr-xr-xtest/functional/mining_getblocktemplate_longpoll.py4
-rwxr-xr-xtest/functional/mining_prioritisetransaction.py1
-rwxr-xr-xtest/functional/p2p_blocksonly.py2
-rwxr-xr-xtest/functional/p2p_disconnect_ban.py2
-rwxr-xr-xtest/functional/p2p_eviction.py32
-rwxr-xr-xtest/functional/p2p_feefilter.py4
-rwxr-xr-xtest/functional/p2p_filter.py1
-rwxr-xr-xtest/functional/p2p_headers_sync_with_minchainwork.py1
-rwxr-xr-xtest/functional/p2p_ibd_stalling.py164
-rwxr-xr-xtest/functional/p2p_leak_tx.py4
-rwxr-xr-xtest/functional/p2p_permissions.py37
-rwxr-xr-xtest/functional/p2p_tx_download.py25
-rwxr-xr-xtest/functional/p2p_tx_privacy.py1
-rwxr-xr-xtest/functional/rpc_blockchain.py12
-rwxr-xr-xtest/functional/rpc_generate.py1
-rwxr-xr-xtest/functional/rpc_invalid_address_message.py10
-rwxr-xr-xtest/functional/rpc_mempool_info.py1
-rwxr-xr-xtest/functional/rpc_net.py5
-rwxr-xr-xtest/functional/rpc_packages.py6
-rwxr-xr-xtest/functional/rpc_psbt.py62
-rwxr-xr-xtest/functional/rpc_rawtransaction.py5
-rwxr-xr-xtest/functional/rpc_scanblocks.py14
-rwxr-xr-xtest/functional/rpc_scantxoutset.py1
-rwxr-xr-xtest/functional/rpc_txoutproof.py7
-rwxr-xr-xtest/functional/rpc_users.py4
-rw-r--r--test/functional/test_framework/script.py7
-rwxr-xr-xtest/functional/test_framework/test_framework.py17
-rwxr-xr-xtest/functional/test_framework/test_node.py17
-rw-r--r--test/functional/test_framework/util.py22
-rw-r--r--test/functional/test_framework/wallet.py128
-rwxr-xr-xtest/functional/test_runner.py6
-rwxr-xr-xtest/functional/tool_wallet.py2
-rwxr-xr-xtest/functional/wallet_basic.py39
-rwxr-xr-xtest/functional/wallet_bumpfee.py30
-rwxr-xr-xtest/functional/wallet_crosschain.py6
-rwxr-xr-xtest/functional/wallet_descriptor.py9
-rwxr-xr-xtest/functional/wallet_fast_rescan.py1
-rwxr-xr-xtest/functional/wallet_fundrawtransaction.py61
-rwxr-xr-xtest/functional/wallet_importdescriptors.py4
-rwxr-xr-xtest/functional/wallet_migration.py4
-rwxr-xr-xtest/functional/wallet_orphanedreward.py37
-rwxr-xr-xtest/functional/wallet_send.py14
-rwxr-xr-xtest/functional/wallet_sendall.py68
-rwxr-xr-xtest/fuzz/test_runner.py10
-rw-r--r--test/lint/README.md18
-rwxr-xr-xtest/lint/lint-assertions.py2
-rwxr-xr-xtest/lint/lint-circular-dependencies.py4
-rwxr-xr-xtest/lint/lint-git-commit-check.py6
-rwxr-xr-xtest/lint/lint-includes.py14
-rwxr-xr-xtest/lint/lint-locale-dependence.py2
-rwxr-xr-xtest/lint/lint-logs.py2
-rwxr-xr-xtest/lint/lint-python-mutable-default-parameters.py2
-rwxr-xr-xtest/lint/lint-python-utf8-encoding.py6
-rwxr-xr-xtest/lint/lint-shell.py2
-rwxr-xr-xtest/lint/lint-submodule.py2
-rwxr-xr-xtest/lint/lint-tests.py2
-rwxr-xr-xtest/lint/lint-whitespace.py4
-rwxr-xr-xtest/util/test_runner.py2
82 files changed, 936 insertions, 441 deletions
diff --git a/test/functional/feature_bip68_sequence.py b/test/functional/feature_bip68_sequence.py
index d63821fbbc..894afffc79 100755
--- a/test/functional/feature_bip68_sequence.py
+++ b/test/functional/feature_bip68_sequence.py
@@ -32,6 +32,7 @@ from test_framework.util import (
assert_raises_rpc_error,
softfork_active,
)
+from test_framework.wallet import MiniWallet
SCRIPT_W0_SH_OP_TRUE = script_to_p2wsh_script(CScript([OP_TRUE]))
@@ -58,14 +59,9 @@ class BIP68Test(BitcoinTestFramework):
],
]
- def skip_test_if_missing_module(self):
- self.skip_if_no_wallet()
-
def run_test(self):
self.relayfee = self.nodes[0].getnetworkinfo()["relayfee"]
-
- # Generate some coins
- self.generate(self.nodes[0], 110)
+ self.wallet = MiniWallet(self.nodes[0])
self.log.info("Running test disable flag")
self.test_disable_flag()
@@ -92,16 +88,10 @@ class BIP68Test(BitcoinTestFramework):
# the first sequence bit is set.
def test_disable_flag(self):
# Create some unconfirmed inputs
- new_addr = self.nodes[0].getnewaddress()
- self.nodes[0].sendtoaddress(new_addr, 2) # send 2 BTC
-
- utxos = self.nodes[0].listunspent(0, 0)
- assert len(utxos) > 0
-
- utxo = utxos[0]
+ utxo = self.wallet.send_self_transfer(from_node=self.nodes[0])["new_utxo"]
tx1 = CTransaction()
- value = int((utxo["amount"] - self.relayfee) * COIN)
+ value = int((utxo["value"] - self.relayfee) * COIN)
# Check that the disable flag disables relative locktime.
# If sequence locks were used, this would require 1 block for the
@@ -110,8 +100,8 @@ class BIP68Test(BitcoinTestFramework):
tx1.vin = [CTxIn(COutPoint(int(utxo["txid"], 16), utxo["vout"]), nSequence=sequence_value)]
tx1.vout = [CTxOut(value, SCRIPT_W0_SH_OP_TRUE)]
- tx1_signed = self.nodes[0].signrawtransactionwithwallet(tx1.serialize().hex())["hex"]
- tx1_id = self.nodes[0].sendrawtransaction(tx1_signed)
+ self.wallet.sign_tx(tx=tx1)
+ tx1_id = self.wallet.sendrawtransaction(from_node=self.nodes[0], tx_hex=tx1.serialize().hex())
tx1_id = int(tx1_id, 16)
# This transaction will enable sequence-locks, so this transaction should
@@ -125,13 +115,13 @@ class BIP68Test(BitcoinTestFramework):
tx2.vout = [CTxOut(int(value - self.relayfee * COIN), SCRIPT_W0_SH_OP_TRUE)]
tx2.rehash()
- assert_raises_rpc_error(-26, NOT_FINAL_ERROR, self.nodes[0].sendrawtransaction, tx2.serialize().hex())
+ assert_raises_rpc_error(-26, NOT_FINAL_ERROR, self.wallet.sendrawtransaction, from_node=self.nodes[0], tx_hex=tx2.serialize().hex())
# Setting the version back down to 1 should disable the sequence lock,
# so this should be accepted.
tx2.nVersion = 1
- self.nodes[0].sendrawtransaction(tx2.serialize().hex())
+ self.wallet.sendrawtransaction(from_node=self.nodes[0], tx_hex=tx2.serialize().hex())
# Calculate the median time past of a prior block ("confirmations" before
# the current tip).
@@ -144,20 +134,13 @@ class BIP68Test(BitcoinTestFramework):
# Create lots of confirmed utxos, and use them to generate lots of random
# transactions.
max_outputs = 50
- addresses = []
- while len(addresses) < max_outputs:
- addresses.append(self.nodes[0].getnewaddress())
- while len(self.nodes[0].listunspent()) < 200:
+ while len(self.wallet.get_utxos(include_immature_coinbase=False, mark_as_spent=False)) < 200:
import random
- random.shuffle(addresses)
num_outputs = random.randint(1, max_outputs)
- outputs = {}
- for i in range(num_outputs):
- outputs[addresses[i]] = random.randint(1, 20)*0.01
- self.nodes[0].sendmany("", outputs)
- self.generate(self.nodes[0], 1)
+ self.wallet.send_self_transfer_multi(from_node=self.nodes[0], num_outputs=num_outputs)
+ self.generate(self.wallet, 1)
- utxos = self.nodes[0].listunspent()
+ utxos = self.wallet.get_utxos(include_immature_coinbase=False)
# Try creating a lot of random transactions.
# Each time, choose a random number of inputs, and randomly set
@@ -214,19 +197,20 @@ class BIP68Test(BitcoinTestFramework):
sequence_value = ((cur_time - orig_time) >> SEQUENCE_LOCKTIME_GRANULARITY)+1
sequence_value |= SEQUENCE_LOCKTIME_TYPE_FLAG
tx.vin.append(CTxIn(COutPoint(int(utxos[j]["txid"], 16), utxos[j]["vout"]), nSequence=sequence_value))
- value += utxos[j]["amount"]*COIN
+ value += utxos[j]["value"]*COIN
# Overestimate the size of the tx - signatures should be less than 120 bytes, and leave 50 for the output
tx_size = len(tx.serialize().hex())//2 + 120*num_inputs + 50
tx.vout.append(CTxOut(int(value - self.relayfee * tx_size * COIN / 1000), SCRIPT_W0_SH_OP_TRUE))
- rawtx = self.nodes[0].signrawtransactionwithwallet(tx.serialize().hex())["hex"]
+ self.wallet.sign_tx(tx=tx)
if (using_sequence_locks and not should_pass):
# This transaction should be rejected
- assert_raises_rpc_error(-26, NOT_FINAL_ERROR, self.nodes[0].sendrawtransaction, rawtx)
+ assert_raises_rpc_error(-26, NOT_FINAL_ERROR, self.wallet.sendrawtransaction, from_node=self.nodes[0], tx_hex=tx.serialize().hex())
else:
# This raw transaction should be accepted
- self.nodes[0].sendrawtransaction(rawtx)
- utxos = self.nodes[0].listunspent()
+ self.wallet.sendrawtransaction(from_node=self.nodes[0], tx_hex=tx.serialize().hex())
+ self.wallet.rescan_utxos()
+ utxos = self.wallet.get_utxos(include_immature_coinbase=False)
# Test that sequence locks on unconfirmed inputs must have nSequence
# height or time of 0 to be accepted.
@@ -237,8 +221,8 @@ class BIP68Test(BitcoinTestFramework):
cur_height = self.nodes[0].getblockcount()
# Create a mempool tx.
- txid = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 2)
- tx1 = tx_from_hex(self.nodes[0].getrawtransaction(txid))
+ self.wallet.rescan_utxos()
+ tx1 = self.wallet.send_self_transfer(from_node=self.nodes[0])["tx"]
tx1.rehash()
# Anyone-can-spend mempool tx.
@@ -247,11 +231,11 @@ class BIP68Test(BitcoinTestFramework):
tx2.nVersion = 2
tx2.vin = [CTxIn(COutPoint(tx1.sha256, 0), nSequence=0)]
tx2.vout = [CTxOut(int(tx1.vout[0].nValue - self.relayfee * COIN), SCRIPT_W0_SH_OP_TRUE)]
- tx2_raw = self.nodes[0].signrawtransactionwithwallet(tx2.serialize().hex())["hex"]
- tx2 = tx_from_hex(tx2_raw)
+ self.wallet.sign_tx(tx=tx2)
+ tx2_raw = tx2.serialize().hex()
tx2.rehash()
- self.nodes[0].sendrawtransaction(tx2_raw)
+ self.wallet.sendrawtransaction(from_node=self.nodes[0], tx_hex=tx2_raw)
# Create a spend of the 0th output of orig_tx with a sequence lock
# of 1, and test what happens when submitting.
@@ -271,10 +255,10 @@ class BIP68Test(BitcoinTestFramework):
if (orig_tx.hash in node.getrawmempool()):
# sendrawtransaction should fail if the tx is in the mempool
- assert_raises_rpc_error(-26, NOT_FINAL_ERROR, node.sendrawtransaction, tx.serialize().hex())
+ assert_raises_rpc_error(-26, NOT_FINAL_ERROR, self.wallet.sendrawtransaction, from_node=node, tx_hex=tx.serialize().hex())
else:
# sendrawtransaction should succeed if the tx is not in the mempool
- node.sendrawtransaction(tx.serialize().hex())
+ self.wallet.sendrawtransaction(from_node=node, tx_hex=tx.serialize().hex())
return tx
@@ -287,7 +271,7 @@ class BIP68Test(BitcoinTestFramework):
cur_time = int(time.time())
for _ in range(10):
self.nodes[0].setmocktime(cur_time + 600)
- self.generate(self.nodes[0], 1, sync_fun=self.no_op)
+ self.generate(self.wallet, 1, sync_fun=self.no_op)
cur_time += 600
assert tx2.hash in self.nodes[0].getrawmempool()
@@ -321,12 +305,12 @@ class BIP68Test(BitcoinTestFramework):
tx5 = test_nonzero_locks(tx4, self.nodes[0], self.relayfee, use_height_lock=True)
assert tx5.hash not in self.nodes[0].getrawmempool()
- utxos = self.nodes[0].listunspent()
- tx5.vin.append(CTxIn(COutPoint(int(utxos[0]["txid"], 16), utxos[0]["vout"]), nSequence=1))
- tx5.vout[0].nValue += int(utxos[0]["amount"]*COIN)
- raw_tx5 = self.nodes[0].signrawtransactionwithwallet(tx5.serialize().hex())["hex"]
+ utxo = self.wallet.get_utxo()
+ tx5.vin.append(CTxIn(COutPoint(int(utxo["txid"], 16), utxo["vout"]), nSequence=1))
+ tx5.vout[0].nValue += int(utxo["value"]*COIN)
+ self.wallet.sign_tx(tx=tx5)
- assert_raises_rpc_error(-26, NOT_FINAL_ERROR, self.nodes[0].sendrawtransaction, raw_tx5)
+ assert_raises_rpc_error(-26, NOT_FINAL_ERROR, self.wallet.sendrawtransaction, from_node=self.nodes[0], tx_hex=tx5.serialize().hex())
# Test mempool-BIP68 consistency after reorg
#
@@ -362,7 +346,7 @@ class BIP68Test(BitcoinTestFramework):
# Reset the chain and get rid of the mocktimed-blocks
self.nodes[0].setmocktime(0)
self.nodes[0].invalidateblock(self.nodes[0].getblockhash(cur_height+1))
- self.generate(self.nodes[0], 10, sync_fun=self.no_op)
+ self.generate(self.wallet, 10, sync_fun=self.no_op)
# Make sure that BIP68 isn't being used to validate blocks prior to
# activation height. If more blocks are mined prior to this test
@@ -370,9 +354,8 @@ class BIP68Test(BitcoinTestFramework):
# this test should be moved to run earlier, or deleted.
def test_bip68_not_consensus(self):
assert not softfork_active(self.nodes[0], 'csv')
- txid = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 2)
- tx1 = tx_from_hex(self.nodes[0].getrawtransaction(txid))
+ tx1 = self.wallet.send_self_transfer(from_node=self.nodes[0])["tx"]
tx1.rehash()
# Make an anyone-can-spend transaction
@@ -382,11 +365,12 @@ class BIP68Test(BitcoinTestFramework):
tx2.vout = [CTxOut(int(tx1.vout[0].nValue - self.relayfee * COIN), SCRIPT_W0_SH_OP_TRUE)]
# sign tx2
- tx2_raw = self.nodes[0].signrawtransactionwithwallet(tx2.serialize().hex())["hex"]
+ self.wallet.sign_tx(tx=tx2)
+ tx2_raw = tx2.serialize().hex()
tx2 = tx_from_hex(tx2_raw)
tx2.rehash()
- self.nodes[0].sendrawtransaction(tx2.serialize().hex())
+ self.wallet.sendrawtransaction(from_node=self.nodes[0], tx_hex=tx2_raw)
# Now make an invalid spend of tx2 according to BIP68
sequence_value = 100 # 100 block relative locktime
@@ -399,7 +383,7 @@ class BIP68Test(BitcoinTestFramework):
tx3.vout = [CTxOut(int(tx2.vout[0].nValue - self.relayfee * COIN), SCRIPT_W0_SH_OP_TRUE)]
tx3.rehash()
- assert_raises_rpc_error(-26, NOT_FINAL_ERROR, self.nodes[0].sendrawtransaction, tx3.serialize().hex())
+ assert_raises_rpc_error(-26, NOT_FINAL_ERROR, self.wallet.sendrawtransaction, from_node=self.nodes[0], tx_hex=tx3.serialize().hex())
# make a block that violates bip68; ensure that the tip updates
block = create_block(tmpl=self.nodes[0].getblocktemplate(NORMAL_GBT_REQUEST_PARAMS), txlist=[tx1, tx2, tx3])
@@ -415,22 +399,19 @@ class BIP68Test(BitcoinTestFramework):
min_activation_height = 432
height = self.nodes[0].getblockcount()
assert_greater_than(min_activation_height - height, 2)
- self.generate(self.nodes[0], min_activation_height - height - 2, sync_fun=self.no_op)
+ self.generate(self.wallet, min_activation_height - height - 2, sync_fun=self.no_op)
assert not softfork_active(self.nodes[0], 'csv')
- self.generate(self.nodes[0], 1, sync_fun=self.no_op)
+ self.generate(self.wallet, 1, sync_fun=self.no_op)
assert softfork_active(self.nodes[0], 'csv')
self.sync_blocks()
# Use self.nodes[1] to test that version 2 transactions are standard.
def test_version2_relay(self):
- inputs = [ ]
- outputs = { self.nodes[1].getnewaddress() : 1.0 }
- rawtx = self.nodes[1].createrawtransaction(inputs, outputs)
- rawtxfund = self.nodes[1].fundrawtransaction(rawtx)['hex']
- tx = tx_from_hex(rawtxfund)
+ mini_wallet = MiniWallet(self.nodes[1])
+ mini_wallet.rescan_utxos()
+ tx = mini_wallet.create_self_transfer()["tx"]
tx.nVersion = 2
- tx_signed = self.nodes[1].signrawtransactionwithwallet(tx.serialize().hex())["hex"]
- self.nodes[1].sendrawtransaction(tx_signed)
+ mini_wallet.sendrawtransaction(from_node=self.nodes[1], tx_hex=tx.serialize().hex())
if __name__ == '__main__':
BIP68Test().main()
diff --git a/test/functional/feature_config_args.py b/test/functional/feature_config_args.py
index d5e5ed47d6..a37d614535 100755
--- a/test/functional/feature_config_args.py
+++ b/test/functional/feature_config_args.py
@@ -126,7 +126,6 @@ class ConfArgsTest(BitcoinTestFramework):
expected_msgs=[
'Command-line arg: addnode="some.node"',
'Command-line arg: rpcauth=****',
- 'Command-line arg: rpcbind=****',
'Command-line arg: rpcpassword=****',
'Command-line arg: rpcuser=****',
'Command-line arg: torpassword=****',
@@ -135,14 +134,17 @@ class ConfArgsTest(BitcoinTestFramework):
],
unexpected_msgs=[
'alice:f7efda5c189b999524f151318c0c86$d5b51b3beffbc0',
- '127.1.1.1',
'secret-rpcuser',
'secret-torpassword',
+ 'Command-line arg: rpcbind=****',
+ 'Command-line arg: rpcallowip=****',
]):
self.start_node(0, extra_args=[
'-addnode=some.node',
'-rpcauth=alice:f7efda5c189b999524f151318c0c86$d5b51b3beffbc0',
'-rpcbind=127.1.1.1',
+ '-rpcbind=127.0.0.1',
+ "-rpcallowip=127.0.0.1",
'-rpcpassword=',
'-rpcuser=secret-rpcuser',
'-torpassword=secret-torpassword',
diff --git a/test/functional/feature_dbcrash.py b/test/functional/feature_dbcrash.py
index ff770e7707..e2bc566f53 100755
--- a/test/functional/feature_dbcrash.py
+++ b/test/functional/feature_dbcrash.py
@@ -202,7 +202,6 @@ class ChainstateWriteCrashTest(BitcoinTestFramework):
def run_test(self):
self.wallet = MiniWallet(self.nodes[3])
- self.wallet.rescan_utxos()
initial_height = self.nodes[3].getblockcount()
self.generate(self.nodes[3], COINBASE_MATURITY, sync_fun=self.no_op)
diff --git a/test/functional/feature_fee_estimation.py b/test/functional/feature_fee_estimation.py
index 6b2d5b9455..05ee556ece 100755
--- a/test/functional/feature_fee_estimation.py
+++ b/test/functional/feature_fee_estimation.py
@@ -297,7 +297,6 @@ class EstimateFeeTest(BitcoinTestFramework):
# Split two coinbases into many small utxos
self.start_node(0)
self.wallet = MiniWallet(self.nodes[0])
- self.wallet.rescan_utxos()
self.initial_split(self.nodes[0])
self.log.info("Finished splitting")
diff --git a/test/functional/feature_notifications.py b/test/functional/feature_notifications.py
index 32fea18f37..8cb633d454 100755
--- a/test/functional/feature_notifications.py
+++ b/test/functional/feature_notifications.py
@@ -31,7 +31,7 @@ class NotificationsTest(BitcoinTestFramework):
self.num_nodes = 2
self.setup_clean_chain = True
# The experimental syscall sandbox feature (-sandbox) is not compatible with -alertnotify,
- # -blocknotify or -walletnotify (which all invoke execve).
+ # -blocknotify, -walletnotify or -shutdownnotify (which all invoke execve).
self.disable_syscall_sandbox = True
def setup_network(self):
@@ -39,14 +39,18 @@ class NotificationsTest(BitcoinTestFramework):
self.alertnotify_dir = os.path.join(self.options.tmpdir, "alertnotify")
self.blocknotify_dir = os.path.join(self.options.tmpdir, "blocknotify")
self.walletnotify_dir = os.path.join(self.options.tmpdir, "walletnotify")
+ self.shutdownnotify_dir = os.path.join(self.options.tmpdir, "shutdownnotify")
+ self.shutdownnotify_file = os.path.join(self.shutdownnotify_dir, "shutdownnotify.txt")
os.mkdir(self.alertnotify_dir)
os.mkdir(self.blocknotify_dir)
os.mkdir(self.walletnotify_dir)
+ os.mkdir(self.shutdownnotify_dir)
# -alertnotify and -blocknotify on node0, walletnotify on node1
self.extra_args = [[
f"-alertnotify=echo > {os.path.join(self.alertnotify_dir, '%s')}",
f"-blocknotify=echo > {os.path.join(self.blocknotify_dir, '%s')}",
+ f"-shutdownnotify=echo > {self.shutdownnotify_file}",
], [
f"-walletnotify=echo %h_%b > {os.path.join(self.walletnotify_dir, notify_outputname('%w', '%s'))}",
]]
@@ -162,6 +166,10 @@ class NotificationsTest(BitcoinTestFramework):
# TODO: add test for `-alertnotify` large fork notifications
+ self.log.info("test -shutdownnotify")
+ self.stop_nodes()
+ self.wait_until(lambda: os.path.isfile(self.shutdownnotify_file), timeout=10)
+
def expect_wallet_notify(self, tx_details):
self.wait_until(lambda: len(os.listdir(self.walletnotify_dir)) >= len(tx_details), timeout=10)
# Should have no more and no less files than expected
diff --git a/test/functional/feature_pruning.py b/test/functional/feature_pruning.py
index f68416dc03..664ed779db 100755
--- a/test/functional/feature_pruning.py
+++ b/test/functional/feature_pruning.py
@@ -84,7 +84,7 @@ class PruneTest(BitcoinTestFramework):
["-maxreceivebuffer=20000", "-prune=550"],
["-maxreceivebuffer=20000"],
["-maxreceivebuffer=20000"],
- ["-prune=550"],
+ ["-prune=550", "-blockfilterindex=1"],
]
self.rpc_timeout = 120
@@ -356,7 +356,7 @@ class PruneTest(BitcoinTestFramework):
self.connect_nodes(0, 5)
nds = [self.nodes[0], self.nodes[5]]
self.sync_blocks(nds, wait=5, timeout=300)
- self.restart_node(5, extra_args=["-prune=550"]) # restart to trigger rescan
+ self.restart_node(5, extra_args=["-prune=550", "-blockfilterindex=1"]) # restart to trigger rescan
self.log.info("Success")
def run_test(self):
@@ -472,7 +472,20 @@ class PruneTest(BitcoinTestFramework):
self.log.info("Test invalid pruning command line options")
self.test_invalid_command_line_options()
+ self.test_scanblocks_pruned()
+
self.log.info("Done")
+ def test_scanblocks_pruned(self):
+ node = self.nodes[5]
+ genesis_blockhash = node.getblockhash(0)
+ false_positive_spk = bytes.fromhex("001400000000000000000000000000000000000cadcb")
+
+ assert genesis_blockhash in node.scanblocks(
+ "start", [{"desc": f"raw({false_positive_spk.hex()})"}], 0, 0)['relevant_blocks']
+
+ assert_raises_rpc_error(-1, "Block not available (pruned data)", node.scanblocks,
+ "start", [{"desc": f"raw({false_positive_spk.hex()})"}], 0, 0, "basic", {"filter_false_positives": True})
+
if __name__ == '__main__':
PruneTest().main()
diff --git a/test/functional/feature_rbf.py b/test/functional/feature_rbf.py
index 085ff3a2e3..947d2e8273 100755
--- a/test/functional/feature_rbf.py
+++ b/test/functional/feature_rbf.py
@@ -42,10 +42,6 @@ class ReplaceByFeeTest(BitcoinTestFramework):
def run_test(self):
self.wallet = MiniWallet(self.nodes[0])
- # the pre-mined test framework chain contains coinbase outputs to the
- # MiniWallet's default address in blocks 76-100 (see method
- # BitcoinTestFramework._initialize_chain())
- self.wallet.rescan_utxos()
self.log.info("Running test simple doublespend...")
self.test_simple_doublespend()
@@ -396,12 +392,11 @@ class ReplaceByFeeTest(BitcoinTestFramework):
enough transactions off of each root UTXO to exceed the MAX_REPLACEMENT_LIMIT.
Then create a conflicting RBF replacement transaction.
"""
- normal_node = self.nodes[1]
- wallet = MiniWallet(normal_node)
- wallet.rescan_utxos()
# Clear mempools to avoid cross-node sync failure.
for node in self.nodes:
self.generate(node, 1)
+ normal_node = self.nodes[1]
+ wallet = MiniWallet(normal_node)
# This has to be chosen so that the total number of transactions can exceed
# MAX_REPLACEMENT_LIMIT without having any one tx graph run into the descendant
diff --git a/test/functional/feature_txindex_compatibility.py b/test/functional/feature_txindex_compatibility.py
index dd18c5fd99..48fefaa0ba 100755
--- a/test/functional/feature_txindex_compatibility.py
+++ b/test/functional/feature_txindex_compatibility.py
@@ -42,7 +42,6 @@ class TxindexCompatibilityTest(BitcoinTestFramework):
def run_test(self):
mini_wallet = MiniWallet(self.nodes[1])
- mini_wallet.rescan_utxos()
spend_utxo = mini_wallet.get_utxo()
mini_wallet.send_self_transfer(from_node=self.nodes[1], utxo_to_spend=spend_utxo)
self.generate(self.nodes[1], 1)
diff --git a/test/functional/interface_rest.py b/test/functional/interface_rest.py
index db2ff19b44..0dddc8946a 100755
--- a/test/functional/interface_rest.py
+++ b/test/functional/interface_rest.py
@@ -96,7 +96,6 @@ class RESTTest (BitcoinTestFramework):
def run_test(self):
self.url = urllib.parse.urlparse(self.nodes[0].url)
self.wallet = MiniWallet(self.nodes[0])
- self.wallet.rescan_utxos()
self.log.info("Broadcast test transaction and sync nodes")
txid, _ = self.wallet.send_to(from_node=self.nodes[0], scriptPubKey=getnewdestination()[1], amount=int(0.1 * COIN))
diff --git a/test/functional/interface_usdt_utxocache.py b/test/functional/interface_usdt_utxocache.py
index e3b0b32f0d..23785b1e8a 100755
--- a/test/functional/interface_usdt_utxocache.py
+++ b/test/functional/interface_usdt_utxocache.py
@@ -144,7 +144,6 @@ class UTXOCacheTracepointTest(BitcoinTestFramework):
def run_test(self):
self.wallet = MiniWallet(self.nodes[0])
- self.generate(self.wallet, 101)
self.test_uncache()
self.test_add_spent()
diff --git a/test/functional/interface_zmq.py b/test/functional/interface_zmq.py
index 5357bdf2b8..2f41f9aa53 100755
--- a/test/functional/interface_zmq.py
+++ b/test/functional/interface_zmq.py
@@ -215,7 +215,6 @@ class ZMQTest (BitcoinTestFramework):
assert_equal([txid.hex()], self.nodes[1].getblock(hash)["tx"])
- self.wallet.rescan_utxos()
self.log.info("Wait for tx from second node")
payment_tx = self.wallet.send_self_transfer(from_node=self.nodes[1])
payment_txid = payment_tx['txid']
diff --git a/test/functional/mempool_accept.py b/test/functional/mempool_accept.py
index 19cb65be36..362b407062 100755
--- a/test/functional/mempool_accept.py
+++ b/test/functional/mempool_accept.py
@@ -69,7 +69,6 @@ class MempoolAcceptanceTest(BitcoinTestFramework):
def run_test(self):
node = self.nodes[0]
self.wallet = MiniWallet(node)
- self.wallet.rescan_utxos()
self.log.info('Start with empty mempool, and 200 blocks')
self.mempool_size = 0
diff --git a/test/functional/mempool_datacarrier.py b/test/functional/mempool_datacarrier.py
index 9c82964a24..c370d8fa91 100755
--- a/test/functional/mempool_datacarrier.py
+++ b/test/functional/mempool_datacarrier.py
@@ -44,7 +44,6 @@ class DataCarrierTest(BitcoinTestFramework):
def run_test(self):
self.wallet = MiniWallet(self.nodes[0])
- self.wallet.rescan_utxos()
# By default, only 80 bytes are used for data (+1 for OP_RETURN, +2 for the pushdata opcodes).
default_size_data = random_bytes(MAX_OP_RETURN_RELAY - 3)
diff --git a/test/functional/mempool_dust.py b/test/functional/mempool_dust.py
new file mode 100755
index 0000000000..41a26e82da
--- /dev/null
+++ b/test/functional/mempool_dust.py
@@ -0,0 +1,115 @@
+#!/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 dust limit mempool policy (`-dustrelayfee` parameter)"""
+from decimal import Decimal
+
+from test_framework.key import ECKey
+from test_framework.messages import (
+ COIN,
+ CTxOut,
+)
+from test_framework.script import (
+ CScript,
+ OP_RETURN,
+ OP_TRUE,
+)
+from test_framework.script_util import (
+ key_to_p2pk_script,
+ key_to_p2pkh_script,
+ key_to_p2wpkh_script,
+ keys_to_multisig_script,
+ output_key_to_p2tr_script,
+ program_to_witness_script,
+ script_to_p2sh_script,
+ script_to_p2wsh_script,
+)
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.test_node import TestNode
+from test_framework.util import (
+ assert_equal,
+ get_fee,
+)
+from test_framework.wallet import MiniWallet
+
+
+DUST_RELAY_TX_FEE = 3000 # default setting [sat/kvB]
+
+
+class DustRelayFeeTest(BitcoinTestFramework):
+ def set_test_params(self):
+ self.num_nodes = 1
+
+ def test_dust_output(self, node: TestNode, dust_relay_fee: Decimal,
+ output_script: CScript, type_desc: str) -> None:
+ # determine dust threshold (see `GetDustThreshold`)
+ if output_script[0] == OP_RETURN:
+ dust_threshold = 0
+ else:
+ tx_size = len(CTxOut(nValue=0, scriptPubKey=output_script).serialize())
+ tx_size += 67 if output_script.IsWitnessProgram() else 148
+ dust_threshold = int(get_fee(tx_size, dust_relay_fee) * COIN)
+ self.log.info(f"-> Test {type_desc} output (size {len(output_script)}, limit {dust_threshold})")
+
+ # amount right on the dust threshold should pass
+ tx = self.wallet.create_self_transfer()["tx"]
+ tx.vout.append(CTxOut(nValue=dust_threshold, scriptPubKey=output_script))
+ tx.vout[0].nValue -= dust_threshold # keep total output value constant
+ tx_good_hex = tx.serialize().hex()
+ res = node.testmempoolaccept([tx_good_hex])[0]
+ assert_equal(res['allowed'], True)
+
+ # amount just below the dust threshold should fail
+ if dust_threshold > 0:
+ tx.vout[1].nValue -= 1
+ res = node.testmempoolaccept([tx.serialize().hex()])[0]
+ assert_equal(res['allowed'], False)
+ assert_equal(res['reject-reason'], 'dust')
+
+ # finally send the transaction to avoid running out of MiniWallet UTXOs
+ self.wallet.sendrawtransaction(from_node=node, tx_hex=tx_good_hex)
+
+ def run_test(self):
+ self.wallet = MiniWallet(self.nodes[0])
+
+ # prepare output scripts of each standard type
+ key = ECKey()
+ key.generate(compressed=False)
+ uncompressed_pubkey = key.get_pubkey().get_bytes()
+ key.generate(compressed=True)
+ pubkey = key.get_pubkey().get_bytes()
+
+ output_scripts = (
+ (key_to_p2pk_script(uncompressed_pubkey), "P2PK (uncompressed)"),
+ (key_to_p2pk_script(pubkey), "P2PK (compressed)"),
+ (key_to_p2pkh_script(pubkey), "P2PKH"),
+ (script_to_p2sh_script(CScript([OP_TRUE])), "P2SH"),
+ (key_to_p2wpkh_script(pubkey), "P2WPKH"),
+ (script_to_p2wsh_script(CScript([OP_TRUE])), "P2WSH"),
+ (output_key_to_p2tr_script(pubkey[1:]), "P2TR"),
+ # witness programs for segwitv2+ can be between 2 and 40 bytes
+ (program_to_witness_script(2, b'\x66' * 2), "P2?? (future witness version 2)"),
+ (program_to_witness_script(16, b'\x77' * 40), "P2?? (future witness version 16)"),
+ # largest possible output script considered standard
+ (keys_to_multisig_script([uncompressed_pubkey]*3), "bare multisig (m-of-3)"),
+ (CScript([OP_RETURN, b'superimportanthash']), "null data (OP_RETURN)"),
+ )
+
+ # test default (no parameter), disabled (=0) and a bunch of arbitrary dust fee rates [sat/kvB]
+ for dustfee_sat_kvb in (DUST_RELAY_TX_FEE, 0, 1, 66, 500, 1337, 12345, 21212, 333333):
+ dustfee_btc_kvb = dustfee_sat_kvb / Decimal(COIN)
+ if dustfee_sat_kvb == DUST_RELAY_TX_FEE:
+ self.log.info(f"Test default dust limit setting ({dustfee_sat_kvb} sat/kvB)...")
+ else:
+ dust_parameter = f"-dustrelayfee={dustfee_btc_kvb:.8f}"
+ self.log.info(f"Test dust limit setting {dust_parameter} ({dustfee_sat_kvb} sat/kvB)...")
+ self.restart_node(0, extra_args=[dust_parameter])
+
+ for output_script, description in output_scripts:
+ self.test_dust_output(self.nodes[0], dustfee_btc_kvb, output_script, description)
+ self.generate(self.nodes[0], 1)
+
+
+if __name__ == '__main__':
+ DustRelayFeeTest().main()
diff --git a/test/functional/mempool_expiry.py b/test/functional/mempool_expiry.py
index 0c91da901f..15a5f765df 100755
--- a/test/functional/mempool_expiry.py
+++ b/test/functional/mempool_expiry.py
@@ -12,7 +12,6 @@ definable expiry timeout via the '-mempoolexpiry=<n>' command line argument
from datetime import timedelta
-from test_framework.blocktools import COINBASE_MATURITY
from test_framework.messages import DEFAULT_MEMPOOL_EXPIRY_HOURS
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
@@ -27,17 +26,11 @@ CUSTOM_MEMPOOL_EXPIRY = 10 # hours
class MempoolExpiryTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 1
- self.setup_clean_chain = True
def test_transaction_expiry(self, timeout):
"""Tests that a transaction expires after the expiry timeout and its
children are removed as well."""
node = self.nodes[0]
- self.wallet = MiniWallet(node)
-
- # Add enough mature utxos to the wallet so that all txs spend confirmed coins.
- self.generate(self.wallet, 4)
- self.generate(node, COINBASE_MATURITY)
# Send a parent transaction that will expire.
parent_txid = self.wallet.send_self_transfer(from_node=node)['txid']
@@ -97,6 +90,8 @@ class MempoolExpiryTest(BitcoinTestFramework):
assert_equal(half_expiry_time, node.getmempoolentry(independent_txid)['time'])
def run_test(self):
+ self.wallet = MiniWallet(self.nodes[0])
+
self.log.info('Test default mempool expiry timeout of %d hours.' %
DEFAULT_MEMPOOL_EXPIRY_HOURS)
self.test_transaction_expiry(DEFAULT_MEMPOOL_EXPIRY_HOURS)
diff --git a/test/functional/mempool_limit.py b/test/functional/mempool_limit.py
index 07636439d0..d38a37f952 100755
--- a/test/functional/mempool_limit.py
+++ b/test/functional/mempool_limit.py
@@ -25,7 +25,6 @@ class MempoolLimitTest(BitcoinTestFramework):
self.extra_args = [[
"-datacarriersize=100000",
"-maxmempool=5",
- "-spendzeroconfchange=0",
]]
self.supports_cli = False
diff --git a/test/functional/mempool_package_limits.py b/test/functional/mempool_package_limits.py
index 47b7be7d88..63f5a3e3d3 100755
--- a/test/functional/mempool_package_limits.py
+++ b/test/functional/mempool_package_limits.py
@@ -45,7 +45,7 @@ class MempoolPackageLimitsTest(BitcoinTestFramework):
assert_equal(0, node.getmempoolinfo()["size"])
chain_hex = []
- chaintip_utxo = self.wallet.send_self_transfer_chain(from_node=node, chain_length=mempool_count)
+ chaintip_utxo = self.wallet.send_self_transfer_chain(from_node=node, chain_length=mempool_count)[-1]["new_utxo"]
# in-package transactions
for _ in range(package_count):
tx = self.wallet.create_self_transfer(utxo_to_spend=chaintip_utxo)
@@ -100,13 +100,13 @@ class MempoolPackageLimitsTest(BitcoinTestFramework):
package_hex = []
# Chain A (M2a... M12a)
- chain_a_tip_utxo = self.wallet.send_self_transfer_chain(from_node=node, chain_length=11, utxo_to_spend=m1_utxos[0])
+ chain_a_tip_utxo = self.wallet.send_self_transfer_chain(from_node=node, chain_length=11, utxo_to_spend=m1_utxos[0])[-1]["new_utxo"]
# Pa
pa_hex = self.wallet.create_self_transfer(utxo_to_spend=chain_a_tip_utxo)["hex"]
package_hex.append(pa_hex)
# Chain B (M2b... M13b)
- chain_b_tip_utxo = self.wallet.send_self_transfer_chain(from_node=node, chain_length=12, utxo_to_spend=m1_utxos[1])
+ chain_b_tip_utxo = self.wallet.send_self_transfer_chain(from_node=node, chain_length=12, utxo_to_spend=m1_utxos[1])[-1]["new_utxo"]
# Pb
pb_hex = self.wallet.create_self_transfer(utxo_to_spend=chain_b_tip_utxo)["hex"]
package_hex.append(pb_hex)
@@ -145,7 +145,7 @@ class MempoolPackageLimitsTest(BitcoinTestFramework):
m1_utxos = self.wallet.send_self_transfer_multi(from_node=node, num_outputs=2)['new_utxos']
# Chain M2...M24
- self.wallet.send_self_transfer_chain(from_node=node, chain_length=23, utxo_to_spend=m1_utxos[0])
+ self.wallet.send_self_transfer_chain(from_node=node, chain_length=23, utxo_to_spend=m1_utxos[0])[-1]["new_utxo"]
# P1
p1_tx = self.wallet.create_self_transfer(utxo_to_spend=m1_utxos[1])
@@ -191,7 +191,7 @@ class MempoolPackageLimitsTest(BitcoinTestFramework):
# Two chains of 13 transactions each
for _ in range(2):
- chain_tip_utxo = self.wallet.send_self_transfer_chain(from_node=node, chain_length=12)
+ chain_tip_utxo = self.wallet.send_self_transfer_chain(from_node=node, chain_length=12)[-1]["new_utxo"]
# Save the 13th transaction for the package
tx = self.wallet.create_self_transfer(utxo_to_spend=chain_tip_utxo)
package_hex.append(tx["hex"])
@@ -234,7 +234,7 @@ class MempoolPackageLimitsTest(BitcoinTestFramework):
self.log.info("Check that in-mempool and in-package ancestors are calculated properly in packages")
# Two chains of 12 transactions each
for _ in range(2):
- chaintip_utxo = self.wallet.send_self_transfer_chain(from_node=node, chain_length=12)
+ chaintip_utxo = self.wallet.send_self_transfer_chain(from_node=node, chain_length=12)[-1]["new_utxo"]
# last 2 transactions will be the parents of Pc
pc_parent_utxos.append(chaintip_utxo)
diff --git a/test/functional/mempool_package_onemore.py b/test/functional/mempool_package_onemore.py
index 23ee587098..921c190668 100755
--- a/test/functional/mempool_package_onemore.py
+++ b/test/functional/mempool_package_onemore.py
@@ -31,7 +31,6 @@ class MempoolPackagesTest(BitcoinTestFramework):
def run_test(self):
self.wallet = MiniWallet(self.nodes[0])
- self.wallet.rescan_utxos()
# DEFAULT_ANCESTOR_LIMIT transactions off a confirmed tx should be fine
chain = []
diff --git a/test/functional/mempool_packages.py b/test/functional/mempool_packages.py
index c89528101e..0387282862 100755
--- a/test/functional/mempool_packages.py
+++ b/test/functional/mempool_packages.py
@@ -6,7 +6,6 @@
from decimal import Decimal
-from test_framework.blocktools import COINBASE_MATURITY
from test_framework.messages import (
COIN,
DEFAULT_ANCESTOR_LIMIT,
@@ -17,9 +16,8 @@ from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
assert_raises_rpc_error,
- chain_transaction,
)
-
+from test_framework.wallet import MiniWallet
# custom limits for node1
CUSTOM_ANCESTOR_LIMIT = 5
@@ -45,43 +43,33 @@ class MempoolPackagesTest(BitcoinTestFramework):
],
]
- def skip_test_if_missing_module(self):
- self.skip_if_no_wallet()
-
def run_test(self):
- # Mine some blocks and have them mature.
+ self.wallet = MiniWallet(self.nodes[0])
+ self.wallet.rescan_utxos()
+
+ if self.is_specified_wallet_compiled():
+ self.nodes[0].createwallet("watch_wallet", disable_private_keys=True)
+ self.nodes[0].importaddress(self.wallet.get_address())
+
peer_inv_store = self.nodes[0].add_p2p_connection(P2PTxInvStore()) # keep track of invs
- self.generate(self.nodes[0], COINBASE_MATURITY + 1)
- utxo = self.nodes[0].listunspent(10)
- txid = utxo[0]['txid']
- vout = utxo[0]['vout']
- value = utxo[0]['amount']
- assert 'ancestorcount' not in utxo[0]
- assert 'ancestorsize' not in utxo[0]
- assert 'ancestorfees' not in utxo[0]
-
- fee = Decimal("0.0001")
+
# DEFAULT_ANCESTOR_LIMIT transactions off a confirmed tx should be fine
- chain = []
- witness_chain = []
+ chain = self.wallet.create_self_transfer_chain(chain_length=DEFAULT_ANCESTOR_LIMIT)
+ witness_chain = [t["wtxid"] for t in chain]
ancestor_vsize = 0
ancestor_fees = Decimal(0)
- for i in range(DEFAULT_ANCESTOR_LIMIT):
- (txid, sent_value) = chain_transaction(self.nodes[0], [txid], [0], value, fee, 1)
- value = sent_value
- chain.append(txid)
- # We need the wtxids to check P2P announcements
- witnesstx = self.nodes[0].gettransaction(txid=txid, verbose=True)['decoded']
- witness_chain.append(witnesstx['hash'])
+ for i, t in enumerate(chain):
+ ancestor_vsize += t["tx"].get_vsize()
+ ancestor_fees += t["fee"]
+ self.wallet.sendrawtransaction(from_node=self.nodes[0], tx_hex=t["hex"])
# Check that listunspent ancestor{count, size, fees} yield the correct results
- wallet_unspent = self.nodes[0].listunspent(minconf=0)
- this_unspent = next(utxo_info for utxo_info in wallet_unspent if utxo_info['txid'] == txid)
- assert_equal(this_unspent['ancestorcount'], i + 1)
- ancestor_vsize += self.nodes[0].getrawtransaction(txid=txid, verbose=True)['vsize']
- assert_equal(this_unspent['ancestorsize'], ancestor_vsize)
- ancestor_fees -= self.nodes[0].gettransaction(txid=txid)['fee']
- assert_equal(this_unspent['ancestorfees'], ancestor_fees * COIN)
+ if self.is_specified_wallet_compiled():
+ wallet_unspent = self.nodes[0].listunspent(minconf=0)
+ this_unspent = next(utxo_info for utxo_info in wallet_unspent if utxo_info["txid"] == t["txid"])
+ assert_equal(this_unspent['ancestorcount'], i + 1)
+ assert_equal(this_unspent['ancestorsize'], ancestor_vsize)
+ assert_equal(this_unspent['ancestorfees'], ancestor_fees * COIN)
# Wait until mempool transactions have passed initial broadcast (sent inv and received getdata)
# Otherwise, getrawmempool may be inconsistent with getmempoolentry if unbroadcast changes in between
@@ -99,15 +87,20 @@ class MempoolPackagesTest(BitcoinTestFramework):
ancestor_count = DEFAULT_ANCESTOR_LIMIT
assert_equal(ancestor_fees, sum([mempool[tx]['fees']['base'] for tx in mempool]))
+ # Adding one more transaction on to the chain should fail.
+ next_hop = self.wallet.create_self_transfer(utxo_to_spend=chain[-1]["new_utxo"])["hex"]
+ assert_raises_rpc_error(-26, "too-long-mempool-chain", lambda: self.nodes[0].sendrawtransaction(next_hop))
+
descendants = []
- ancestors = list(chain)
+ ancestors = [t["txid"] for t in chain]
+ chain = [t["txid"] for t in chain]
for x in reversed(chain):
# Check that getmempoolentry is consistent with getrawmempool
entry = self.nodes[0].getmempoolentry(x)
assert_equal(entry, mempool[x])
# Check that gettxspendingprevout is consistent with getrawmempool
- witnesstx = self.nodes[0].gettransaction(txid=x, verbose=True)['decoded']
+ witnesstx = self.nodes[0].getrawtransaction(txid=x, verbose=True)
for tx_in in witnesstx["vin"]:
spending_result = self.nodes[0].gettxspendingprevout([ {'txid' : tx_in["txid"], 'vout' : tx_in["vout"]} ])
assert_equal(spending_result, [ {'txid' : tx_in["txid"], 'vout' : tx_in["vout"], 'spendingtxid' : x} ])
@@ -193,9 +186,6 @@ class MempoolPackagesTest(BitcoinTestFramework):
descendant_fees += entry['fees']['base']
assert_equal(entry['fees']['descendant'], descendant_fees + Decimal('0.00001'))
- # Adding one more transaction on to the chain should fail.
- assert_raises_rpc_error(-26, "too-long-mempool-chain", chain_transaction, self.nodes[0], [txid], [vout], value, fee, 1)
-
# Check that prioritising a tx before it's added to the mempool works
# First clear the mempool by mining a block.
self.generate(self.nodes[0], 1)
@@ -232,28 +222,23 @@ class MempoolPackagesTest(BitcoinTestFramework):
# TODO: test ancestor size limits
# Now test descendant chain limits
- txid = utxo[1]['txid']
- value = utxo[1]['amount']
- vout = utxo[1]['vout']
- transaction_package = []
tx_children = []
# First create one parent tx with 10 children
- (txid, sent_value) = chain_transaction(self.nodes[0], [txid], [vout], value, fee, 10)
- parent_transaction = txid
- for i in range(10):
- transaction_package.append({'txid': txid, 'vout': i, 'amount': sent_value})
+ tx_with_children = self.wallet.send_self_transfer_multi(from_node=self.nodes[0], num_outputs=10)
+ parent_transaction = tx_with_children["txid"]
+ transaction_package = tx_with_children["new_utxos"]
# Sign and send up to MAX_DESCENDANT transactions chained off the parent tx
chain = [] # save sent txs for the purpose of checking node1's mempool later (see below)
for _ in range(DEFAULT_DESCENDANT_LIMIT - 1):
utxo = transaction_package.pop(0)
- (txid, sent_value) = chain_transaction(self.nodes[0], [utxo['txid']], [utxo['vout']], utxo['amount'], fee, 10)
+ new_tx = self.wallet.send_self_transfer_multi(from_node=self.nodes[0], num_outputs=10, utxos_to_spend=[utxo])
+ txid = new_tx["txid"]
chain.append(txid)
if utxo['txid'] is parent_transaction:
tx_children.append(txid)
- for j in range(10):
- transaction_package.append({'txid': txid, 'vout': j, 'amount': sent_value})
+ transaction_package.extend(new_tx["new_utxos"])
mempool = self.nodes[0].getrawmempool(True)
assert_equal(mempool[parent_transaction]['descendantcount'], DEFAULT_DESCENDANT_LIMIT)
@@ -263,8 +248,8 @@ class MempoolPackagesTest(BitcoinTestFramework):
assert_equal(mempool[child]['depends'], [parent_transaction])
# Sending one more chained transaction will fail
- utxo = transaction_package.pop(0)
- assert_raises_rpc_error(-26, "too-long-mempool-chain", chain_transaction, self.nodes[0], [utxo['txid']], [utxo['vout']], utxo['amount'], fee, 10)
+ next_hop = self.wallet.create_self_transfer(utxo_to_spend=transaction_package.pop(0))["hex"]
+ assert_raises_rpc_error(-26, "too-long-mempool-chain", lambda: self.nodes[0].sendrawtransaction(next_hop))
# Check that node1's mempool is as expected, containing:
# - txs from previous ancestor test (-> custom ancestor limit)
@@ -304,42 +289,19 @@ class MempoolPackagesTest(BitcoinTestFramework):
# last block.
# Create tx0 with 2 outputs
- utxo = self.nodes[0].listunspent()
- txid = utxo[0]['txid']
- value = utxo[0]['amount']
- vout = utxo[0]['vout']
-
- send_value = (value - fee) / 2
- inputs = [ {'txid' : txid, 'vout' : vout} ]
- outputs = {}
- for _ in range(2):
- outputs[self.nodes[0].getnewaddress()] = send_value
- rawtx = self.nodes[0].createrawtransaction(inputs, outputs)
- signedtx = self.nodes[0].signrawtransactionwithwallet(rawtx)
- txid = self.nodes[0].sendrawtransaction(signedtx['hex'])
- tx0_id = txid
- value = send_value
+ tx0 = self.wallet.send_self_transfer_multi(from_node=self.nodes[0], num_outputs=2)
# Create tx1
- tx1_id, _ = chain_transaction(self.nodes[0], [tx0_id], [0], value, fee, 1)
+ tx1 = self.wallet.send_self_transfer(from_node=self.nodes[0], utxo_to_spend=tx0["new_utxos"][0])
# Create tx2-7
- vout = 1
- txid = tx0_id
- for _ in range(6):
- (txid, sent_value) = chain_transaction(self.nodes[0], [txid], [vout], value, fee, 1)
- vout = 0
- value = sent_value
+ tx7 = self.wallet.send_self_transfer_chain(from_node=self.nodes[0], utxo_to_spend=tx0["new_utxos"][1], chain_length=6)[-1]
# Mine these in a block
self.generate(self.nodes[0], 1)
# Now generate tx8, with a big fee
- inputs = [ {'txid' : tx1_id, 'vout': 0}, {'txid' : txid, 'vout': 0} ]
- outputs = { self.nodes[0].getnewaddress() : send_value + value - 4*fee }
- rawtx = self.nodes[0].createrawtransaction(inputs, outputs)
- signedtx = self.nodes[0].signrawtransactionwithwallet(rawtx)
- txid = self.nodes[0].sendrawtransaction(signedtx['hex'])
+ self.wallet.send_self_transfer_multi(from_node=self.nodes[0], utxos_to_spend=[tx1["new_utxo"], tx7["new_utxo"]], fee_per_output=40000)
self.sync_mempools()
# Now try to disconnect the tip on each node...
diff --git a/test/functional/mempool_persist.py b/test/functional/mempool_persist.py
index dca4a71bd0..f818801136 100755
--- a/test/functional/mempool_persist.py
+++ b/test/functional/mempool_persist.py
@@ -59,7 +59,6 @@ class MempoolPersistTest(BitcoinTestFramework):
def run_test(self):
self.mini_wallet = MiniWallet(self.nodes[2])
- self.mini_wallet.rescan_utxos()
if self.is_sqlite_compiled():
self.nodes[2].createwallet(
wallet_name="watch",
diff --git a/test/functional/mempool_reorg.py b/test/functional/mempool_reorg.py
index 83ab65f1ba..3a5bc1ebcd 100755
--- a/test/functional/mempool_reorg.py
+++ b/test/functional/mempool_reorg.py
@@ -31,7 +31,6 @@ class MempoolCoinbaseTest(BitcoinTestFramework):
self.log.info("Add 4 coinbase utxos to the miniwallet")
# Block 76 contains the first spendable coinbase txs.
first_block = 76
- wallet.rescan_utxos()
# Three scenarios for re-orging coinbase spends in the memory pool:
# 1. Direct coinbase spend : spend_1
diff --git a/test/functional/mempool_resurrect.py b/test/functional/mempool_resurrect.py
index 3e610d02ac..c10052372d 100755
--- a/test/functional/mempool_resurrect.py
+++ b/test/functional/mempool_resurrect.py
@@ -4,7 +4,6 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test resurrection of mined transactions when the blockchain is re-organized."""
-from test_framework.blocktools import COINBASE_MATURITY
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_equal
from test_framework.wallet import MiniWallet
@@ -13,16 +12,11 @@ from test_framework.wallet import MiniWallet
class MempoolCoinbaseTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 1
- self.setup_clean_chain = True
def run_test(self):
node = self.nodes[0]
wallet = MiniWallet(node)
- # Add enough mature utxos to the wallet so that all txs spend confirmed coins
- self.generate(wallet, 3)
- self.generate(node, COINBASE_MATURITY)
-
# Spend block 1/2/3's coinbase transactions
# Mine a block
# Create three more transactions, spending the spends
diff --git a/test/functional/mempool_spend_coinbase.py b/test/functional/mempool_spend_coinbase.py
index bca512445c..a7cb2ba602 100755
--- a/test/functional/mempool_spend_coinbase.py
+++ b/test/functional/mempool_spend_coinbase.py
@@ -28,7 +28,6 @@ class MempoolSpendCoinbaseTest(BitcoinTestFramework):
chain_height = 198
self.nodes[0].invalidateblock(self.nodes[0].getblockhash(chain_height + 1))
assert_equal(chain_height, self.nodes[0].getblockcount())
- wallet.rescan_utxos()
# Coinbase at height chain_height-100+1 ok in mempool, should
# get mined. Coinbase at height chain_height-100+2 is
diff --git a/test/functional/mempool_unbroadcast.py b/test/functional/mempool_unbroadcast.py
index 1b0097d578..12de750731 100755
--- a/test/functional/mempool_unbroadcast.py
+++ b/test/functional/mempool_unbroadcast.py
@@ -23,7 +23,6 @@ class MempoolUnbroadcastTest(BitcoinTestFramework):
def run_test(self):
self.wallet = MiniWallet(self.nodes[0])
- self.wallet.rescan_utxos()
self.test_broadcast()
self.test_txn_removal()
diff --git a/test/functional/mining_getblocktemplate_longpoll.py b/test/functional/mining_getblocktemplate_longpoll.py
index e928ee4936..ec492f9e72 100755
--- a/test/functional/mining_getblocktemplate_longpoll.py
+++ b/test/functional/mining_getblocktemplate_longpoll.py
@@ -8,7 +8,6 @@ from decimal import Decimal
import random
import threading
-from test_framework.blocktools import COINBASE_MATURITY
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import get_rpc_proxy
from test_framework.wallet import MiniWallet
@@ -62,9 +61,6 @@ class GetBlockTemplateLPTest(BitcoinTestFramework):
thr.join(5) # wait 5 seconds or until thread exits
assert not thr.is_alive()
- # Add enough mature utxos to the wallets, so that all txs spend confirmed coins
- self.generate(self.nodes[0], COINBASE_MATURITY)
-
self.log.info("Test that introducing a new transaction into the mempool will terminate the longpoll")
thr = LongpollThread(self.nodes[0])
thr.start()
diff --git a/test/functional/mining_prioritisetransaction.py b/test/functional/mining_prioritisetransaction.py
index 4a54f82b58..a4481c15a0 100755
--- a/test/functional/mining_prioritisetransaction.py
+++ b/test/functional/mining_prioritisetransaction.py
@@ -106,7 +106,6 @@ class PrioritiseTransactionTest(BitcoinTestFramework):
def run_test(self):
self.wallet = MiniWallet(self.nodes[0])
- self.wallet.rescan_utxos()
# Test `prioritisetransaction` required parameters
assert_raises_rpc_error(-1, "prioritisetransaction", self.nodes[0].prioritisetransaction)
diff --git a/test/functional/p2p_blocksonly.py b/test/functional/p2p_blocksonly.py
index fa9ddf7ebe..110a1bd03f 100755
--- a/test/functional/p2p_blocksonly.py
+++ b/test/functional/p2p_blocksonly.py
@@ -20,8 +20,6 @@ class P2PBlocksOnly(BitcoinTestFramework):
def run_test(self):
self.miniwallet = MiniWallet(self.nodes[0])
- # Add enough mature utxos to the wallet, so that all txs spend confirmed coins
- self.miniwallet.rescan_utxos()
self.blocksonly_mode_tests()
self.blocks_relay_conn_tests()
diff --git a/test/functional/p2p_disconnect_ban.py b/test/functional/p2p_disconnect_ban.py
index 7169cc8937..b2f0659eda 100755
--- a/test/functional/p2p_disconnect_ban.py
+++ b/test/functional/p2p_disconnect_ban.py
@@ -107,7 +107,7 @@ class DisconnectBanTest(BitcoinTestFramework):
self.log.info("disconnectnode: fail to disconnect when calling with address and nodeid")
address1 = self.nodes[0].getpeerinfo()[0]['addr']
- node1 = self.nodes[0].getpeerinfo()[0]['addr']
+ node1 = self.nodes[0].getpeerinfo()[0]["id"]
assert_raises_rpc_error(-32602, "Only one of address and nodeid should be provided.", self.nodes[0].disconnectnode, address=address1, nodeid=node1)
self.log.info("disconnectnode: fail to disconnect when calling with junk address")
diff --git a/test/functional/p2p_eviction.py b/test/functional/p2p_eviction.py
index 1f4797a89d..8b31dfa549 100755
--- a/test/functional/p2p_eviction.py
+++ b/test/functional/p2p_eviction.py
@@ -12,22 +12,23 @@ address/netgroup since in the current framework, all peers are connecting from
the same local address. See Issue #14210 for more info.
Therefore, this test is limited to the remaining protection criteria.
"""
-
import time
from test_framework.blocktools import (
- COINBASE_MATURITY,
create_block,
create_coinbase,
)
from test_framework.messages import (
msg_pong,
msg_tx,
- tx_from_hex,
)
-from test_framework.p2p import P2PDataStore, P2PInterface
+from test_framework.p2p import (
+ P2PDataStore,
+ P2PInterface,
+)
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_equal
+from test_framework.wallet import MiniWallet
class SlowP2PDataStore(P2PDataStore):
@@ -35,14 +36,15 @@ class SlowP2PDataStore(P2PDataStore):
time.sleep(0.1)
self.send_message(msg_pong(message.nonce))
+
class SlowP2PInterface(P2PInterface):
def on_ping(self, message):
time.sleep(0.1)
self.send_message(msg_pong(message.nonce))
+
class P2PEvict(BitcoinTestFramework):
def set_test_params(self):
- self.setup_clean_chain = True
self.num_nodes = 1
# The choice of maxconnections=32 results in a maximum of 21 inbound connections
# (32 - 10 outbound - 1 feeler). 20 inbound peers are protected from eviction:
@@ -53,7 +55,7 @@ class P2PEvict(BitcoinTestFramework):
protected_peers = set() # peers that we expect to be protected from eviction
current_peer = -1
node = self.nodes[0]
- self.generatetoaddress(node, COINBASE_MATURITY + 1, node.get_deterministic_priv_key().address)
+ self.wallet = MiniWallet(node)
self.log.info("Create 4 peers and protect them from eviction by sending us a block")
for _ in range(4):
@@ -79,21 +81,8 @@ class P2PEvict(BitcoinTestFramework):
current_peer += 1
txpeer.sync_with_ping()
- prevtx = node.getblock(node.getblockhash(i + 1), 2)['tx'][0]
- rawtx = node.createrawtransaction(
- inputs=[{'txid': prevtx['txid'], 'vout': 0}],
- outputs=[{node.get_deterministic_priv_key().address: 50 - 0.00125}],
- )
- sigtx = node.signrawtransactionwithkey(
- hexstring=rawtx,
- privkeys=[node.get_deterministic_priv_key().key],
- prevtxs=[{
- 'txid': prevtx['txid'],
- 'vout': 0,
- 'scriptPubKey': prevtx['vout'][0]['scriptPubKey']['hex'],
- }],
- )['hex']
- txpeer.send_message(msg_tx(tx_from_hex(sigtx)))
+ tx = self.wallet.create_self_transfer()['tx']
+ txpeer.send_message(msg_tx(tx))
protected_peers.add(current_peer)
self.log.info("Create 8 peers and protect them from eviction by having faster pings")
@@ -133,5 +122,6 @@ class P2PEvict(BitcoinTestFramework):
self.log.debug("{} protected peers: {}".format(len(protected_peers), protected_peers))
assert evicted_peers[0] not in protected_peers
+
if __name__ == '__main__':
P2PEvict().main()
diff --git a/test/functional/p2p_feefilter.py b/test/functional/p2p_feefilter.py
index b65e927d5b..6b03cdf877 100755
--- a/test/functional/p2p_feefilter.py
+++ b/test/functional/p2p_feefilter.py
@@ -6,7 +6,6 @@
from decimal import Decimal
-from test_framework.blocktools import COINBASE_MATURITY
from test_framework.messages import MSG_TX, MSG_WTX, msg_feefilter
from test_framework.p2p import P2PInterface, p2p_lock
from test_framework.test_framework import BitcoinTestFramework
@@ -80,9 +79,6 @@ class FeeFilterTest(BitcoinTestFramework):
node1 = self.nodes[1]
node0 = self.nodes[0]
miniwallet = MiniWallet(node1)
- # Add enough mature utxos to the wallet, so that all txs spend confirmed coins
- self.generate(miniwallet, 5)
- self.generate(node1, COINBASE_MATURITY)
conn = self.nodes[0].add_p2p_connection(TestP2PConn())
diff --git a/test/functional/p2p_filter.py b/test/functional/p2p_filter.py
index 3cf92b0316..b3e68ca536 100755
--- a/test/functional/p2p_filter.py
+++ b/test/functional/p2p_filter.py
@@ -214,7 +214,6 @@ class FilterTest(BitcoinTestFramework):
def run_test(self):
self.wallet = MiniWallet(self.nodes[0])
- self.wallet.rescan_utxos()
filter_peer = self.nodes[0].add_p2p_connection(P2PBloomFilter())
self.log.info('Test filter size limits')
diff --git a/test/functional/p2p_headers_sync_with_minchainwork.py b/test/functional/p2p_headers_sync_with_minchainwork.py
index b07077c668..832fd7e0e9 100755
--- a/test/functional/p2p_headers_sync_with_minchainwork.py
+++ b/test/functional/p2p_headers_sync_with_minchainwork.py
@@ -27,6 +27,7 @@ NODE2_BLOCKS_REQUIRED = 2047
class RejectLowDifficultyHeadersTest(BitcoinTestFramework):
def set_test_params(self):
+ self.rpc_timeout *= 4 # To avoid timeout when generating BLOCKS_TO_MINE
self.setup_clean_chain = True
self.num_nodes = 4
# Node0 has no required chainwork; node1 requires 15 blocks on top of the genesis block; node2 requires 2047
diff --git a/test/functional/p2p_ibd_stalling.py b/test/functional/p2p_ibd_stalling.py
new file mode 100755
index 0000000000..aca98ceb3f
--- /dev/null
+++ b/test/functional/p2p_ibd_stalling.py
@@ -0,0 +1,164 @@
+#!/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 stalling logic during IBD
+"""
+
+import time
+
+from test_framework.blocktools import (
+ create_block,
+ create_coinbase
+)
+from test_framework.messages import (
+ MSG_BLOCK,
+ MSG_TYPE_MASK,
+)
+from test_framework.p2p import (
+ CBlockHeader,
+ msg_block,
+ msg_headers,
+ P2PDataStore,
+)
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import (
+ assert_equal,
+)
+
+
+class P2PStaller(P2PDataStore):
+ def __init__(self, stall_block):
+ self.stall_block = stall_block
+ super().__init__()
+
+ def on_getdata(self, message):
+ for inv in message.inv:
+ self.getdata_requests.append(inv.hash)
+ if (inv.type & MSG_TYPE_MASK) == MSG_BLOCK:
+ if (inv.hash != self.stall_block):
+ self.send_message(msg_block(self.block_store[inv.hash]))
+
+ def on_getheaders(self, message):
+ pass
+
+
+class P2PIBDStallingTest(BitcoinTestFramework):
+ def set_test_params(self):
+ self.setup_clean_chain = True
+ self.num_nodes = 1
+
+ def run_test(self):
+ NUM_BLOCKS = 1025
+ NUM_PEERS = 4
+ node = self.nodes[0]
+ tip = int(node.getbestblockhash(), 16)
+ blocks = []
+ height = 1
+ block_time = node.getblock(node.getbestblockhash())['time'] + 1
+ self.log.info("Prepare blocks without sending them to the node")
+ block_dict = {}
+ for _ in range(NUM_BLOCKS):
+ blocks.append(create_block(tip, create_coinbase(height), block_time))
+ blocks[-1].solve()
+ tip = blocks[-1].sha256
+ block_time += 1
+ height += 1
+ block_dict[blocks[-1].sha256] = blocks[-1]
+ stall_block = blocks[0].sha256
+
+ headers_message = msg_headers()
+ headers_message.headers = [CBlockHeader(b) for b in blocks[:NUM_BLOCKS-1]]
+ peers = []
+
+ self.log.info("Check that a staller does not get disconnected if the 1024 block lookahead buffer is filled")
+ for id in range(NUM_PEERS):
+ peers.append(node.add_outbound_p2p_connection(P2PStaller(stall_block), p2p_idx=id, connection_type="outbound-full-relay"))
+ peers[-1].block_store = block_dict
+ peers[-1].send_message(headers_message)
+
+ # Need to wait until 1023 blocks are received - the magic total bytes number is a workaround in lack of an rpc
+ # returning the number of downloaded (but not connected) blocks.
+ self.wait_until(lambda: self.total_bytes_recv_for_blocks() == 172761)
+
+ self.all_sync_send_with_ping(peers)
+ # If there was a peer marked for stalling, it would get disconnected
+ self.mocktime = int(time.time()) + 3
+ node.setmocktime(self.mocktime)
+ self.all_sync_send_with_ping(peers)
+ assert_equal(node.num_test_p2p_connections(), NUM_PEERS)
+
+ self.log.info("Check that increasing the window beyond 1024 blocks triggers stalling logic")
+ headers_message.headers = [CBlockHeader(b) for b in blocks]
+ with node.assert_debug_log(expected_msgs=['Stall started']):
+ for p in peers:
+ p.send_message(headers_message)
+ self.all_sync_send_with_ping(peers)
+
+ self.log.info("Check that the stalling peer is disconnected after 2 seconds")
+ self.mocktime += 3
+ node.setmocktime(self.mocktime)
+ peers[0].wait_for_disconnect()
+ assert_equal(node.num_test_p2p_connections(), NUM_PEERS - 1)
+ self.wait_until(lambda: self.is_block_requested(peers, stall_block))
+ # Make sure that SendMessages() is invoked, which assigns the missing block
+ # to another peer and starts the stalling logic for them
+ self.all_sync_send_with_ping(peers)
+
+ self.log.info("Check that the stalling timeout gets doubled to 4 seconds for the next staller")
+ # No disconnect after just 3 seconds
+ self.mocktime += 3
+ node.setmocktime(self.mocktime)
+ self.all_sync_send_with_ping(peers)
+ assert_equal(node.num_test_p2p_connections(), NUM_PEERS - 1)
+
+ self.mocktime += 2
+ node.setmocktime(self.mocktime)
+ self.wait_until(lambda: sum(x.is_connected for x in node.p2ps) == NUM_PEERS - 2)
+ self.wait_until(lambda: self.is_block_requested(peers, stall_block))
+ self.all_sync_send_with_ping(peers)
+
+ self.log.info("Check that the stalling timeout gets doubled to 8 seconds for the next staller")
+ # No disconnect after just 7 seconds
+ self.mocktime += 7
+ node.setmocktime(self.mocktime)
+ self.all_sync_send_with_ping(peers)
+ assert_equal(node.num_test_p2p_connections(), NUM_PEERS - 2)
+
+ self.mocktime += 2
+ node.setmocktime(self.mocktime)
+ self.wait_until(lambda: sum(x.is_connected for x in node.p2ps) == NUM_PEERS - 3)
+ self.wait_until(lambda: self.is_block_requested(peers, stall_block))
+ self.all_sync_send_with_ping(peers)
+
+ self.log.info("Provide the withheld block and check that stalling timeout gets reduced back to 2 seconds")
+ with node.assert_debug_log(expected_msgs=['Decreased stalling timeout to 2 seconds']):
+ for p in peers:
+ if p.is_connected and (stall_block in p.getdata_requests):
+ p.send_message(msg_block(block_dict[stall_block]))
+
+ self.log.info("Check that all outstanding blocks get connected")
+ self.wait_until(lambda: node.getblockcount() == NUM_BLOCKS)
+
+ def total_bytes_recv_for_blocks(self):
+ total = 0
+ for info in self.nodes[0].getpeerinfo():
+ if ("block" in info["bytesrecv_per_msg"].keys()):
+ total += info["bytesrecv_per_msg"]["block"]
+ return total
+
+ def all_sync_send_with_ping(self, peers):
+ for p in peers:
+ if p.is_connected:
+ p.sync_send_with_ping()
+
+ def is_block_requested(self, peers, hash):
+ for p in peers:
+ if p.is_connected and (hash in p.getdata_requests):
+ return True
+ return False
+
+
+if __name__ == '__main__':
+ P2PIBDStallingTest().main()
diff --git a/test/functional/p2p_leak_tx.py b/test/functional/p2p_leak_tx.py
index 4c064b359e..6283dd89ac 100755
--- a/test/functional/p2p_leak_tx.py
+++ b/test/functional/p2p_leak_tx.py
@@ -4,7 +4,6 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test that we don't leak txs to inbound peers that we haven't yet announced to"""
-from test_framework.blocktools import COINBASE_MATURITY
from test_framework.messages import msg_getdata, CInv, MSG_TX
from test_framework.p2p import p2p_lock, P2PDataStore
from test_framework.test_framework import BitcoinTestFramework
@@ -26,9 +25,6 @@ class P2PLeakTxTest(BitcoinTestFramework):
def run_test(self):
gen_node = self.nodes[0] # The block and tx generating node
miniwallet = MiniWallet(gen_node)
- # Add enough mature utxos to the wallet, so that all txs spend confirmed coins
- self.generate(miniwallet, 1)
- self.generate(gen_node, COINBASE_MATURITY)
inbound_peer = self.nodes[0].add_p2p_connection(P2PNode()) # An "attacking" inbound peer
diff --git a/test/functional/p2p_permissions.py b/test/functional/p2p_permissions.py
index f8d3fd919d..f84bbf67e6 100755
--- a/test/functional/p2p_permissions.py
+++ b/test/functional/p2p_permissions.py
@@ -7,30 +7,26 @@
Test that permissions are correctly calculated and applied
"""
-from test_framework.address import ADDRESS_BCRT1_P2WSH_OP_TRUE
from test_framework.messages import (
- CTxInWitness,
- tx_from_hex,
+ SEQUENCE_FINAL,
)
from test_framework.p2p import P2PDataStore
-from test_framework.script import (
- CScript,
- OP_TRUE,
-)
from test_framework.test_node import ErrorMatch
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
p2p_port,
)
+from test_framework.wallet import MiniWallet
class P2PPermissionsTests(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 2
- self.setup_clean_chain = True
def run_test(self):
+ self.wallet = MiniWallet(self.nodes[0])
+
self.check_tx_relay()
self.checkpermission(
@@ -60,12 +56,12 @@ class P2PPermissionsTests(BitcoinTestFramework):
# For this, we need to use whitebind instead of bind
# by modifying the configuration file.
ip_port = "127.0.0.1:{}".format(p2p_port(1))
- self.replaceinconfig(1, "bind=127.0.0.1", "whitebind=bloomfilter,forcerelay@" + ip_port)
+ self.nodes[1].replace_in_config([("bind=127.0.0.1", "whitebind=bloomfilter,forcerelay@" + ip_port)])
self.checkpermission(
["-whitelist=noban@127.0.0.1"],
# Check parameter interaction forcerelay should activate relay
["noban", "bloomfilter", "forcerelay", "relay", "download"])
- self.replaceinconfig(1, "whitebind=bloomfilter,forcerelay@" + ip_port, "bind=127.0.0.1")
+ self.nodes[1].replace_in_config([("whitebind=bloomfilter,forcerelay@" + ip_port, "bind=127.0.0.1")])
self.checkpermission(
# legacy whitelistrelay should be ignored
@@ -94,8 +90,6 @@ class P2PPermissionsTests(BitcoinTestFramework):
self.nodes[1].assert_start_raises_init_error(["-whitebind=noban@127.0.0.1", "-bind=127.0.0.1", "-listen=0"], "Cannot set -bind or -whitebind together with -listen=0", match=ErrorMatch.PARTIAL_REGEX)
def check_tx_relay(self):
- block_op_true = self.nodes[0].getblock(self.generatetoaddress(self.nodes[0], 100, ADDRESS_BCRT1_P2WSH_OP_TRUE)[0])
-
self.log.debug("Create a connection from a forcerelay peer that rebroadcasts raw txs")
# A test framework p2p connection is needed to send the raw transaction directly. If a full node was used, it could only
# rebroadcast via the inv-getdata mechanism. However, even for forcerelay connections, a full node would
@@ -104,18 +98,7 @@ class P2PPermissionsTests(BitcoinTestFramework):
p2p_rebroadcast_wallet = self.nodes[1].add_p2p_connection(P2PDataStore())
self.log.debug("Send a tx from the wallet initially")
- tx = tx_from_hex(
- self.nodes[0].createrawtransaction(
- inputs=[{
- 'txid': block_op_true['tx'][0],
- 'vout': 0,
- }], outputs=[{
- ADDRESS_BCRT1_P2WSH_OP_TRUE: 5,
- }],
- replaceable=False),
- )
- tx.wit.vtxinwit = [CTxInWitness()]
- tx.wit.vtxinwit[0].scriptWitness.stack = [CScript([OP_TRUE])]
+ tx = self.wallet.create_self_transfer(sequence=SEQUENCE_FINAL)['tx']
txid = tx.rehash()
self.log.debug("Wait until tx is in node[1]'s mempool")
@@ -155,12 +138,6 @@ class P2PPermissionsTests(BitcoinTestFramework):
if p not in peerinfo['permissions']:
raise AssertionError("Expected permissions %r is not granted." % p)
- def replaceinconfig(self, nodeid, old, new):
- with open(self.nodes[nodeid].bitcoinconf, encoding="utf8") as f:
- newText = f.read().replace(old, new)
- with open(self.nodes[nodeid].bitcoinconf, 'w', encoding="utf8") as f:
- f.write(newText)
-
if __name__ == '__main__':
P2PPermissionsTests().main()
diff --git a/test/functional/p2p_tx_download.py b/test/functional/p2p_tx_download.py
index 7356b8bbb3..0e463c5072 100755
--- a/test/functional/p2p_tx_download.py
+++ b/test/functional/p2p_tx_download.py
@@ -5,6 +5,7 @@
"""
Test transaction download behavior
"""
+import time
from test_framework.messages import (
CInv,
@@ -13,7 +14,6 @@ from test_framework.messages import (
MSG_WTX,
msg_inv,
msg_notfound,
- tx_from_hex,
)
from test_framework.p2p import (
P2PInterface,
@@ -23,9 +23,7 @@ from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
)
-from test_framework.address import ADDRESS_BCRT1_UNSPENDABLE
-
-import time
+from test_framework.wallet import MiniWallet
class TestP2PConn(P2PInterface):
@@ -88,19 +86,8 @@ class TxDownloadTest(BitcoinTestFramework):
def test_inv_block(self):
self.log.info("Generate a transaction on node 0")
- tx = self.nodes[0].createrawtransaction(
- inputs=[{ # coinbase
- "txid": self.nodes[0].getblock(self.nodes[0].getblockhash(1))['tx'][0],
- "vout": 0
- }],
- outputs={ADDRESS_BCRT1_UNSPENDABLE: 50 - 0.00025},
- )
- tx = self.nodes[0].signrawtransactionwithkey(
- hexstring=tx,
- privkeys=[self.nodes[0].get_deterministic_priv_key().key],
- )['hex']
- ctx = tx_from_hex(tx)
- txid = int(ctx.rehash(), 16)
+ tx = self.wallet.create_self_transfer()
+ txid = int(tx['txid'], 16)
self.log.info(
"Announce the transaction to all nodes from all {} incoming peers, but never send it".format(NUM_INBOUND))
@@ -109,7 +96,7 @@ class TxDownloadTest(BitcoinTestFramework):
p.send_and_ping(msg)
self.log.info("Put the tx in node 0's mempool")
- self.nodes[0].sendrawtransaction(tx)
+ self.nodes[0].sendrawtransaction(tx['hex'])
# Since node 1 is connected outbound to an honest peer (node 0), it
# should get the tx within a timeout. (Assuming that node 0
@@ -255,6 +242,8 @@ class TxDownloadTest(BitcoinTestFramework):
self.nodes[0].p2ps[0].send_message(msg_notfound(vec=[CInv(MSG_TX, 1)]))
def run_test(self):
+ self.wallet = MiniWallet(self.nodes[0])
+
# Run tests without mocktime that only need one peer-connection first, to avoid restarting the nodes
self.test_expiry_fallback()
self.test_disconnect_fallback()
diff --git a/test/functional/p2p_tx_privacy.py b/test/functional/p2p_tx_privacy.py
index b885ccdf5d..e674f6c3eb 100755
--- a/test/functional/p2p_tx_privacy.py
+++ b/test/functional/p2p_tx_privacy.py
@@ -53,7 +53,6 @@ class TxPrivacyTest(BitcoinTestFramework):
def run_test(self):
self.wallet = MiniWallet(self.nodes[0])
- self.wallet.rescan_utxos()
tx_originator = self.nodes[0].add_p2p_connection(P2PInterface())
spy = self.nodes[0].add_p2p_connection(P2PTxSpy(), wait_for_verack=False)
diff --git a/test/functional/rpc_blockchain.py b/test/functional/rpc_blockchain.py
index 19c73eebf0..7a0cedb1f5 100755
--- a/test/functional/rpc_blockchain.py
+++ b/test/functional/rpc_blockchain.py
@@ -25,6 +25,7 @@ from decimal import Decimal
import http.client
import os
import subprocess
+import textwrap
from test_framework.blocktools import (
MAX_FUTURE_BLOCK_TIME,
@@ -429,6 +430,17 @@ class BlockchainTest(BitcoinTestFramework):
def _test_getnetworkhashps(self):
self.log.info("Test getnetworkhashps")
hashes_per_second = self.nodes[0].getnetworkhashps()
+ assert_raises_rpc_error(
+ -3,
+ textwrap.dedent("""
+ Wrong type passed:
+ {
+ "Position 1 (nblocks)": "JSON value of type string is not of expected type number",
+ "Position 2 (height)": "JSON value of type array is not of expected type number"
+ }
+ """).strip(),
+ lambda: self.nodes[0].getnetworkhashps("a", []),
+ )
# This should be 2 hashes every 10 minutes or 1/300
assert abs(hashes_per_second * 300 - 1) < 0.0001
diff --git a/test/functional/rpc_generate.py b/test/functional/rpc_generate.py
index 89b410e37e..8948ccb48d 100755
--- a/test/functional/rpc_generate.py
+++ b/test/functional/rpc_generate.py
@@ -28,7 +28,6 @@ class RPCGenerateTest(BitcoinTestFramework):
def test_generateblock(self):
node = self.nodes[0]
miniwallet = MiniWallet(node)
- miniwallet.rescan_utxos()
self.log.info('Generate an empty block to address')
address = miniwallet.get_address()
diff --git a/test/functional/rpc_invalid_address_message.py b/test/functional/rpc_invalid_address_message.py
index 452f857a44..0c29efb85a 100755
--- a/test/functional/rpc_invalid_address_message.py
+++ b/test/functional/rpc_invalid_address_message.py
@@ -95,15 +95,19 @@ class InvalidAddressErrorMessageTest(BitcoinTestFramework):
self.check_invalid(INVALID_ADDRESS, 'Not a valid Bech32 or Base58 encoding')
self.check_invalid(INVALID_ADDRESS_2, 'Not a valid Bech32 or Base58 encoding')
+ node = self.nodes[0]
+
+ # Missing arg returns the help text
+ assert_raises_rpc_error(-1, "Return information about the given bitcoin address.", node.validateaddress)
+ # Explicit None is not allowed for required parameters
+ assert_raises_rpc_error(-3, "JSON value of type null is not of expected type string", node.validateaddress, None)
+
def test_getaddressinfo(self):
node = self.nodes[0]
assert_raises_rpc_error(-5, "Invalid Bech32 address data size", node.getaddressinfo, BECH32_INVALID_SIZE)
-
assert_raises_rpc_error(-5, "Not a valid Bech32 or Base58 encoding", node.getaddressinfo, BECH32_INVALID_PREFIX)
-
assert_raises_rpc_error(-5, "Invalid prefix for Base58-encoded address", node.getaddressinfo, BASE58_INVALID_PREFIX)
-
assert_raises_rpc_error(-5, "Not a valid Bech32 or Base58 encoding", node.getaddressinfo, INVALID_ADDRESS)
def run_test(self):
diff --git a/test/functional/rpc_mempool_info.py b/test/functional/rpc_mempool_info.py
index ae9c6572cf..246af22e50 100755
--- a/test/functional/rpc_mempool_info.py
+++ b/test/functional/rpc_mempool_info.py
@@ -18,7 +18,6 @@ class RPCMempoolInfoTest(BitcoinTestFramework):
def run_test(self):
self.wallet = MiniWallet(self.nodes[0])
- self.wallet.rescan_utxos()
confirmed_utxo = self.wallet.get_utxo()
# Create a tree of unconfirmed transactions in the mempool:
diff --git a/test/functional/rpc_net.py b/test/functional/rpc_net.py
index af8b2ad72b..5fdd5daddf 100755
--- a/test/functional/rpc_net.py
+++ b/test/functional/rpc_net.py
@@ -11,7 +11,6 @@ from decimal import Decimal
from itertools import product
import time
-from test_framework.blocktools import COINBASE_MATURITY
import test_framework.messages
from test_framework.p2p import (
P2PInterface,
@@ -43,7 +42,6 @@ def assert_net_servicesnames(servicesflag, servicenames):
class NetTest(BitcoinTestFramework):
def set_test_params(self):
- self.setup_clean_chain = True
self.num_nodes = 2
self.extra_args = [["-minrelaytxfee=0.00001000"], ["-minrelaytxfee=0.00000500"]]
self.supports_cli = False
@@ -51,9 +49,6 @@ class NetTest(BitcoinTestFramework):
def run_test(self):
# We need miniwallet to make a transaction
self.wallet = MiniWallet(self.nodes[0])
- self.generate(self.wallet, 1)
- # Get out of IBD for the minfeefilter and getpeerinfo tests.
- self.generate(self.nodes[0], COINBASE_MATURITY + 1)
# By default, the test framework sets up an addnode connection from
# node 1 --> node0. By connecting node0 --> node 1, we're left with
diff --git a/test/functional/rpc_packages.py b/test/functional/rpc_packages.py
index 10388399ad..6cb9760b3d 100755
--- a/test/functional/rpc_packages.py
+++ b/test/functional/rpc_packages.py
@@ -128,8 +128,8 @@ class RPCPackagesTest(BitcoinTestFramework):
node = self.nodes[0]
chain = self.wallet.create_self_transfer_chain(chain_length=25)
- chain_hex = chain["chain_hex"]
- chain_txns = chain["chain_txns"]
+ chain_hex = [t["hex"] for t in chain]
+ chain_txns = [t["tx"] for t in chain]
self.log.info("Check that testmempoolaccept requires packages to be sorted by dependency")
assert_equal(node.testmempoolaccept(rawtxs=chain_hex[::-1]),
@@ -374,7 +374,7 @@ class RPCPackagesTest(BitcoinTestFramework):
self.log.info("Submitpackage only allows packages of 1 child with its parents")
# Chain of 3 transactions has too many generations
- chain_hex = self.wallet.create_self_transfer_chain(chain_length=25)["chain_hex"]
+ chain_hex = [t["hex"] for t in self.wallet.create_self_transfer_chain(chain_length=25)]
assert_raises_rpc_error(-25, "not-child-with-parents", node.submitpackage, chain_hex)
diff --git a/test/functional/rpc_psbt.py b/test/functional/rpc_psbt.py
index a50e0fb244..58a80e37a2 100755
--- a/test/functional/rpc_psbt.py
+++ b/test/functional/rpc_psbt.py
@@ -36,6 +36,7 @@ from test_framework.util import (
assert_approx,
assert_equal,
assert_greater_than,
+ assert_greater_than_or_equal,
assert_raises_rpc_error,
find_output,
find_vout_for_address,
@@ -106,6 +107,65 @@ class PSBTTest(BitcoinTestFramework):
self.connect_nodes(0, 1)
self.connect_nodes(0, 2)
+ def test_input_confs_control(self):
+ self.nodes[0].createwallet("minconf")
+ wallet = self.nodes[0].get_wallet_rpc("minconf")
+
+ # Fund the wallet with different chain heights
+ for _ in range(2):
+ self.nodes[1].sendmany("", {wallet.getnewaddress():1, wallet.getnewaddress():1})
+ self.generate(self.nodes[1], 1)
+
+ unconfirmed_txid = wallet.sendtoaddress(wallet.getnewaddress(), 0.5)
+
+ self.log.info("Crafting PSBT using an unconfirmed input")
+ target_address = self.nodes[1].getnewaddress()
+ psbtx1 = wallet.walletcreatefundedpsbt([], {target_address: 0.1}, 0, {'fee_rate': 1, 'maxconf': 0})['psbt']
+
+ # Make sure we only had the one input
+ tx1_inputs = self.nodes[0].decodepsbt(psbtx1)['tx']['vin']
+ assert_equal(len(tx1_inputs), 1)
+
+ utxo1 = tx1_inputs[0]
+ assert_equal(unconfirmed_txid, utxo1['txid'])
+
+ signed_tx1 = wallet.walletprocesspsbt(psbtx1)['psbt']
+ final_tx1 = wallet.finalizepsbt(signed_tx1)['hex']
+ txid1 = self.nodes[0].sendrawtransaction(final_tx1)
+
+ mempool = self.nodes[0].getrawmempool()
+ assert txid1 in mempool
+
+ self.log.info("Fail to craft a new PSBT that sends more funds with add_inputs = False")
+ assert_raises_rpc_error(-4, "The preselected coins total amount does not cover the transaction target. Please allow other inputs to be automatically selected or include more coins manually", wallet.walletcreatefundedpsbt, [{'txid': utxo1['txid'], 'vout': utxo1['vout']}], {target_address: 1}, 0, {'add_inputs': False})
+
+ self.log.info("Fail to craft a new PSBT with minconf above highest one")
+ assert_raises_rpc_error(-4, "Insufficient funds", wallet.walletcreatefundedpsbt, [{'txid': utxo1['txid'], 'vout': utxo1['vout']}], {target_address: 1}, 0, {'add_inputs': True, 'minconf': 3, 'fee_rate': 10})
+
+ self.log.info("Fail to broadcast a new PSBT with maxconf 0 due to BIP125 rules to verify it actually chose unconfirmed outputs")
+ psbt_invalid = wallet.walletcreatefundedpsbt([{'txid': utxo1['txid'], 'vout': utxo1['vout']}], {target_address: 1}, 0, {'add_inputs': True, 'maxconf': 0, 'fee_rate': 10})['psbt']
+ signed_invalid = wallet.walletprocesspsbt(psbt_invalid)['psbt']
+ final_invalid = wallet.finalizepsbt(signed_invalid)['hex']
+ assert_raises_rpc_error(-26, "bad-txns-spends-conflicting-tx", self.nodes[0].sendrawtransaction, final_invalid)
+
+ self.log.info("Craft a replacement adding inputs with highest confs possible")
+ psbtx2 = wallet.walletcreatefundedpsbt([{'txid': utxo1['txid'], 'vout': utxo1['vout']}], {target_address: 1}, 0, {'add_inputs': True, 'minconf': 2, 'fee_rate': 10})['psbt']
+ tx2_inputs = self.nodes[0].decodepsbt(psbtx2)['tx']['vin']
+ assert_greater_than_or_equal(len(tx2_inputs), 2)
+ for vin in tx2_inputs:
+ if vin['txid'] != unconfirmed_txid:
+ assert_greater_than_or_equal(self.nodes[0].gettxout(vin['txid'], vin['vout'])['confirmations'], 2)
+
+ signed_tx2 = wallet.walletprocesspsbt(psbtx2)['psbt']
+ final_tx2 = wallet.finalizepsbt(signed_tx2)['hex']
+ txid2 = self.nodes[0].sendrawtransaction(final_tx2)
+
+ mempool = self.nodes[0].getrawmempool()
+ assert txid1 not in mempool
+ assert txid2 in mempool
+
+ wallet.unloadwallet()
+
def assert_change_type(self, psbtx, expected_type):
"""Assert that the given PSBT has a change output with the given type."""
@@ -514,6 +574,8 @@ class PSBTTest(BitcoinTestFramework):
# TODO: Re-enable this for segwit v1
# self.test_utxo_conversion()
+ self.test_input_confs_control()
+
# Test that psbts with p2pkh outputs are created properly
p2pkh = self.nodes[0].getnewaddress(address_type='legacy')
psbt = self.nodes[1].walletcreatefundedpsbt([], [{p2pkh : 1}], 0, {"includeWatching" : True}, True)
diff --git a/test/functional/rpc_rawtransaction.py b/test/functional/rpc_rawtransaction.py
index 1c91ab6f5f..cdec4b2a85 100755
--- a/test/functional/rpc_rawtransaction.py
+++ b/test/functional/rpc_rawtransaction.py
@@ -16,7 +16,6 @@ from collections import OrderedDict
from decimal import Decimal
from itertools import product
-from test_framework.blocktools import COINBASE_MATURITY
from test_framework.messages import (
MAX_BIP125_RBF_SEQUENCE,
CTransaction,
@@ -59,7 +58,6 @@ class RawTransactionsTest(BitcoinTestFramework):
self.add_wallet_options(parser, descriptors=False)
def set_test_params(self):
- self.setup_clean_chain = True
self.num_nodes = 3
self.extra_args = [
["-txindex"],
@@ -77,9 +75,6 @@ class RawTransactionsTest(BitcoinTestFramework):
def run_test(self):
self.wallet = MiniWallet(self.nodes[0])
- self.log.info("Prepare some coins for multiple *rawtransaction commands")
- self.generate(self.wallet, 10)
- self.generate(self.nodes[0], COINBASE_MATURITY + 1)
self.getrawtransaction_tests()
self.getrawtransaction_verbosity_tests()
diff --git a/test/functional/rpc_scanblocks.py b/test/functional/rpc_scanblocks.py
index 9a00518150..126e95362b 100755
--- a/test/functional/rpc_scanblocks.py
+++ b/test/functional/rpc_scanblocks.py
@@ -27,7 +27,6 @@ class ScanblocksTest(BitcoinTestFramework):
def run_test(self):
node = self.nodes[0]
wallet = MiniWallet(node)
- wallet.rescan_utxos()
# send 1.0, mempool only
_, spk_1, addr_1 = getnewdestination()
@@ -62,6 +61,12 @@ class ScanblocksTest(BitcoinTestFramework):
# make sure the blockhash is present when using the first mined block as start_height
assert blockhash in node.scanblocks(
"start", [f"addr({addr_1})"], height)['relevant_blocks']
+ for v in [False, True]:
+ assert blockhash in node.scanblocks(
+ action="start",
+ scanobjects=[f"addr({addr_1})"],
+ start_height=height,
+ options={"filter_false_positives": v})['relevant_blocks']
# also test the stop height
assert blockhash in node.scanblocks(
@@ -94,8 +99,11 @@ class ScanblocksTest(BitcoinTestFramework):
assert genesis_blockhash in node.scanblocks(
"start", [{"desc": f"raw({false_positive_spk.hex()})"}], 0, 0)['relevant_blocks']
- # TODO: after an "accurate" mode for scanblocks is implemented (e.g. PR #26325)
- # check here that it filters out the false-positive
+ # check that the filter_false_positives option works
+ assert genesis_blockhash in node.scanblocks(
+ "start", [{"desc": f"raw({genesis_coinbase_spk.hex()})"}], 0, 0, "basic", {"filter_false_positives": True})['relevant_blocks']
+ assert genesis_blockhash not in node.scanblocks(
+ "start", [{"desc": f"raw({false_positive_spk.hex()})"}], 0, 0, "basic", {"filter_false_positives": True})['relevant_blocks']
# test node with disabled blockfilterindex
assert_raises_rpc_error(-1, "Index is not enabled for filtertype basic",
diff --git a/test/functional/rpc_scantxoutset.py b/test/functional/rpc_scantxoutset.py
index af3e7a6d19..507a4f48e5 100755
--- a/test/functional/rpc_scantxoutset.py
+++ b/test/functional/rpc_scantxoutset.py
@@ -31,7 +31,6 @@ class ScantxoutsetTest(BitcoinTestFramework):
def run_test(self):
self.wallet = MiniWallet(self.nodes[0])
- self.wallet.rescan_utxos()
self.log.info("Test if we find coinbase outputs.")
assert_equal(sum(u["coinbase"] for u in self.nodes[0].scantxoutset("start", [self.wallet.get_descriptor()])["unspents"]), 49)
diff --git a/test/functional/rpc_txoutproof.py b/test/functional/rpc_txoutproof.py
index d04d05962f..60b7ce8d20 100755
--- a/test/functional/rpc_txoutproof.py
+++ b/test/functional/rpc_txoutproof.py
@@ -4,7 +4,6 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test gettxoutproof and verifytxoutproof RPCs."""
-from test_framework.blocktools import COINBASE_MATURITY
from test_framework.messages import (
CMerkleBlock,
from_hex,
@@ -20,7 +19,6 @@ from test_framework.wallet import MiniWallet
class MerkleBlockTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 2
- self.setup_clean_chain = True
self.extra_args = [
[],
["-txindex"],
@@ -28,12 +26,9 @@ class MerkleBlockTest(BitcoinTestFramework):
def run_test(self):
miniwallet = MiniWallet(self.nodes[0])
- # Add enough mature utxos to the wallet, so that all txs spend confirmed coins
- self.generate(miniwallet, 5)
- self.generate(self.nodes[0], COINBASE_MATURITY)
chain_height = self.nodes[1].getblockcount()
- assert_equal(chain_height, 105)
+ assert_equal(chain_height, 200)
txid1 = miniwallet.send_self_transfer(from_node=self.nodes[0])['txid']
txid2 = miniwallet.send_self_transfer(from_node=self.nodes[0])['txid']
diff --git a/test/functional/rpc_users.py b/test/functional/rpc_users.py
index 560f226469..8cc3ec401e 100755
--- a/test/functional/rpc_users.py
+++ b/test/functional/rpc_users.py
@@ -53,13 +53,13 @@ class HTTPBasicsTest(BitcoinTestFramework):
# Generate RPCAUTH with specified password
self.rt2password = "8/F3uMDw4KSEbw96U3CA1C4X05dkHDN2BPFjTgZW4KI="
- p = subprocess.Popen([sys.executable, gen_rpcauth, 'rt2', self.rt2password], stdout=subprocess.PIPE, universal_newlines=True)
+ p = subprocess.Popen([sys.executable, gen_rpcauth, 'rt2', self.rt2password], stdout=subprocess.PIPE, text=True)
lines = p.stdout.read().splitlines()
rpcauth2 = lines[1]
# Generate RPCAUTH without specifying password
self.user = ''.join(SystemRandom().choice(string.ascii_letters + string.digits) for _ in range(10))
- p = subprocess.Popen([sys.executable, gen_rpcauth, self.user], stdout=subprocess.PIPE, universal_newlines=True)
+ p = subprocess.Popen([sys.executable, gen_rpcauth, self.user], stdout=subprocess.PIPE, text=True)
lines = p.stdout.read().splitlines()
rpcauth3 = lines[1]
self.password = lines[3]
diff --git a/test/functional/test_framework/script.py b/test/functional/test_framework/script.py
index f345bf02db..443cae86a1 100644
--- a/test/functional/test_framework/script.py
+++ b/test/functional/test_framework/script.py
@@ -597,6 +597,13 @@ class CScript(bytes):
lastOpcode = opcode
return n
+ def IsWitnessProgram(self):
+ """A witness program is any valid CScript that consists of a 1-byte
+ push opcode followed by a data push between 2 and 40 bytes."""
+ return ((4 <= len(self) <= 42) and
+ (self[0] == OP_0 or (OP_1 <= self[0] <= OP_16)) and
+ (self[1] + 2 == len(self)))
+
SIGHASH_DEFAULT = 0 # Taproot-only default, semantics same as SIGHASH_ALL
SIGHASH_ALL = 1
diff --git a/test/functional/test_framework/test_framework.py b/test/functional/test_framework/test_framework.py
index f7dd4551c8..6ff4e4ee54 100755
--- a/test/functional/test_framework/test_framework.py
+++ b/test/functional/test_framework/test_framework.py
@@ -533,11 +533,7 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
self.nodes.append(test_node_i)
if not test_node_i.version_is_at_least(170000):
# adjust conf for pre 17
- conf_file = test_node_i.bitcoinconf
- with open(conf_file, 'r', encoding='utf8') as conf:
- conf_data = conf.read()
- with open(conf_file, 'w', encoding='utf8') as conf:
- conf.write(conf_data.replace('[regtest]', ''))
+ test_node_i.replace_in_config([('[regtest]', '')])
def start_node(self, i, *args, **kwargs):
"""Start a bitcoind"""
@@ -608,6 +604,10 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
self.wait_until(lambda: sum(peer['version'] != 0 for peer in to_connection.getpeerinfo()) == to_num_peers)
self.wait_until(lambda: sum(peer['bytesrecv_per_msg'].pop('verack', 0) == 24 for peer in from_connection.getpeerinfo()) == from_num_peers)
self.wait_until(lambda: sum(peer['bytesrecv_per_msg'].pop('verack', 0) == 24 for peer in to_connection.getpeerinfo()) == to_num_peers)
+ # The message bytes are counted before processing the message, so make
+ # sure it was fully processed by waiting for a ping.
+ self.wait_until(lambda: sum(peer["bytesrecv_per_msg"].pop("pong", 0) >= 32 for peer in from_connection.getpeerinfo()) == from_num_peers)
+ self.wait_until(lambda: sum(peer["bytesrecv_per_msg"].pop("pong", 0) >= 32 for peer in to_connection.getpeerinfo()) == to_num_peers)
def disconnect_nodes(self, a, b):
def disconnect_nodes_helper(node_a, node_b):
@@ -850,6 +850,13 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
except ImportError:
raise SkipTest("python3-zmq module not available.")
+ def skip_if_no_py_sqlite3(self):
+ """Attempt to import the sqlite3 package and skip the test if the import fails."""
+ try:
+ import sqlite3 # noqa
+ except ImportError:
+ raise SkipTest("sqlite3 module not available.")
+
def skip_if_no_python_bcc(self):
"""Attempt to import the bcc package and skip the tests if the import fails."""
try:
diff --git a/test/functional/test_framework/test_node.py b/test/functional/test_framework/test_node.py
index 8585972cb3..b515538a1a 100755
--- a/test/functional/test_framework/test_node.py
+++ b/test/functional/test_framework/test_node.py
@@ -387,6 +387,21 @@ class TestNode():
def wait_until_stopped(self, timeout=BITCOIND_PROC_WAIT_TIMEOUT):
wait_until_helper(self.is_node_stopped, timeout=timeout, timeout_factor=self.timeout_factor)
+ def replace_in_config(self, replacements):
+ """
+ Perform replacements in the configuration file.
+ The substitutions are passed as a list of search-replace-tuples, e.g.
+ [("old", "new"), ("foo", "bar"), ...]
+ """
+ with open(self.bitcoinconf, 'r', encoding='utf8') as conf:
+ conf_data = conf.read()
+ for replacement in replacements:
+ assert_equal(len(replacement), 2)
+ old, new = replacement[0], replacement[1]
+ conf_data = conf_data.replace(old, new)
+ with open(self.bitcoinconf, 'w', encoding='utf8') as conf:
+ conf.write(conf_data)
+
@property
def chain_path(self) -> Path:
return Path(self.datadir) / self.chain
@@ -730,7 +745,7 @@ class TestNodeCLI():
p_args += [command]
p_args += pos_args + named_args
self.log.debug("Running bitcoin-cli {}".format(p_args[2:]))
- process = subprocess.Popen(p_args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)
+ process = subprocess.Popen(p_args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
cli_stdout, cli_stderr = process.communicate(input=self.input)
returncode = process.poll()
if returncode:
diff --git a/test/functional/test_framework/util.py b/test/functional/test_framework/util.py
index 1d5108c31a..9048a915b2 100644
--- a/test/functional/test_framework/util.py
+++ b/test/functional/test_framework/util.py
@@ -488,28 +488,6 @@ def find_output(node, txid, amount, *, blockhash=None):
raise RuntimeError("find_output txid %s : %s not found" % (txid, str(amount)))
-def chain_transaction(node, parent_txids, vouts, value, fee, num_outputs):
- """Build and send a transaction that spends the given inputs (specified
- by lists of parent_txid:vout each), with the desired total value and fee,
- equally divided up to the desired number of outputs.
-
- Returns a tuple with the txid and the amount sent per output.
- """
- send_value = satoshi_round((value - fee)/num_outputs)
- inputs = []
- for (txid, vout) in zip(parent_txids, vouts):
- inputs.append({'txid' : txid, 'vout' : vout})
- outputs = {}
- for _ in range(num_outputs):
- outputs[node.getnewaddress()] = send_value
- rawtx = node.createrawtransaction(inputs, outputs, 0, True)
- signedtx = node.signrawtransactionwithwallet(rawtx)
- txid = node.sendrawtransaction(signedtx['hex'])
- fulltx = node.getrawtransaction(txid, 1)
- assert len(fulltx['vout']) == num_outputs # make sure we didn't generate a change output
- return (txid, send_value)
-
-
# Create large OP_RETURN txouts that can be appended to a transaction
# to make it large (helper for constructing large transactions). The
# total serialized size of the txouts is about 66k vbytes.
diff --git a/test/functional/test_framework/wallet.py b/test/functional/test_framework/wallet.py
index a72b5e5891..f3253630c4 100644
--- a/test/functional/test_framework/wallet.py
+++ b/test/functional/test_framework/wallet.py
@@ -55,6 +55,7 @@ from test_framework.util import (
assert_equal,
assert_greater_than_or_equal,
)
+from test_framework.blocktools import COINBASE_MATURITY
DEFAULT_FEE = Decimal("0.0001")
@@ -100,8 +101,15 @@ class MiniWallet:
self._address, self._internal_key = create_deterministic_address_bcrt1_p2tr_op_true()
self._scriptPubKey = bytes.fromhex(self._test_node.validateaddress(self._address)['scriptPubKey'])
- def _create_utxo(self, *, txid, vout, value, height):
- return {"txid": txid, "vout": vout, "value": value, "height": height}
+ # When the pre-mined test framework chain is used, it contains coinbase
+ # outputs to the MiniWallet's default address in blocks 76-100
+ # (see method BitcoinTestFramework._initialize_chain())
+ # The MiniWallet needs to rescan_utxos() in order to account
+ # for those mature UTXOs, so that all txs spend confirmed coins
+ self.rescan_utxos()
+
+ def _create_utxo(self, *, txid, vout, value, height, coinbase, confirmations):
+ return {"txid": txid, "vout": vout, "value": value, "height": height, "coinbase": coinbase, "confirmations": confirmations}
def _bulk_tx(self, tx, target_weight):
"""Pad a transaction with extra outputs until it reaches a target weight (or higher).
@@ -118,13 +126,25 @@ class MiniWallet:
def get_balance(self):
return sum(u['value'] for u in self._utxos)
- def rescan_utxos(self):
+ def rescan_utxos(self, *, include_mempool=True):
"""Drop all utxos and rescan the utxo set"""
self._utxos = []
res = self._test_node.scantxoutset(action="start", scanobjects=[self.get_descriptor()])
assert_equal(True, res['success'])
for utxo in res['unspents']:
- self._utxos.append(self._create_utxo(txid=utxo["txid"], vout=utxo["vout"], value=utxo["amount"], height=utxo["height"]))
+ self._utxos.append(
+ self._create_utxo(txid=utxo["txid"],
+ vout=utxo["vout"],
+ value=utxo["amount"],
+ height=utxo["height"],
+ coinbase=utxo["coinbase"],
+ confirmations=res["height"] - utxo["height"] + 1))
+ if include_mempool:
+ mempool = self._test_node.getrawmempool(verbose=True)
+ # Sort tx by ancestor count. See BlockAssembler::SortForBlock in src/node/miner.cpp
+ sorted_mempool = sorted(mempool.items(), key=lambda item: (item[1]["ancestorcount"], int(item[0], 16)))
+ for txid, _ in sorted_mempool:
+ self.scan_tx(self._test_node.getrawtransaction(txid=txid, verbose=True))
def scan_tx(self, tx):
"""Scan the tx and adjust the internal list of owned utxos"""
@@ -139,27 +159,35 @@ class MiniWallet:
pass
for out in tx['vout']:
if out['scriptPubKey']['hex'] == self._scriptPubKey.hex():
- self._utxos.append(self._create_utxo(txid=tx["txid"], vout=out["n"], value=out["value"], height=0))
+ self._utxos.append(self._create_utxo(txid=tx["txid"], vout=out["n"], value=out["value"], height=0, coinbase=False, confirmations=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)
- (sighash, err) = LegacySignatureHash(CScript(self._scriptPubKey), tx, 0, SIGHASH_ALL)
- assert err is None
- # for exact fee calculation, create only signatures with fixed size by default (>49.89% probability):
- # 65 bytes: high-R val (33 bytes) + low-S val (32 bytes)
- # with the DER header/skeleton data of 6 bytes added, this leads to a target size of 71 bytes
- der_sig = b''
- while not len(der_sig) == 71:
- der_sig = self._priv_key.sign_ecdsa(sighash)
- if not fixed_length:
- break
- tx.vin[0].scriptSig = CScript([der_sig + bytes(bytearray([SIGHASH_ALL]))])
- tx.rehash()
+ if self._mode == MiniWalletMode.RAW_P2PK:
+ (sighash, err) = LegacySignatureHash(CScript(self._scriptPubKey), tx, 0, SIGHASH_ALL)
+ assert err is None
+ # for exact fee calculation, create only signatures with fixed size by default (>49.89% probability):
+ # 65 bytes: high-R val (33 bytes) + low-S val (32 bytes)
+ # with the DER header/skeleton data of 6 bytes added, this leads to a target size of 71 bytes
+ der_sig = b''
+ while not len(der_sig) == 71:
+ der_sig = self._priv_key.sign_ecdsa(sighash)
+ if not fixed_length:
+ break
+ tx.vin[0].scriptSig = CScript([der_sig + bytes(bytearray([SIGHASH_ALL]))])
+ tx.rehash()
+ elif self._mode == MiniWalletMode.RAW_OP_TRUE:
+ for i in tx.vin:
+ i.scriptSig = CScript([OP_NOP] * 43) # pad to identical size
+ elif self._mode == MiniWalletMode.ADDRESS_OP_TRUE:
+ tx.wit.vtxinwit = [CTxInWitness()] * len(tx.vin)
+ for i in tx.wit.vtxinwit:
+ i.scriptWitness.stack = [CScript([OP_TRUE]), bytes([LEAF_VERSION_TAPSCRIPT]) + self._internal_key]
+ else:
+ assert False
def generate(self, num_blocks, **kwargs):
"""Generate blocks with coinbase outputs to the internal address, and call rescan_utxos"""
@@ -204,9 +232,13 @@ class MiniWallet:
else:
return self._utxos[index]
- def get_utxos(self, *, mark_as_spent=True):
+ def get_utxos(self, *, include_immature_coinbase=False, mark_as_spent=True):
"""Returns the list of all utxos and optionally mark them as spent"""
- utxos = deepcopy(self._utxos)
+ if not include_immature_coinbase:
+ utxo_filter = filter(lambda utxo: not utxo['coinbase'] or COINBASE_MATURITY <= utxo['confirmations'], self._utxos)
+ else:
+ utxo_filter = self._utxos
+ utxos = deepcopy(list(utxo_filter))
if mark_as_spent:
self._utxos = []
return utxos
@@ -266,24 +298,17 @@ class MiniWallet:
inputs_value_total = sum([int(COIN * utxo['value']) for utxo in utxos_to_spend])
outputs_value_total = inputs_value_total - fee_per_output * num_outputs
amount_per_output = amount_per_output or (outputs_value_total // num_outputs)
+ assert amount_per_output > 0
+ outputs_value_total = amount_per_output * num_outputs
+ fee = Decimal(inputs_value_total - outputs_value_total) / COIN
# create tx
tx = CTransaction()
- tx.vin = [CTxIn(COutPoint(int(utxo_to_spend['txid'], 16), utxo_to_spend['vout']), nSequence=seq) for utxo_to_spend,seq in zip(utxos_to_spend, sequence)]
+ tx.vin = [CTxIn(COutPoint(int(utxo_to_spend['txid'], 16), utxo_to_spend['vout']), nSequence=seq) for utxo_to_spend, seq in zip(utxos_to_spend, sequence)]
tx.vout = [CTxOut(amount_per_output, bytearray(self._scriptPubKey)) for _ in range(num_outputs)]
tx.nLockTime = locktime
- if self._mode == MiniWalletMode.RAW_P2PK:
- self.sign_tx(tx)
- elif self._mode == MiniWalletMode.RAW_OP_TRUE:
- for i in range(len(utxos_to_spend)):
- tx.vin[i].scriptSig = CScript([OP_NOP] * 43) # pad to identical size
- elif self._mode == MiniWalletMode.ADDRESS_OP_TRUE:
- tx.wit.vtxinwit = [CTxInWitness()] * len(utxos_to_spend)
- for i in range(len(utxos_to_spend)):
- tx.wit.vtxinwit[i].scriptWitness.stack = [CScript([OP_TRUE]), bytes([LEAF_VERSION_TAPSCRIPT]) + self._internal_key]
- else:
- assert False
+ self.sign_tx(tx)
if target_weight:
self._bulk_tx(tx, target_weight)
@@ -295,8 +320,12 @@ class MiniWallet:
vout=i,
value=Decimal(tx.vout[i].nValue) / COIN,
height=0,
+ coinbase=False,
+ confirmations=0,
) for i in range(len(tx.vout))],
+ "fee": fee,
"txid": txid,
+ "wtxid": tx.getwtxid(),
"hex": tx.serialize().hex(),
"tx": tx,
}
@@ -314,52 +343,45 @@ class MiniWallet:
else:
assert False
send_value = utxo_to_spend["value"] - (fee or (fee_rate * vsize / 1000))
- assert send_value > 0
# create tx
tx = self.create_self_transfer_multi(utxos_to_spend=[utxo_to_spend], locktime=locktime, sequence=sequence, amount_per_output=int(COIN * send_value), target_weight=target_weight)
if not target_weight:
assert_equal(tx["tx"].get_vsize(), vsize)
+ tx["new_utxo"] = tx.pop("new_utxos")[0]
- return {"txid": tx["txid"], "wtxid": tx["tx"].getwtxid(), "hex": tx["hex"], "tx": tx["tx"], "new_utxo": tx["new_utxos"][0]}
+ return tx
def sendrawtransaction(self, *, from_node, tx_hex, maxfeerate=0, **kwargs):
txid = from_node.sendrawtransaction(hexstring=tx_hex, maxfeerate=maxfeerate, **kwargs)
self.scan_tx(from_node.decoderawtransaction(tx_hex))
return txid
- def create_self_transfer_chain(self, *, chain_length):
+ def create_self_transfer_chain(self, *, chain_length, utxo_to_spend=None):
"""
Create a "chain" of chain_length transactions. The nth transaction in
the chain is a child of the n-1th transaction and parent of the n+1th transaction.
-
- Returns a dic {"chain_hex": chain_hex, "chain_txns" : chain_txns}
-
- "chain_hex" is a list representing the chain's transactions in hexadecimal.
- "chain_txns" is a list representing the chain's transactions in the CTransaction object.
"""
- chaintip_utxo = self.get_utxo()
- chain_hex = []
- chain_txns = []
+ chaintip_utxo = utxo_to_spend or self.get_utxo()
+ chain = []
for _ in range(chain_length):
tx = self.create_self_transfer(utxo_to_spend=chaintip_utxo)
chaintip_utxo = tx["new_utxo"]
- chain_hex.append(tx["hex"])
- chain_txns.append(tx["tx"])
+ chain.append(tx)
- return {"chain_hex": chain_hex, "chain_txns" : chain_txns}
+ return chain
- def send_self_transfer_chain(self, *, from_node, chain_length, utxo_to_spend=None):
+ def send_self_transfer_chain(self, *, from_node, **kwargs):
"""Create and send a "chain" of chain_length transactions. The nth transaction in
the chain is a child of the n-1th transaction and parent of the n+1th transaction.
- Returns the chaintip (nth) utxo
+ Returns a list of objects for each tx (see create_self_transfer_multi).
"""
- chaintip_utxo = utxo_to_spend or self.get_utxo()
- for _ in range(chain_length):
- chaintip_utxo = self.send_self_transfer(utxo_to_spend=chaintip_utxo, from_node=from_node)["new_utxo"]
- return chaintip_utxo
+ chain = self.create_self_transfer_chain(**kwargs)
+ for t in chain:
+ self.sendrawtransaction(from_node=from_node, tx_hex=t["hex"])
+ return chain
def getnewdestination(address_type='bech32m'):
diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py
index 86a9e4cd9a..a301cf9184 100755
--- a/test/functional/test_runner.py
+++ b/test/functional/test_runner.py
@@ -254,6 +254,7 @@ BASE_SCRIPTS = [
'wallet_importprunedfunds.py --descriptors',
'p2p_leak_tx.py',
'p2p_eviction.py',
+ 'p2p_ibd_stalling.py',
'wallet_signmessagewithaddress.py',
'rpc_signmessagewithprivkey.py',
'rpc_generate.py',
@@ -318,6 +319,7 @@ BASE_SCRIPTS = [
'mempool_unbroadcast.py',
'mempool_compatibility.py',
'mempool_accept_wtxid.py',
+ 'mempool_dust.py',
'rpc_deriveaddresses.py',
'rpc_deriveaddresses.py --usecli',
'p2p_ping.py',
@@ -584,7 +586,7 @@ def run_tests(*, test_list, src_dir, build_dir, tmpdir, jobs=1, enable_coverage=
combined_logs_args = [sys.executable, os.path.join(tests_dir, 'combine_logs.py'), testdir]
if BOLD[0]:
combined_logs_args += ['--color']
- combined_logs, _ = subprocess.Popen(combined_logs_args, universal_newlines=True, stdout=subprocess.PIPE).communicate()
+ combined_logs, _ = subprocess.Popen(combined_logs_args, text=True, stdout=subprocess.PIPE).communicate()
print("\n".join(deque(combined_logs.splitlines(), combined_logs_len)))
if failfast:
@@ -669,7 +671,7 @@ class TestHandler:
self.jobs.append((test,
time.time(),
subprocess.Popen([sys.executable, self.tests_dir + test_argv[0]] + test_argv[1:] + self.flags + portseed_arg + tmpdir_arg,
- universal_newlines=True,
+ text=True,
stdout=log_stdout,
stderr=log_stderr),
testdir,
diff --git a/test/functional/tool_wallet.py b/test/functional/tool_wallet.py
index 4a321c2fc4..a888f93b03 100755
--- a/test/functional/tool_wallet.py
+++ b/test/functional/tool_wallet.py
@@ -37,7 +37,7 @@ class ToolWalletTest(BitcoinTestFramework):
if not self.options.descriptors and 'create' in args:
default_args.append('-legacy')
- return subprocess.Popen([binary] + default_args + list(args), stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)
+ return subprocess.Popen([binary] + default_args + list(args), stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
def assert_raises_tool_error(self, error, *args):
p = self.bitcoin_wallet_process(*args)
diff --git a/test/functional/wallet_basic.py b/test/functional/wallet_basic.py
index 86cfd4a230..52022a2eee 100755
--- a/test/functional/wallet_basic.py
+++ b/test/functional/wallet_basic.py
@@ -731,6 +731,45 @@ class WalletTest(BitcoinTestFramework):
assert_equal(coin_b["parent_descs"][0], multi_b)
self.nodes[0].unloadwallet("wo")
+ self.log.info("Test -spendzeroconfchange")
+ self.restart_node(0, ["-spendzeroconfchange=0"])
+
+ # create new wallet and fund it with a confirmed UTXO
+ self.nodes[0].createwallet(wallet_name="zeroconf", load_on_startup=True)
+ zeroconf_wallet = self.nodes[0].get_wallet_rpc("zeroconf")
+ default_wallet = self.nodes[0].get_wallet_rpc(self.default_wallet_name)
+ default_wallet.sendtoaddress(zeroconf_wallet.getnewaddress(), Decimal('1.0'))
+ self.generate(self.nodes[0], 1, sync_fun=self.no_op)
+ utxos = zeroconf_wallet.listunspent(minconf=0)
+ assert_equal(len(utxos), 1)
+ assert_equal(utxos[0]['confirmations'], 1)
+
+ # spend confirmed UTXO to ourselves
+ zeroconf_wallet.sendall(recipients=[zeroconf_wallet.getnewaddress()])
+ utxos = zeroconf_wallet.listunspent(minconf=0)
+ assert_equal(len(utxos), 1)
+ assert_equal(utxos[0]['confirmations'], 0)
+ # accounts for untrusted pending balance
+ bal = zeroconf_wallet.getbalances()
+ assert_equal(bal['mine']['trusted'], 0)
+ assert_equal(bal['mine']['untrusted_pending'], utxos[0]['amount'])
+
+ # spending an unconfirmed UTXO sent to ourselves should fail
+ assert_raises_rpc_error(-6, "Insufficient funds", zeroconf_wallet.sendtoaddress, zeroconf_wallet.getnewaddress(), Decimal('0.5'))
+
+ # check that it works again with -spendzeroconfchange set (=default)
+ self.restart_node(0, ["-spendzeroconfchange=1"])
+ zeroconf_wallet = self.nodes[0].get_wallet_rpc("zeroconf")
+ utxos = zeroconf_wallet.listunspent(minconf=0)
+ assert_equal(len(utxos), 1)
+ assert_equal(utxos[0]['confirmations'], 0)
+ # accounts for trusted balance
+ bal = zeroconf_wallet.getbalances()
+ assert_equal(bal['mine']['trusted'], utxos[0]['amount'])
+ assert_equal(bal['mine']['untrusted_pending'], 0)
+
+ zeroconf_wallet.sendtoaddress(zeroconf_wallet.getnewaddress(), Decimal('0.5'))
+
if __name__ == '__main__':
WalletTest().main()
diff --git a/test/functional/wallet_bumpfee.py b/test/functional/wallet_bumpfee.py
index 1cb3f434e8..a2ae997ecb 100755
--- a/test/functional/wallet_bumpfee.py
+++ b/test/functional/wallet_bumpfee.py
@@ -88,6 +88,7 @@ class BumpFeeTest(BitcoinTestFramework):
test_nonrbf_bumpfee_fails(self, peer_node, dest_address)
test_notmine_bumpfee(self, rbf_node, peer_node, dest_address)
test_bumpfee_with_descendant_fails(self, rbf_node, rbf_node_address, dest_address)
+ test_bumpfee_with_abandoned_descendant_succeeds(self, rbf_node, rbf_node_address, dest_address)
test_dust_to_fee(self, rbf_node, dest_address)
test_watchonly_psbt(self, peer_node, rbf_node, dest_address)
test_rebumping(self, rbf_node, dest_address)
@@ -286,6 +287,35 @@ def test_bumpfee_with_descendant_fails(self, rbf_node, rbf_node_address, dest_ad
self.clear_mempool()
+def test_bumpfee_with_abandoned_descendant_succeeds(self, rbf_node, rbf_node_address, dest_address):
+ self.log.info('Test that fee can be bumped when it has abandoned 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)
+ # Submit child transaction with low fee
+ child_id = rbf_node.send(outputs={dest_address: 0.00020000},
+ options={"inputs": [{"txid": parent_id, "vout": 0}], "fee_rate": 2})["txid"]
+ assert child_id in rbf_node.getrawmempool()
+
+ # Restart the node with higher min relay fee so the descendant tx is no longer in mempool so that we can abandon it
+ self.restart_node(1, ['-minrelaytxfee=0.00005'] + self.extra_args[1])
+ rbf_node.walletpassphrase(WALLET_PASSPHRASE, WALLET_PASSPHRASE_TIMEOUT)
+ self.connect_nodes(1, 0)
+ assert parent_id in rbf_node.getrawmempool()
+ assert child_id not in rbf_node.getrawmempool()
+ # Should still raise an error even if not in mempool
+ assert_raises_rpc_error(-8, "Transaction has descendants in the wallet", rbf_node.bumpfee, parent_id)
+ # Now abandon the child transaction and bump the original
+ rbf_node.abandontransaction(child_id)
+ bumped_result = rbf_node.bumpfee(parent_id, {"fee_rate": HIGH})
+ assert bumped_result['txid'] in rbf_node.getrawmempool()
+ assert parent_id not in rbf_node.getrawmempool()
+ # Cleanup
+ self.restart_node(1, self.extra_args[1])
+ rbf_node.walletpassphrase(WALLET_PASSPHRASE, WALLET_PASSPHRASE_TIMEOUT)
+ self.connect_nodes(1, 0)
+ self.clear_mempool()
+
+
def test_small_output_with_feerate_succeeds(self, rbf_node, dest_address):
self.log.info('Testing small output with feerate bump succeeds')
diff --git a/test/functional/wallet_crosschain.py b/test/functional/wallet_crosschain.py
index 6f93ad4e3b..7a1297e65f 100755
--- a/test/functional/wallet_crosschain.py
+++ b/test/functional/wallet_crosschain.py
@@ -25,11 +25,7 @@ class WalletCrossChain(BitcoinTestFramework):
# Switch node 1 to testnet before starting it.
self.nodes[1].chain = 'testnet3'
self.nodes[1].extra_args = ['-maxconnections=0', '-prune=550'] # disable testnet sync
- with open(self.nodes[1].bitcoinconf, 'r', encoding='utf8') as conf:
- conf_data = conf.read()
- with open (self.nodes[1].bitcoinconf, 'w', encoding='utf8') as conf:
- conf.write(conf_data.replace('regtest=', 'testnet=').replace('[regtest]', '[test]'))
-
+ self.nodes[1].replace_in_config([('regtest=', 'testnet='), ('[regtest]', '[test]')])
self.start_nodes()
def run_test(self):
diff --git a/test/functional/wallet_descriptor.py b/test/functional/wallet_descriptor.py
index 2b70e5ecc9..b0e93df36a 100755
--- a/test/functional/wallet_descriptor.py
+++ b/test/functional/wallet_descriptor.py
@@ -4,7 +4,11 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test descriptor wallet function."""
import os
-import sqlite3
+
+try:
+ import sqlite3
+except ImportError:
+ pass
from test_framework.blocktools import COINBASE_MATURITY
from test_framework.test_framework import BitcoinTestFramework
@@ -27,6 +31,7 @@ class WalletDescriptorTest(BitcoinTestFramework):
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
self.skip_if_no_sqlite()
+ self.skip_if_no_py_sqlite3()
def run_test(self):
if self.is_bdb_compiled():
@@ -109,7 +114,7 @@ class WalletDescriptorTest(BitcoinTestFramework):
# Make sure things are disabled
self.log.info("Test disabled RPCs")
assert_raises_rpc_error(-4, "Only legacy wallets are supported by this command", recv_wrpc.rpc.importprivkey, "cVpF924EspNh8KjYsfhgY96mmxvT6DgdWiTYMtMjuM74hJaU5psW")
- assert_raises_rpc_error(-4, "Only legacy wallets are supported by this command", recv_wrpc.rpc.importpubkey, send_wrpc.getaddressinfo(send_wrpc.getnewaddress()))
+ assert_raises_rpc_error(-4, "Only legacy wallets are supported by this command", recv_wrpc.rpc.importpubkey, send_wrpc.getaddressinfo(send_wrpc.getnewaddress())["pubkey"])
assert_raises_rpc_error(-4, "Only legacy wallets are supported by this command", recv_wrpc.rpc.importaddress, recv_wrpc.getnewaddress())
assert_raises_rpc_error(-4, "Only legacy wallets are supported by this command", recv_wrpc.rpc.importmulti, [])
assert_raises_rpc_error(-4, "Only legacy wallets are supported by this command", recv_wrpc.rpc.addmultisigaddress, 1, [recv_wrpc.getnewaddress()])
diff --git a/test/functional/wallet_fast_rescan.py b/test/functional/wallet_fast_rescan.py
index 52e33acb24..1ab24f1a96 100755
--- a/test/functional/wallet_fast_rescan.py
+++ b/test/functional/wallet_fast_rescan.py
@@ -40,7 +40,6 @@ class WalletFastRescanTest(BitcoinTestFramework):
def run_test(self):
node = self.nodes[0]
wallet = MiniWallet(node)
- wallet.rescan_utxos()
self.log.info("Create descriptor wallet with backup")
WALLET_BACKUP_FILENAME = os.path.join(node.datadir, 'wallet.bak')
diff --git a/test/functional/wallet_fundrawtransaction.py b/test/functional/wallet_fundrawtransaction.py
index 98b0f70b01..29ddb77b41 100755
--- a/test/functional/wallet_fundrawtransaction.py
+++ b/test/functional/wallet_fundrawtransaction.py
@@ -148,6 +148,7 @@ class RawTransactionsTest(BitcoinTestFramework):
self.test_external_inputs()
self.test_22670()
self.test_feerate_rounding()
+ self.test_input_confs_control()
def test_change_position(self):
"""Ensure setting changePosition in fundraw with an exact match is handled properly."""
@@ -1403,6 +1404,66 @@ class RawTransactionsTest(BitcoinTestFramework):
rawtx = w.createrawtransaction(inputs=[], outputs=[{self.nodes[0].getnewaddress(address_type="bech32"): 1 - 0.00000202}])
assert_raises_rpc_error(-4, "Insufficient funds", w.fundrawtransaction, rawtx, {"fee_rate": 1.85})
+ def test_input_confs_control(self):
+ self.nodes[0].createwallet("minconf")
+ wallet = self.nodes[0].get_wallet_rpc("minconf")
+
+ # Fund the wallet with different chain heights
+ for _ in range(2):
+ self.nodes[2].sendmany("", {wallet.getnewaddress():1, wallet.getnewaddress():1})
+ self.generate(self.nodes[2], 1)
+
+ unconfirmed_txid = wallet.sendtoaddress(wallet.getnewaddress(), 0.5)
+
+ self.log.info("Crafting TX using an unconfirmed input")
+ target_address = self.nodes[2].getnewaddress()
+ raw_tx1 = wallet.createrawtransaction([], {target_address: 0.1}, 0, True)
+ funded_tx1 = wallet.fundrawtransaction(raw_tx1, {'fee_rate': 1, 'maxconf': 0})['hex']
+
+ # Make sure we only had the one input
+ tx1_inputs = self.nodes[0].decoderawtransaction(funded_tx1)['vin']
+ assert_equal(len(tx1_inputs), 1)
+
+ utxo1 = tx1_inputs[0]
+ assert unconfirmed_txid == utxo1['txid']
+
+ final_tx1 = wallet.signrawtransactionwithwallet(funded_tx1)['hex']
+ txid1 = self.nodes[0].sendrawtransaction(final_tx1)
+
+ mempool = self.nodes[0].getrawmempool()
+ assert txid1 in mempool
+
+ self.log.info("Fail to craft a new TX with minconf above highest one")
+ # Create a replacement tx to 'final_tx1' that has 1 BTC target instead of 0.1.
+ raw_tx2 = wallet.createrawtransaction([{'txid': utxo1['txid'], 'vout': utxo1['vout']}], {target_address: 1})
+ assert_raises_rpc_error(-4, "Insufficient funds", wallet.fundrawtransaction, raw_tx2, {'add_inputs': True, 'minconf': 3, 'fee_rate': 10})
+
+ self.log.info("Fail to broadcast a new TX with maxconf 0 due to BIP125 rules to verify it actually chose unconfirmed outputs")
+ # Now fund 'raw_tx2' to fulfill the total target (1 BTC) by using all the wallet unconfirmed outputs.
+ # As it was created with the first unconfirmed output, 'raw_tx2' only has 0.1 BTC covered (need to fund 0.9 BTC more).
+ # So, the selection process, to cover the amount, will pick up the 'final_tx1' output as well, which is an output of the tx that this
+ # new tx is replacing!. So, once we send it to the mempool, it will return a "bad-txns-spends-conflicting-tx"
+ # because the input will no longer exist once the first tx gets replaced by this new one).
+ funded_invalid = wallet.fundrawtransaction(raw_tx2, {'add_inputs': True, 'maxconf': 0, 'fee_rate': 10})['hex']
+ final_invalid = wallet.signrawtransactionwithwallet(funded_invalid)['hex']
+ assert_raises_rpc_error(-26, "bad-txns-spends-conflicting-tx", self.nodes[0].sendrawtransaction, final_invalid)
+
+ self.log.info("Craft a replacement adding inputs with highest depth possible")
+ funded_tx2 = wallet.fundrawtransaction(raw_tx2, {'add_inputs': True, 'minconf': 2, 'fee_rate': 10})['hex']
+ tx2_inputs = self.nodes[0].decoderawtransaction(funded_tx2)['vin']
+ assert_greater_than_or_equal(len(tx2_inputs), 2)
+ for vin in tx2_inputs:
+ if vin['txid'] != unconfirmed_txid:
+ assert_greater_than_or_equal(self.nodes[0].gettxout(vin['txid'], vin['vout'])['confirmations'], 2)
+
+ final_tx2 = wallet.signrawtransactionwithwallet(funded_tx2)['hex']
+ txid2 = self.nodes[0].sendrawtransaction(final_tx2)
+
+ mempool = self.nodes[0].getrawmempool()
+ assert txid1 not in mempool
+ assert txid2 in mempool
+
+ wallet.unloadwallet()
if __name__ == '__main__':
RawTransactionsTest().main()
diff --git a/test/functional/wallet_importdescriptors.py b/test/functional/wallet_importdescriptors.py
index 9e813166c5..ca0209b61d 100755
--- a/test/functional/wallet_importdescriptors.py
+++ b/test/functional/wallet_importdescriptors.py
@@ -15,7 +15,6 @@ variants.
- `test_address()` is called to call getaddressinfo for an address on node1
and test the values returned."""
-from test_framework.address import key_to_p2pkh
from test_framework.blocktools import COINBASE_MATURITY
from test_framework.test_framework import BitcoinTestFramework
from test_framework.descriptors import descsum_create
@@ -120,12 +119,11 @@ class ImportDescriptorsTest(BitcoinTestFramework):
self.log.info("Internal addresses should be detected as such")
key = get_generate_key()
- addr = key_to_p2pkh(key.pubkey)
self.test_importdesc({"desc": descsum_create("pkh(" + key.pubkey + ")"),
"timestamp": "now",
"internal": True},
success=True)
- info = w1.getaddressinfo(addr)
+ info = w1.getaddressinfo(key.p2pkh_addr)
assert_equal(info["ismine"], True)
assert_equal(info["ischange"], True)
diff --git a/test/functional/wallet_migration.py b/test/functional/wallet_migration.py
index 688ac98617..72c5fe7b84 100755
--- a/test/functional/wallet_migration.py
+++ b/test/functional/wallet_migration.py
@@ -163,6 +163,10 @@ class WalletMigrationTest(BitcoinTestFramework):
assert_equal(basic2.getbalance(), basic2_balance)
self.assert_list_txs_equal(basic2.listtransactions(), basic2_txs)
+ # Now test migration on a descriptor wallet
+ self.log.info("Test \"nothing to migrate\" when the user tries to migrate a wallet with no legacy data")
+ assert_raises_rpc_error(-4, "Error: This wallet is already a descriptor wallet", basic2.migratewallet)
+
def test_multisig(self):
default = self.nodes[0].get_wallet_rpc(self.default_wallet_name)
diff --git a/test/functional/wallet_orphanedreward.py b/test/functional/wallet_orphanedreward.py
index d9f7c14ded..d8931fa620 100755
--- a/test/functional/wallet_orphanedreward.py
+++ b/test/functional/wallet_orphanedreward.py
@@ -34,29 +34,40 @@ class OrphanedBlockRewardTest(BitcoinTestFramework):
# the existing balance and the block reward.
self.generate(self.nodes[0], 150)
assert_equal(self.nodes[1].getbalance(), 10 + 25)
+ pre_reorg_conf_bals = self.nodes[1].getbalances()
txid = self.nodes[1].sendtoaddress(self.nodes[0].getnewaddress(), 30)
+ orig_chain_tip = self.nodes[0].getbestblockhash()
+ self.sync_mempools()
# Orphan the block reward and make sure that the original coins
# from the wallet can still be spent.
self.nodes[0].invalidateblock(blk)
- self.generate(self.nodes[0], 152)
- # Without the following abandontransaction call, the coins are
- # not considered available yet.
- assert_equal(self.nodes[1].getbalances()["mine"], {
- "trusted": 0,
- "untrusted_pending": 0,
- "immature": 0,
- })
- # The following abandontransaction is necessary to make the later
- # lines succeed, and probably should not be needed; see
- # https://github.com/bitcoin/bitcoin/issues/14148.
- self.nodes[1].abandontransaction(txid)
+ blocks = self.generate(self.nodes[0], 152)
+ conflict_block = blocks[0]
+ # We expect the descendants of orphaned rewards to no longer be considered
assert_equal(self.nodes[1].getbalances()["mine"], {
"trusted": 10,
"untrusted_pending": 0,
"immature": 0,
})
- self.nodes[1].sendtoaddress(self.nodes[0].getnewaddress(), 9)
+ # And the unconfirmed tx to be abandoned
+ assert_equal(self.nodes[1].gettransaction(txid)["details"][0]["abandoned"], True)
+
+ # The abandoning should persist through reloading
+ self.nodes[1].unloadwallet(self.default_wallet_name)
+ self.nodes[1].loadwallet(self.default_wallet_name)
+ assert_equal(self.nodes[1].gettransaction(txid)["details"][0]["abandoned"], True)
+
+ # If the orphaned reward is reorged back into the main chain, any unconfirmed
+ # descendant txs at the time of the original reorg remain abandoned.
+ self.nodes[0].invalidateblock(conflict_block)
+ self.nodes[0].reconsiderblock(blk)
+ assert_equal(self.nodes[0].getbestblockhash(), orig_chain_tip)
+ self.generate(self.nodes[0], 3)
+
+ assert_equal(self.nodes[1].getbalances(), pre_reorg_conf_bals)
+ assert_equal(self.nodes[1].gettransaction(txid)["details"][0]["abandoned"], True)
+
if __name__ == '__main__':
OrphanedBlockRewardTest().main()
diff --git a/test/functional/wallet_send.py b/test/functional/wallet_send.py
index 424834323f..ac3ec06eec 100755
--- a/test/functional/wallet_send.py
+++ b/test/functional/wallet_send.py
@@ -45,7 +45,7 @@ class WalletSendTest(BitcoinTestFramework):
conf_target=None, estimate_mode=None, fee_rate=None, add_to_wallet=None, psbt=None,
inputs=None, add_inputs=None, include_unsafe=None, change_address=None, change_position=None, change_type=None,
include_watching=None, locktime=None, lock_unspents=None, replaceable=None, subtract_fee_from_outputs=None,
- expect_error=None, solving_data=None):
+ expect_error=None, solving_data=None, minconf=None):
assert (amount is None) != (data is None)
from_balance_before = from_wallet.getbalances()["mine"]["trusted"]
@@ -106,6 +106,8 @@ class WalletSendTest(BitcoinTestFramework):
options["subtract_fee_from_outputs"] = subtract_fee_from_outputs
if solving_data is not None:
options["solving_data"] = solving_data
+ if minconf is not None:
+ options["minconf"] = minconf
if len(options.keys()) == 0:
options = None
@@ -487,6 +489,16 @@ class WalletSendTest(BitcoinTestFramework):
res = self.test_send(from_wallet=w5, to_wallet=w0, amount=1, include_unsafe=True)
assert res["complete"]
+ self.log.info("Minconf")
+ self.nodes[1].createwallet(wallet_name="minconfw")
+ minconfw= self.nodes[1].get_wallet_rpc("minconfw")
+ self.test_send(from_wallet=w0, to_wallet=minconfw, amount=2)
+ self.generate(self.nodes[0], 3)
+ self.test_send(from_wallet=minconfw, to_wallet=w0, amount=1, minconf=4, expect_error=(-4, "Insufficient funds"))
+ self.test_send(from_wallet=minconfw, to_wallet=w0, amount=1, minconf=-4, expect_error=(-8, "Negative minconf"))
+ res = self.test_send(from_wallet=minconfw, to_wallet=w0, amount=1, minconf=3)
+ assert res["complete"]
+
self.log.info("External outputs")
eckey = ECKey()
eckey.generate()
diff --git a/test/functional/wallet_sendall.py b/test/functional/wallet_sendall.py
index 778c8a5b9e..f6440f07d7 100755
--- a/test/functional/wallet_sendall.py
+++ b/test/functional/wallet_sendall.py
@@ -317,6 +317,68 @@ class SendallTest(BitcoinTestFramework):
assert_equal(decoded["tx"]["vin"][0]["vout"], utxo["vout"])
assert_equal(decoded["tx"]["vout"][0]["scriptPubKey"]["address"], self.remainder_target)
+ @cleanup
+ def sendall_with_minconf(self):
+ # utxo of 17 bicoin has 6 confirmations, utxo of 4 has 3
+ self.add_utxos([17])
+ self.generate(self.nodes[0], 2)
+ self.add_utxos([4])
+ self.generate(self.nodes[0], 2)
+
+ self.log.info("Test sendall fails because minconf is negative")
+
+ assert_raises_rpc_error(-8,
+ "Invalid minconf (minconf cannot be negative): -2",
+ self.wallet.sendall,
+ recipients=[self.remainder_target],
+ options={"minconf": -2})
+ self.log.info("Test sendall fails because minconf is used while specific inputs are provided")
+
+ utxo = self.wallet.listunspent()[0]
+ assert_raises_rpc_error(-8,
+ "Cannot combine minconf or maxconf with specific inputs.",
+ self.wallet.sendall,
+ recipients=[self.remainder_target],
+ options={"inputs": [utxo], "minconf": 2})
+
+ self.log.info("Test sendall fails because there are no utxos with enough confirmations specified by minconf")
+
+ assert_raises_rpc_error(-6,
+ "Total value of UTXO pool too low to pay for transaction. Try using lower feerate or excluding uneconomic UTXOs with 'send_max' option.",
+ self.wallet.sendall,
+ recipients=[self.remainder_target],
+ options={"minconf": 7})
+
+ self.log.info("Test sendall only spends utxos with a specified number of confirmations when minconf is used")
+ self.wallet.sendall(recipients=[self.remainder_target], fee_rate=300, options={"minconf": 6})
+
+ assert_equal(len(self.wallet.listunspent()), 1)
+ assert_equal(self.wallet.listunspent()[0]['confirmations'], 3)
+
+ # decrease minconf and show the remaining utxo is picked up
+ self.wallet.sendall(recipients=[self.remainder_target], fee_rate=300, options={"minconf": 3})
+ assert_equal(self.wallet.getbalance(), 0)
+
+ @cleanup
+ def sendall_with_maxconf(self):
+ # utxo of 17 bicoin has 6 confirmations, utxo of 4 has 3
+ self.add_utxos([17])
+ self.generate(self.nodes[0], 2)
+ self.add_utxos([4])
+ self.generate(self.nodes[0], 2)
+
+ self.log.info("Test sendall fails because there are no utxos with enough confirmations specified by maxconf")
+ assert_raises_rpc_error(-6,
+ "Total value of UTXO pool too low to pay for transaction. Try using lower feerate or excluding uneconomic UTXOs with 'send_max' option.",
+ self.wallet.sendall,
+ recipients=[self.remainder_target],
+ options={"maxconf": 1})
+
+ self.log.info("Test sendall only spends utxos with a specified number of confirmations when maxconf is used")
+ self.wallet.sendall(recipients=[self.remainder_target], fee_rate=300, options={"maxconf":4})
+ assert_equal(len(self.wallet.listunspent()), 1)
+ assert_equal(self.wallet.listunspent()[0]['confirmations'], 6)
+
# This tests needs to be the last one otherwise @cleanup will fail with "Transaction too large" error
def sendall_fails_with_transaction_too_large(self):
self.log.info("Test that sendall fails if resulting transaction is too large")
@@ -392,6 +454,12 @@ class SendallTest(BitcoinTestFramework):
# Sendall succeeds with watchonly wallets spending specific UTXOs
self.sendall_watchonly_specific_inputs()
+ # Sendall only uses outputs with at least a give number of confirmations when using minconf
+ self.sendall_with_minconf()
+
+ # Sendall only uses outputs with less than a given number of confirmation when using minconf
+ self.sendall_with_maxconf()
+
# Sendall fails when many inputs result to too large transaction
self.sendall_fails_with_transaction_too_large()
diff --git a/test/fuzz/test_runner.py b/test/fuzz/test_runner.py
index e2eab2a0fe..af21e7b956 100755
--- a/test/fuzz/test_runner.py
+++ b/test/fuzz/test_runner.py
@@ -143,7 +143,7 @@ def main():
timeout=20,
check=True,
stderr=subprocess.PIPE,
- universal_newlines=True,
+ text=True,
).stderr
if "libFuzzer" not in help_output:
logging.error("Must be built with libFuzzer")
@@ -200,7 +200,7 @@ def generate_corpus(*, fuzz_pool, src_dir, build_dir, corpus_dir, targets):
env=get_fuzz_env(target=t, source_dir=src_dir),
check=True,
stderr=subprocess.PIPE,
- universal_newlines=True,
+ text=True,
).stderr))
futures = []
@@ -241,7 +241,7 @@ def merge_inputs(*, fuzz_pool, corpus, test_list, src_dir, build_dir, merge_dir)
env=get_fuzz_env(target=t, source_dir=src_dir),
check=True,
stderr=subprocess.PIPE,
- universal_newlines=True,
+ text=True,
).stderr
logging.debug(output)
@@ -270,7 +270,7 @@ def run_once(*, fuzz_pool, corpus, test_list, src_dir, build_dir, use_valgrind):
args,
env=get_fuzz_env(target=t, source_dir=src_dir),
stderr=subprocess.PIPE,
- universal_newlines=True,
+ text=True,
)
output += result.stderr
return output, result
@@ -299,7 +299,7 @@ def parse_test_list(*, fuzz_bin):
},
stdout=subprocess.PIPE,
stderr=subprocess.DEVNULL,
- universal_newlines=True,
+ text=True,
).stdout.splitlines()
return test_list_all
diff --git a/test/lint/README.md b/test/lint/README.md
index 8d592c3282..704922d7ab 100644
--- a/test/lint/README.md
+++ b/test/lint/README.md
@@ -1,5 +1,23 @@
This folder contains lint scripts.
+Running locally
+===============
+
+To run linters locally with the same versions as the CI environment, use the included
+Dockerfile:
+
+```sh
+cd ./ci/lint
+docker build -t bitcoin-linter .
+
+cd /root/of/bitcoin/repo
+docker run --rm -v $(pwd):/bitcoin -it bitcoin-linter
+```
+
+After building the container once, you can simply run the last command any time you
+want to lint.
+
+
check-doc.py
============
Check for missing documentation of command line options.
diff --git a/test/lint/lint-assertions.py b/test/lint/lint-assertions.py
index 195ff33d11..e7eecebce5 100755
--- a/test/lint/lint-assertions.py
+++ b/test/lint/lint-assertions.py
@@ -12,7 +12,7 @@ import subprocess
def git_grep(params: [], error_msg: ""):
try:
- output = subprocess.check_output(["git", "grep", *params], universal_newlines=True, encoding="utf8")
+ output = subprocess.check_output(["git", "grep", *params], text=True, encoding="utf8")
print(error_msg)
print(output)
return 1
diff --git a/test/lint/lint-circular-dependencies.py b/test/lint/lint-circular-dependencies.py
index b69bbe7cd0..cf6a5f81f1 100755
--- a/test/lint/lint-circular-dependencies.py
+++ b/test/lint/lint-circular-dependencies.py
@@ -38,14 +38,14 @@ def main():
os.chdir(CODE_DIR)
files = subprocess.check_output(
['git', 'ls-files', '--', '*.h', '*.cpp'],
- universal_newlines=True,
+ text=True,
).splitlines()
command = [sys.executable, "../contrib/devtools/circular-dependencies.py", *files]
dependencies_output = subprocess.run(
command,
stdout=subprocess.PIPE,
- universal_newlines=True,
+ text=True,
)
for dependency_str in dependencies_output.stdout.rstrip().split("\n"):
diff --git a/test/lint/lint-git-commit-check.py b/test/lint/lint-git-commit-check.py
index 049104398a..5897a17e70 100755
--- a/test/lint/lint-git-commit-check.py
+++ b/test/lint/lint-git-commit-check.py
@@ -42,17 +42,17 @@ def main():
commit_range = "HEAD~" + args.prev_commits + "...HEAD"
else:
# This assumes that the target branch of the pull request will be master.
- merge_base = check_output(["git", "merge-base", "HEAD", "master"], universal_newlines=True, encoding="utf8").rstrip("\n")
+ merge_base = check_output(["git", "merge-base", "HEAD", "master"], text=True, encoding="utf8").rstrip("\n")
commit_range = merge_base + "..HEAD"
else:
commit_range = os.getenv("COMMIT_RANGE")
if commit_range == "SKIP_EMPTY_NOT_A_PR":
sys.exit(0)
- commit_hashes = check_output(["git", "log", commit_range, "--format=%H"], universal_newlines=True, encoding="utf8").splitlines()
+ commit_hashes = check_output(["git", "log", commit_range, "--format=%H"], text=True, encoding="utf8").splitlines()
for hash in commit_hashes:
- commit_info = check_output(["git", "log", "--format=%B", "-n", "1", hash], universal_newlines=True, encoding="utf8").splitlines()
+ commit_info = check_output(["git", "log", "--format=%B", "-n", "1", hash], text=True, encoding="utf8").splitlines()
if len(commit_info) >= 2:
if commit_info[1]:
print(f"The subject line of commit hash {hash} is followed by a non-empty line. Subject lines should always be followed by a blank line.")
diff --git a/test/lint/lint-includes.py b/test/lint/lint-includes.py
index b3fa4b9303..459030bb0b 100755
--- a/test/lint/lint-includes.py
+++ b/test/lint/lint-includes.py
@@ -35,13 +35,13 @@ EXPECTED_BOOST_INCLUDES = ["boost/date_time/posix_time/posix_time.hpp",
def get_toplevel():
- return check_output(["git", "rev-parse", "--show-toplevel"], universal_newlines=True, encoding="utf8").rstrip("\n")
+ return check_output(["git", "rev-parse", "--show-toplevel"], text=True, encoding="utf8").rstrip("\n")
def list_files_by_suffix(suffixes):
exclude_args = [":(exclude)" + dir for dir in EXCLUDED_DIRS]
- files_list = check_output(["git", "ls-files", "src"] + exclude_args, universal_newlines=True, encoding="utf8").splitlines()
+ files_list = check_output(["git", "ls-files", "src"] + exclude_args, text=True, encoding="utf8").splitlines()
return [file for file in files_list if file.endswith(suffixes)]
@@ -63,7 +63,7 @@ def find_included_cpps():
included_cpps = list()
try:
- included_cpps = check_output(["git", "grep", "-E", r"^#include [<\"][^>\"]+\.cpp[>\"]", "--", "*.cpp", "*.h"], universal_newlines=True, encoding="utf8").splitlines()
+ included_cpps = check_output(["git", "grep", "-E", r"^#include [<\"][^>\"]+\.cpp[>\"]", "--", "*.cpp", "*.h"], text=True, encoding="utf8").splitlines()
except CalledProcessError as e:
if e.returncode > 1:
raise e
@@ -77,7 +77,7 @@ def find_extra_boosts():
exclusion_set = set()
try:
- included_boosts = check_output(["git", "grep", "-E", r"^#include <boost/", "--", "*.cpp", "*.h"], universal_newlines=True, encoding="utf8").splitlines()
+ included_boosts = check_output(["git", "grep", "-E", r"^#include <boost/", "--", "*.cpp", "*.h"], text=True, encoding="utf8").splitlines()
except CalledProcessError as e:
if e.returncode > 1:
raise e
@@ -100,7 +100,7 @@ def find_quote_syntax_inclusions():
quote_syntax_inclusions = list()
try:
- quote_syntax_inclusions = check_output(["git", "grep", r"^#include \"", "--", "*.cpp", "*.h"] + exclude_args, universal_newlines=True, encoding="utf8").splitlines()
+ quote_syntax_inclusions = check_output(["git", "grep", r"^#include \"", "--", "*.cpp", "*.h"] + exclude_args, text=True, encoding="utf8").splitlines()
except CalledProcessError as e:
if e.returncode > 1:
raise e
@@ -143,13 +143,13 @@ def main():
if extra_boosts:
for boost in extra_boosts:
print(f"A new Boost dependency in the form of \"{boost}\" appears to have been introduced:")
- print(check_output(["git", "grep", boost, "--", "*.cpp", "*.h"], universal_newlines=True, encoding="utf8"))
+ print(check_output(["git", "grep", boost, "--", "*.cpp", "*.h"], text=True, encoding="utf8"))
exit_code = 1
# Check if Boost dependencies are no longer used
for expected_boost in EXPECTED_BOOST_INCLUDES:
try:
- check_output(["git", "grep", "-q", r"^#include <%s>" % expected_boost, "--", "*.cpp", "*.h"], universal_newlines=True, encoding="utf8")
+ check_output(["git", "grep", "-q", r"^#include <%s>" % expected_boost, "--", "*.cpp", "*.h"], text=True, encoding="utf8")
except CalledProcessError as e:
if e.returncode > 1:
raise e
diff --git a/test/lint/lint-locale-dependence.py b/test/lint/lint-locale-dependence.py
index ce7444cd1a..c5cb34b20a 100755
--- a/test/lint/lint-locale-dependence.py
+++ b/test/lint/lint-locale-dependence.py
@@ -223,7 +223,7 @@ def find_locale_dependent_function_uses():
git_grep_output = list()
try:
- git_grep_output = check_output(git_grep_command, universal_newlines=True, encoding="utf8").splitlines()
+ git_grep_output = check_output(git_grep_command, text=True, encoding="utf8").splitlines()
except CalledProcessError as e:
if e.returncode > 1:
raise e
diff --git a/test/lint/lint-logs.py b/test/lint/lint-logs.py
index aaf697467d..de04a1aeca 100755
--- a/test/lint/lint-logs.py
+++ b/test/lint/lint-logs.py
@@ -16,7 +16,7 @@ from subprocess import check_output
def main():
- logs_list = check_output(["git", "grep", "--extended-regexp", r"(LogPrintLevel|LogPrintfCategory|LogPrintf?)\(", "--", "*.cpp"], universal_newlines=True, encoding="utf8").splitlines()
+ logs_list = check_output(["git", "grep", "--extended-regexp", r"(LogPrintLevel|LogPrintfCategory|LogPrintf?)\(", "--", "*.cpp"], text=True, encoding="utf8").splitlines()
unterminated_logs = [line for line in logs_list if not re.search(r'(\\n"|/\* Continued \*/)', line)]
diff --git a/test/lint/lint-python-mutable-default-parameters.py b/test/lint/lint-python-mutable-default-parameters.py
index 3dfc5940f7..820595ea34 100755
--- a/test/lint/lint-python-mutable-default-parameters.py
+++ b/test/lint/lint-python-mutable-default-parameters.py
@@ -21,7 +21,7 @@ def main():
"--",
"*.py",
]
- output = subprocess.run(command, stdout=subprocess.PIPE, universal_newlines=True)
+ output = subprocess.run(command, stdout=subprocess.PIPE, text=True)
if len(output.stdout) > 0:
error_msg = (
"A mutable list or dict seems to be used as default parameter value:\n\n"
diff --git a/test/lint/lint-python-utf8-encoding.py b/test/lint/lint-python-utf8-encoding.py
index 62fdc34d50..364da63468 100755
--- a/test/lint/lint-python-utf8-encoding.py
+++ b/test/lint/lint-python-utf8-encoding.py
@@ -23,7 +23,7 @@ def check_fileopens():
fileopens = list()
try:
- fileopens = check_output(["git", "grep", r" open(", "--", "*.py"] + get_exclude_args(), universal_newlines=True, encoding="utf8").splitlines()
+ fileopens = check_output(["git", "grep", r" open(", "--", "*.py"] + get_exclude_args(), text=True, encoding="utf8").splitlines()
except CalledProcessError as e:
if e.returncode > 1:
raise e
@@ -37,12 +37,12 @@ def check_checked_outputs():
checked_outputs = list()
try:
- checked_outputs = check_output(["git", "grep", "check_output(", "--", "*.py"] + get_exclude_args(), universal_newlines=True, encoding="utf8").splitlines()
+ checked_outputs = check_output(["git", "grep", "check_output(", "--", "*.py"] + get_exclude_args(), text=True, encoding="utf8").splitlines()
except CalledProcessError as e:
if e.returncode > 1:
raise e
- filtered_checked_outputs = [checked_output for checked_output in checked_outputs if re.search(r"universal_newlines=True", checked_output) and not re.search(r"encoding=.(ascii|utf8|utf-8).", checked_output)]
+ filtered_checked_outputs = [checked_output for checked_output in checked_outputs if re.search(r"text=True", checked_output) and not re.search(r"encoding=.(ascii|utf8|utf-8).", checked_output)]
return filtered_checked_outputs
diff --git a/test/lint/lint-shell.py b/test/lint/lint-shell.py
index ed95024ef5..1646bf0d3e 100755
--- a/test/lint/lint-shell.py
+++ b/test/lint/lint-shell.py
@@ -25,7 +25,7 @@ def check_shellcheck_install():
sys.exit(0)
def get_files(command):
- output = subprocess.run(command, stdout=subprocess.PIPE, universal_newlines=True)
+ output = subprocess.run(command, stdout=subprocess.PIPE, text=True)
files = output.stdout.split('\n')
# remove whitespace element
diff --git a/test/lint/lint-submodule.py b/test/lint/lint-submodule.py
index 89d4c80f55..4d2fbf088f 100755
--- a/test/lint/lint-submodule.py
+++ b/test/lint/lint-submodule.py
@@ -13,7 +13,7 @@ import sys
def main():
submodules_list = subprocess.check_output(['git', 'submodule', 'status', '--recursive'],
- universal_newlines = True, encoding = 'utf8').rstrip('\n')
+ text = True, encoding = 'utf8').rstrip('\n')
if submodules_list:
print("These submodules were found, delete them:\n", submodules_list)
sys.exit(1)
diff --git a/test/lint/lint-tests.py b/test/lint/lint-tests.py
index 849ddcb961..1eeb7bb014 100755
--- a/test/lint/lint-tests.py
+++ b/test/lint/lint-tests.py
@@ -23,7 +23,7 @@ def grep_boost_fixture_test_suite():
"src/test/**.cpp",
"src/wallet/test/**.cpp",
]
- return subprocess.check_output(command, universal_newlines=True, encoding="utf8")
+ return subprocess.check_output(command, text=True, encoding="utf8")
def check_matching_test_names(test_suite_list):
diff --git a/test/lint/lint-whitespace.py b/test/lint/lint-whitespace.py
index 72b7ebc394..f5e4a776d0 100755
--- a/test/lint/lint-whitespace.py
+++ b/test/lint/lint-whitespace.py
@@ -80,7 +80,7 @@ def get_diff(commit_range, check_only_code):
else:
what_files = ["."]
- diff = check_output(["git", "diff", "-U0", commit_range, "--"] + what_files + exclude_args, universal_newlines=True, encoding="utf8")
+ diff = check_output(["git", "diff", "-U0", commit_range, "--"] + what_files + exclude_args, text=True, encoding="utf8")
return diff
@@ -93,7 +93,7 @@ def main():
commit_range = "HEAD~" + args.prev_commits + "...HEAD"
else:
# This assumes that the target branch of the pull request will be master.
- merge_base = check_output(["git", "merge-base", "HEAD", "master"], universal_newlines=True, encoding="utf8").rstrip("\n")
+ merge_base = check_output(["git", "merge-base", "HEAD", "master"], text=True, encoding="utf8").rstrip("\n")
commit_range = merge_base + "..HEAD"
else:
commit_range = os.getenv("COMMIT_RANGE")
diff --git a/test/util/test_runner.py b/test/util/test_runner.py
index 03db05c563..ea3626fa65 100755
--- a/test/util/test_runner.py
+++ b/test/util/test_runner.py
@@ -107,7 +107,7 @@ def bctest(testDir, testObj, buildenv):
raise Exception
# Run the test
- proc = subprocess.Popen(execrun, stdin=stdinCfg, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)
+ proc = subprocess.Popen(execrun, stdin=stdinCfg, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
try:
outs = proc.communicate(input=inputData)
except OSError: