aboutsummaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rw-r--r--test/functional/data/rpc_decodescript.json2
-rwxr-xr-xtest/functional/feature_abortnode.py4
-rwxr-xr-xtest/functional/feature_addrman.py8
-rwxr-xr-xtest/functional/feature_block.py14
-rwxr-xr-xtest/functional/feature_dirsymlinks.py2
-rwxr-xr-xtest/functional/feature_index_prune.py4
-rwxr-xr-xtest/functional/feature_init.py2
-rwxr-xr-xtest/functional/feature_loadblock.py2
-rwxr-xr-xtest/functional/feature_reindex.py2
-rwxr-xr-xtest/functional/feature_remove_pruned_files_on_startup.py8
-rwxr-xr-xtest/functional/feature_txindex_compatibility.py24
-rwxr-xr-xtest/functional/feature_unsupported_utxo_db.py4
-rwxr-xr-xtest/functional/interface_usdt_mempool.py5
-rwxr-xr-xtest/functional/mempool_compatibility.py11
-rwxr-xr-xtest/functional/mining_basic.py46
-rwxr-xr-xtest/functional/p2p_getaddr_caching.py9
-rwxr-xr-xtest/functional/p2p_invalid_locator.py2
-rwxr-xr-xtest/functional/p2p_segwit.py8
-rwxr-xr-xtest/functional/rpc_blockchain.py4
-rwxr-xr-xtest/functional/rpc_decodescript.py2
-rwxr-xr-xtest/functional/rpc_dumptxoutset.py19
-rwxr-xr-xtest/functional/rpc_psbt.py9
-rwxr-xr-xtest/functional/rpc_signrawtransactionwithkey.py50
-rw-r--r--test/functional/test_framework/script.py10
-rw-r--r--test/functional/test_framework/siphash.py4
-rwxr-xr-xtest/functional/test_framework/test_node.py55
-rw-r--r--test/functional/test_framework/wallet.py17
-rwxr-xr-xtest/functional/test_runner.py4
-rwxr-xr-xtest/functional/tool_wallet.py15
-rwxr-xr-xtest/functional/wallet_backwards_compatibility.py5
-rwxr-xr-xtest/functional/wallet_basic.py4
-rwxr-xr-xtest/functional/wallet_bumpfee.py79
-rwxr-xr-xtest/functional/wallet_fundrawtransaction.py77
-rwxr-xr-xtest/functional/wallet_migration.py186
-rwxr-xr-xtest/functional/wallet_miniscript.py12
-rwxr-xr-xtest/functional/wallet_resendwallettransactions.py8
-rwxr-xr-xtest/functional/wallet_send.py2
-rwxr-xr-xtest/functional/wallet_signrawtransactionwithwallet.py13
-rwxr-xr-xtest/fuzz/test_runner.py62
-rw-r--r--test/lint/README.md4
-rwxr-xr-xtest/lint/lint-format-strings.py2
-rwxr-xr-xtest/lint/lint-python-utf8-encoding.py2
-rwxr-xr-xtest/lint/run-lint-format-strings.py3
-rw-r--r--test/sanitizer_suppressions/ubsan16
44 files changed, 597 insertions, 224 deletions
diff --git a/test/functional/data/rpc_decodescript.json b/test/functional/data/rpc_decodescript.json
index 5f3e725d4c..4a15ae8792 100644
--- a/test/functional/data/rpc_decodescript.json
+++ b/test/functional/data/rpc_decodescript.json
@@ -69,7 +69,7 @@
"p2sh": "2N34iiGoUUkVSPiaaTFpJjB1FR9TXQu3PGM",
"segwit": {
"asm": "0 96c2368fc30514a438a8bd909f93c49a1549d77198ccbdb792043b666cb24f42",
- "desc": "wsh(raw(02eeee))#gtay4y0z",
+ "desc": "addr(bcrt1qjmprdr7rq522gw9ghkgfly7yng25n4m3nrxtmdujqsakvm9jfapqk795l5)#5akkdska",
"hex": "002096c2368fc30514a438a8bd909f93c49a1549d77198ccbdb792043b666cb24f42",
"address": "bcrt1qjmprdr7rq522gw9ghkgfly7yng25n4m3nrxtmdujqsakvm9jfapqk795l5",
"type": "witness_v0_scripthash",
diff --git a/test/functional/feature_abortnode.py b/test/functional/feature_abortnode.py
index afee9597ad..740d3b7f0e 100755
--- a/test/functional/feature_abortnode.py
+++ b/test/functional/feature_abortnode.py
@@ -25,7 +25,7 @@ class AbortNodeTest(BitcoinTestFramework):
self.generate(self.nodes[0], 3, sync_fun=self.no_op)
# Deleting the undo file will result in reorg failure
- (self.nodes[0].chain_path / "blocks" / "rev00000.dat").unlink()
+ (self.nodes[0].blocks_path / "rev00000.dat").unlink()
# Connecting to a node with a more work chain will trigger a reorg
# attempt.
@@ -36,7 +36,7 @@ class AbortNodeTest(BitcoinTestFramework):
# Check that node0 aborted
self.log.info("Waiting for crash")
- self.nodes[0].wait_until_stopped(timeout=5, expect_error=True)
+ self.nodes[0].wait_until_stopped(timeout=5, expect_error=True, expected_stderr="Error: A fatal internal error occurred, see debug.log for details")
self.log.info("Node crashed - now verifying restart fails")
self.nodes[0].assert_start_raises_init_error()
diff --git a/test/functional/feature_addrman.py b/test/functional/feature_addrman.py
index 7877f9d302..d901b7bcf9 100755
--- a/test/functional/feature_addrman.py
+++ b/test/functional/feature_addrman.py
@@ -32,12 +32,12 @@ def serialize_addrman(
r += struct.pack("B", format)
r += struct.pack("B", INCOMPATIBILITY_BASE + lowest_compatible)
r += ser_uint256(bucket_key)
- r += struct.pack("i", len_new or len(new))
- r += struct.pack("i", len_tried or len(tried))
+ r += struct.pack("<i", len_new or len(new))
+ r += struct.pack("<i", len_tried or len(tried))
ADDRMAN_NEW_BUCKET_COUNT = 1 << 10
- r += struct.pack("i", ADDRMAN_NEW_BUCKET_COUNT ^ (1 << 30))
+ r += struct.pack("<i", ADDRMAN_NEW_BUCKET_COUNT ^ (1 << 30))
for _ in range(ADDRMAN_NEW_BUCKET_COUNT):
- r += struct.pack("i", 0)
+ r += struct.pack("<i", 0)
checksum = hash256(r)
r += mock_checksum or checksum
return r
diff --git a/test/functional/feature_block.py b/test/functional/feature_block.py
index 765db97445..58ef1e761d 100755
--- a/test/functional/feature_block.py
+++ b/test/functional/feature_block.py
@@ -43,8 +43,7 @@ from test_framework.script import (
OP_INVALIDOPCODE,
OP_RETURN,
OP_TRUE,
- SIGHASH_ALL,
- LegacySignatureHash,
+ sign_input_legacy,
)
from test_framework.script_util import (
script_to_p2sh_script,
@@ -539,12 +538,8 @@ class FullBlockTest(BitcoinTestFramework):
# second input is corresponding P2SH output from b39
tx.vin.append(CTxIn(COutPoint(b39.vtx[i].sha256, 0), b''))
# Note: must pass the redeem_script (not p2sh_script) to the signature hash function
- (sighash, err) = LegacySignatureHash(redeem_script, tx, 1, SIGHASH_ALL)
- sig = self.coinbase_key.sign_ecdsa(sighash) + bytes(bytearray([SIGHASH_ALL]))
- scriptSig = CScript([sig, redeem_script])
-
- tx.vin[1].scriptSig = scriptSig
- tx.rehash()
+ tx.vin[1].scriptSig = CScript([redeem_script])
+ sign_input_legacy(tx, 1, redeem_script, self.coinbase_key)
new_txs.append(tx)
lastOutpoint = COutPoint(tx.sha256, 0)
@@ -1338,8 +1333,7 @@ class FullBlockTest(BitcoinTestFramework):
if (scriptPubKey[0] == OP_TRUE): # an anyone-can-spend
tx.vin[0].scriptSig = CScript()
return
- (sighash, err) = LegacySignatureHash(spend_tx.vout[0].scriptPubKey, tx, 0, SIGHASH_ALL)
- tx.vin[0].scriptSig = CScript([self.coinbase_key.sign_ecdsa(sighash) + bytes(bytearray([SIGHASH_ALL]))])
+ sign_input_legacy(tx, 0, spend_tx.vout[0].scriptPubKey, self.coinbase_key)
def create_and_sign_transaction(self, spend_tx, value, script=CScript([OP_TRUE])):
tx = self.create_tx(spend_tx, 0, value, script)
diff --git a/test/functional/feature_dirsymlinks.py b/test/functional/feature_dirsymlinks.py
index 288754c04c..96f4aed08a 100755
--- a/test/functional/feature_dirsymlinks.py
+++ b/test/functional/feature_dirsymlinks.py
@@ -26,7 +26,7 @@ class SymlinkTest(BitcoinTestFramework):
self.stop_node(0)
rename_and_link(
- from_name=self.nodes[0].chain_path / "blocks",
+ from_name=self.nodes[0].blocks_path,
to_name=dir_new_blocks,
)
rename_and_link(
diff --git a/test/functional/feature_index_prune.py b/test/functional/feature_index_prune.py
index 77a056346a..d6e802b399 100755
--- a/test/functional/feature_index_prune.py
+++ b/test/functional/feature_index_prune.py
@@ -3,6 +3,7 @@
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test indices in conjunction with prune."""
+import os
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
@@ -127,8 +128,9 @@ class FeatureIndexPruneTest(BitcoinTestFramework):
self.log.info("make sure we get an init error when starting the nodes again with the indices")
filter_msg = "Error: basic block filter index best block of the index goes beyond pruned data. Please disable the index or reindex (which will download the whole blockchain again)"
stats_msg = "Error: coinstatsindex best block of the index goes beyond pruned data. Please disable the index or reindex (which will download the whole blockchain again)"
+ end_msg = f"{os.linesep}Error: Failed to start indexes, shutting down.."
for i, msg in enumerate([filter_msg, stats_msg, filter_msg]):
- self.nodes[i].assert_start_raises_init_error(extra_args=self.extra_args[i], expected_msg=msg)
+ self.nodes[i].assert_start_raises_init_error(extra_args=self.extra_args[i], expected_msg=msg+end_msg)
self.log.info("make sure the nodes start again with the indices and an additional -reindex arg")
for i in range(3):
diff --git a/test/functional/feature_init.py b/test/functional/feature_init.py
index 7af67730bd..64ca312b84 100755
--- a/test/functional/feature_init.py
+++ b/test/functional/feature_init.py
@@ -71,7 +71,7 @@ class InitStressTest(BitcoinTestFramework):
b'init message: Starting network threads',
b'net thread start',
b'addcon thread start',
- b'loadblk thread start',
+ b'initload thread start',
b'txindex thread start',
b'block filter index thread start',
b'coinstatsindex thread start',
diff --git a/test/functional/feature_loadblock.py b/test/functional/feature_loadblock.py
index c90ccc4936..12d65fde68 100755
--- a/test/functional/feature_loadblock.py
+++ b/test/functional/feature_loadblock.py
@@ -37,7 +37,7 @@ class LoadblockTest(BitcoinTestFramework):
cfg_file = os.path.join(data_dir, "linearize.cfg")
bootstrap_file = os.path.join(self.options.tmpdir, "bootstrap.dat")
genesis_block = self.nodes[0].getblockhash(0)
- blocks_dir = self.nodes[0].chain_path / "blocks"
+ blocks_dir = self.nodes[0].blocks_path
hash_list = tempfile.NamedTemporaryFile(dir=data_dir,
mode='w',
delete=False,
diff --git a/test/functional/feature_reindex.py b/test/functional/feature_reindex.py
index fcbb49d420..83f1c5003c 100755
--- a/test/functional/feature_reindex.py
+++ b/test/functional/feature_reindex.py
@@ -38,7 +38,7 @@ class ReindexTest(BitcoinTestFramework):
# In this test environment, blocks will always be in order (since
# we're generating them rather than getting them from peers), so to
# test out-of-order handling, swap blocks 1 and 2 on disk.
- blk0 = self.nodes[0].chain_path / "blocks" / "blk00000.dat"
+ blk0 = self.nodes[0].blocks_path / "blk00000.dat"
with open(blk0, 'r+b') as bf:
# Read at least the first few blocks (including genesis)
b = bf.read(2000)
diff --git a/test/functional/feature_remove_pruned_files_on_startup.py b/test/functional/feature_remove_pruned_files_on_startup.py
index a55e08ef1a..c128587949 100755
--- a/test/functional/feature_remove_pruned_files_on_startup.py
+++ b/test/functional/feature_remove_pruned_files_on_startup.py
@@ -20,10 +20,10 @@ class FeatureRemovePrunedFilesOnStartupTest(BitcoinTestFramework):
self.sync_blocks()
def run_test(self):
- blk0 = self.nodes[0].chain_path / "blocks" / "blk00000.dat"
- rev0 = self.nodes[0].chain_path / "blocks" / "rev00000.dat"
- blk1 = self.nodes[0].chain_path / "blocks" / "blk00001.dat"
- rev1 = self.nodes[0].chain_path / "blocks" / "rev00001.dat"
+ blk0 = self.nodes[0].blocks_path / "blk00000.dat"
+ rev0 = self.nodes[0].blocks_path / "rev00000.dat"
+ blk1 = self.nodes[0].blocks_path / "blk00001.dat"
+ rev1 = self.nodes[0].blocks_path / "rev00001.dat"
self.mine_batches(800)
fo1 = os.open(blk0, os.O_RDONLY)
fo2 = os.open(rev1, os.O_RDONLY)
diff --git a/test/functional/feature_txindex_compatibility.py b/test/functional/feature_txindex_compatibility.py
index a5b25cbd71..572e12df13 100755
--- a/test/functional/feature_txindex_compatibility.py
+++ b/test/functional/feature_txindex_compatibility.py
@@ -11,16 +11,16 @@ import os
import shutil
from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import assert_raises_rpc_error
from test_framework.wallet import MiniWallet
class TxindexCompatibilityTest(BitcoinTestFramework):
def set_test_params(self):
- self.num_nodes = 3
+ self.num_nodes = 2
self.extra_args = [
["-reindex", "-txindex"],
[],
- [],
]
def skip_test_if_missing_module(self):
@@ -33,12 +33,10 @@ class TxindexCompatibilityTest(BitcoinTestFramework):
versions=[
160300, # Last release with legacy txindex
None, # For MiniWallet, without migration code
- 220000, # Last release with migration code (0.17.x - 22.x)
],
)
self.start_nodes()
self.connect_nodes(0, 1)
- self.connect_nodes(1, 2)
def run_test(self):
mini_wallet = MiniWallet(self.nodes[1])
@@ -47,22 +45,12 @@ class TxindexCompatibilityTest(BitcoinTestFramework):
self.generate(self.nodes[1], 1)
self.log.info("Check legacy txindex")
+ assert_raises_rpc_error(-5, "Use -txindex", lambda: self.nodes[1].getrawtransaction(txid=spend_utxo["txid"]))
self.nodes[0].getrawtransaction(txid=spend_utxo["txid"]) # Requires -txindex
self.stop_nodes()
legacy_chain_dir = self.nodes[0].chain_path
- self.log.info("Migrate legacy txindex")
- migrate_chain_dir = self.nodes[2].chain_path
- shutil.rmtree(migrate_chain_dir)
- shutil.copytree(legacy_chain_dir, migrate_chain_dir)
- with self.nodes[2].assert_debug_log([
- "Upgrading txindex database...",
- "txindex is enabled at height 200",
- ]):
- self.start_node(2, extra_args=["-txindex"])
- self.nodes[2].getrawtransaction(txid=spend_utxo["txid"]) # Requires -txindex
-
self.log.info("Drop legacy txindex")
drop_index_chain_dir = self.nodes[1].chain_path
shutil.rmtree(drop_index_chain_dir)
@@ -73,16 +61,14 @@ class TxindexCompatibilityTest(BitcoinTestFramework):
)
# Build txindex from scratch and check there is no error this time
self.start_node(1, extra_args=["-txindex"])
- self.nodes[2].getrawtransaction(txid=spend_utxo["txid"]) # Requires -txindex
+ self.wait_until(lambda: self.nodes[1].getindexinfo()["txindex"]["synced"] == True)
+ self.nodes[1].getrawtransaction(txid=spend_utxo["txid"]) # Requires -txindex
self.stop_nodes()
self.log.info("Check migrated txindex cannot be read by legacy node")
err_msg = f": You need to rebuild the database using -reindex to change -txindex.{os.linesep}Please restart with -reindex or -reindex-chainstate to recover."
shutil.rmtree(legacy_chain_dir)
- shutil.copytree(migrate_chain_dir, legacy_chain_dir)
- self.nodes[0].assert_start_raises_init_error(extra_args=["-txindex"], expected_msg=err_msg)
- shutil.rmtree(legacy_chain_dir)
shutil.copytree(drop_index_chain_dir, legacy_chain_dir)
self.nodes[0].assert_start_raises_init_error(extra_args=["-txindex"], expected_msg=err_msg)
diff --git a/test/functional/feature_unsupported_utxo_db.py b/test/functional/feature_unsupported_utxo_db.py
index 1c8c08d1d8..6acf551216 100755
--- a/test/functional/feature_unsupported_utxo_db.py
+++ b/test/functional/feature_unsupported_utxo_db.py
@@ -40,9 +40,9 @@ class UnsupportedUtxoDbTest(BitcoinTestFramework):
self.log.info("Check init error")
legacy_utxos_dir = self.nodes[0].chain_path / "chainstate"
- legacy_blocks_dir = self.nodes[0].chain_path / "blocks"
+ legacy_blocks_dir = self.nodes[0].blocks_path
recent_utxos_dir = self.nodes[1].chain_path / "chainstate"
- recent_blocks_dir = self.nodes[1].chain_path / "blocks"
+ recent_blocks_dir = self.nodes[1].blocks_path
shutil.copytree(legacy_utxos_dir, recent_utxos_dir)
shutil.copytree(legacy_blocks_dir, recent_blocks_dir)
self.nodes[1].assert_start_raises_init_error(
diff --git a/test/functional/interface_usdt_mempool.py b/test/functional/interface_usdt_mempool.py
index f138fa44cc..67cf649830 100755
--- a/test/functional/interface_usdt_mempool.py
+++ b/test/functional/interface_usdt_mempool.py
@@ -307,7 +307,10 @@ class MempoolTracepointTest(BitcoinTestFramework):
self.log.info("Ensuring mempool:rejected event was handled successfully...")
assert_equal(EXPECTED_REJECTED_EVENTS, handled_rejected_events)
assert_equal(bytes(event.hash)[::-1].hex(), tx["tx"].hash)
- assert_equal(event.reason.decode("UTF-8"), "min relay fee not met")
+ # The next test is already known to fail, so disable it to avoid
+ # wasting CPU time and developer time. See
+ # https://github.com/bitcoin/bitcoin/issues/27380
+ #assert_equal(event.reason.decode("UTF-8"), "min relay fee not met")
bpf.cleanup()
self.generate(self.wallet, 1)
diff --git a/test/functional/mempool_compatibility.py b/test/functional/mempool_compatibility.py
index 3f632d3d56..fd3e219586 100755
--- a/test/functional/mempool_compatibility.py
+++ b/test/functional/mempool_compatibility.py
@@ -10,8 +10,6 @@ In case we need to break mempool compatibility we can continue to use the test b
Previous releases are required by this test, see test/README.md.
"""
-import os
-
from test_framework.blocktools import COINBASE_MATURITY
from test_framework.test_framework import BitcoinTestFramework
from test_framework.wallet import (
@@ -23,6 +21,7 @@ from test_framework.wallet import (
class MempoolCompatibilityTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 2
+ self.setup_clean_chain = True
def skip_test_if_missing_module(self):
self.skip_if_no_previous_releases()
@@ -55,9 +54,9 @@ class MempoolCompatibilityTest(BitcoinTestFramework):
self.stop_node(1)
self.log.info("Move mempool.dat from old to new node")
- old_node_mempool = os.path.join(old_node.chain_path, 'mempool.dat')
- new_node_mempool = os.path.join(new_node.chain_path, 'mempool.dat')
- os.rename(old_node_mempool, new_node_mempool)
+ old_node_mempool = old_node.chain_path / "mempool.dat"
+ new_node_mempool = new_node.chain_path / "mempool.dat"
+ old_node_mempool.rename(new_node_mempool)
self.log.info("Start new node and verify mempool contains the tx")
self.start_node(1)
@@ -70,7 +69,7 @@ class MempoolCompatibilityTest(BitcoinTestFramework):
self.stop_node(1)
self.log.info("Move mempool.dat from new to old node")
- os.rename(new_node_mempool, old_node_mempool)
+ new_node_mempool.rename(old_node_mempool)
self.log.info("Start old node again and verify mempool contains both txs")
self.start_node(0, ['-nowallet'])
diff --git a/test/functional/mining_basic.py b/test/functional/mining_basic.py
index aabf06ee53..56cd615dac 100755
--- a/test/functional/mining_basic.py
+++ b/test/functional/mining_basic.py
@@ -18,9 +18,10 @@ from test_framework.blocktools import (
TIME_GENESIS_BLOCK,
)
from test_framework.messages import (
+ BLOCK_HEADER_SIZE,
CBlock,
CBlockHeader,
- BLOCK_HEADER_SIZE,
+ COIN,
ser_uint256,
)
from test_framework.p2p import P2PDataStore
@@ -28,12 +29,14 @@ from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
assert_raises_rpc_error,
+ get_fee,
)
from test_framework.wallet import MiniWallet
VERSIONBITS_TOP_BITS = 0x20000000
VERSIONBITS_DEPLOYMENT_TESTDUMMY_BIT = 28
+DEFAULT_BLOCK_MIN_TX_FEE = 1000 # default `-blockmintxfee` setting [sat/kvB]
def assert_template(node, block, expect, rehash=True):
@@ -73,6 +76,45 @@ class MiningTest(BitcoinTestFramework):
self.restart_node(0)
self.connect_nodes(0, 1)
+ def test_blockmintxfee_parameter(self):
+ self.log.info("Test -blockmintxfee setting")
+ self.restart_node(0, extra_args=['-minrelaytxfee=0', '-persistmempool=0'])
+ node = self.nodes[0]
+
+ # test default (no parameter), zero and a bunch of arbitrary blockmintxfee rates [sat/kvB]
+ for blockmintxfee_sat_kvb in (DEFAULT_BLOCK_MIN_TX_FEE, 0, 50, 100, 500, 2500, 5000, 21000, 333333, 2500000):
+ blockmintxfee_btc_kvb = blockmintxfee_sat_kvb / Decimal(COIN)
+ if blockmintxfee_sat_kvb == DEFAULT_BLOCK_MIN_TX_FEE:
+ self.log.info(f"-> Default -blockmintxfee setting ({blockmintxfee_sat_kvb} sat/kvB)...")
+ else:
+ blockmintxfee_parameter = f"-blockmintxfee={blockmintxfee_btc_kvb:.8f}"
+ self.log.info(f"-> Test {blockmintxfee_parameter} ({blockmintxfee_sat_kvb} sat/kvB)...")
+ self.restart_node(0, extra_args=[blockmintxfee_parameter, '-minrelaytxfee=0', '-persistmempool=0'])
+ self.wallet.rescan_utxos() # to avoid spending outputs of txs that are not in mempool anymore after restart
+
+ # submit one tx with exactly the blockmintxfee rate, and one slightly below
+ tx_with_min_feerate = self.wallet.send_self_transfer(from_node=node, fee_rate=blockmintxfee_btc_kvb)
+ assert_equal(tx_with_min_feerate["fee"], get_fee(tx_with_min_feerate["tx"].get_vsize(), blockmintxfee_btc_kvb))
+ if blockmintxfee_btc_kvb > 0:
+ lowerfee_btc_kvb = blockmintxfee_btc_kvb - Decimal(10)/COIN # 0.01 sat/vbyte lower
+ tx_below_min_feerate = self.wallet.send_self_transfer(from_node=node, fee_rate=lowerfee_btc_kvb)
+ assert_equal(tx_below_min_feerate["fee"], get_fee(tx_below_min_feerate["tx"].get_vsize(), lowerfee_btc_kvb))
+ else: # go below zero fee by using modified fees
+ tx_below_min_feerate = self.wallet.send_self_transfer(from_node=node, fee_rate=blockmintxfee_btc_kvb)
+ node.prioritisetransaction(tx_below_min_feerate["txid"], 0, -1)
+
+ # check that tx below specified fee-rate is neither in template nor in the actual block
+ block_template = node.getblocktemplate(NORMAL_GBT_REQUEST_PARAMS)
+ block_template_txids = [tx['txid'] for tx in block_template['transactions']]
+ self.generate(self.wallet, 1, sync_fun=self.no_op)
+ block = node.getblock(node.getbestblockhash(), verbosity=2)
+ block_txids = [tx['txid'] for tx in block['tx']]
+
+ assert tx_with_min_feerate['txid'] in block_template_txids
+ assert tx_with_min_feerate['txid'] in block_txids
+ assert tx_below_min_feerate['txid'] not in block_template_txids
+ assert tx_below_min_feerate['txid'] not in block_txids
+
def run_test(self):
node = self.nodes[0]
self.wallet = MiniWallet(node)
@@ -279,6 +321,8 @@ class MiningTest(BitcoinTestFramework):
node.submitheader(hexdata=CBlockHeader(bad_block_root).serialize().hex())
assert_equal(node.submitblock(hexdata=block.serialize().hex()), 'duplicate') # valid
+ self.test_blockmintxfee_parameter()
+
if __name__ == '__main__':
MiningTest().main()
diff --git a/test/functional/p2p_getaddr_caching.py b/test/functional/p2p_getaddr_caching.py
index 1c9ad7289b..60b43c32ae 100755
--- a/test/functional/p2p_getaddr_caching.py
+++ b/test/functional/p2p_getaddr_caching.py
@@ -6,7 +6,6 @@
import time
-from test_framework.messages import msg_getaddr
from test_framework.p2p import (
P2PInterface,
p2p_lock
@@ -21,6 +20,7 @@ from test_framework.util import (
MAX_ADDR_TO_SEND = 1000
MAX_PCT_ADDR_TO_SEND = 23
+
class AddrReceiver(P2PInterface):
def __init__(self):
@@ -70,11 +70,8 @@ class AddrTest(BitcoinTestFramework):
cur_mock_time = int(time.time())
for i in range(N):
addr_receiver_local = self.nodes[0].add_p2p_connection(AddrReceiver())
- addr_receiver_local.send_and_ping(msg_getaddr())
addr_receiver_onion1 = self.nodes[0].add_p2p_connection(AddrReceiver(), dstport=self.onion_port1)
- addr_receiver_onion1.send_and_ping(msg_getaddr())
addr_receiver_onion2 = self.nodes[0].add_p2p_connection(AddrReceiver(), dstport=self.onion_port2)
- addr_receiver_onion2.send_and_ping(msg_getaddr())
# Trigger response
cur_mock_time += 5 * 60
@@ -105,11 +102,8 @@ class AddrTest(BitcoinTestFramework):
self.log.info('After time passed, see a new response to addr request')
addr_receiver_local = self.nodes[0].add_p2p_connection(AddrReceiver())
- addr_receiver_local.send_and_ping(msg_getaddr())
addr_receiver_onion1 = self.nodes[0].add_p2p_connection(AddrReceiver(), dstport=self.onion_port1)
- addr_receiver_onion1.send_and_ping(msg_getaddr())
addr_receiver_onion2 = self.nodes[0].add_p2p_connection(AddrReceiver(), dstport=self.onion_port2)
- addr_receiver_onion2.send_and_ping(msg_getaddr())
# Trigger response
cur_mock_time += 5 * 60
@@ -123,5 +117,6 @@ class AddrTest(BitcoinTestFramework):
assert set(last_response_on_onion_bind1) != set(addr_receiver_onion1.get_received_addrs())
assert set(last_response_on_onion_bind2) != set(addr_receiver_onion2.get_received_addrs())
+
if __name__ == '__main__':
AddrTest().main()
diff --git a/test/functional/p2p_invalid_locator.py b/test/functional/p2p_invalid_locator.py
index 626422370a..32a23532a2 100755
--- a/test/functional/p2p_invalid_locator.py
+++ b/test/functional/p2p_invalid_locator.py
@@ -32,7 +32,7 @@ class InvalidLocatorTest(BitcoinTestFramework):
within_max_peer = node.add_p2p_connection(P2PInterface())
msg.locator.vHave = [int(node.getblockhash(i - 1), 16) for i in range(block_count, block_count - (MAX_LOCATOR_SZ), -1)]
within_max_peer.send_message(msg)
- if type(msg) == msg_getheaders:
+ if type(msg) is msg_getheaders:
within_max_peer.wait_for_header(node.getbestblockhash())
else:
within_max_peer.wait_for_block(int(node.getbestblockhash(), 16))
diff --git a/test/functional/p2p_segwit.py b/test/functional/p2p_segwit.py
index bfae190c66..1e7bc95a63 100755
--- a/test/functional/p2p_segwit.py
+++ b/test/functional/p2p_segwit.py
@@ -71,8 +71,8 @@ from test_framework.script import (
SIGHASH_NONE,
SIGHASH_SINGLE,
SegwitV0SignatureHash,
- LegacySignatureHash,
hash160,
+ sign_input_legacy,
)
from test_framework.script_util import (
key_to_p2pk_script,
@@ -1529,10 +1529,8 @@ class SegWitTest(BitcoinTestFramework):
tx5 = CTransaction()
tx5.vin.append(CTxIn(COutPoint(tx4.sha256, 0), b""))
tx5.vout.append(CTxOut(tx4.vout[0].nValue - 1000, CScript([OP_TRUE])))
- (sig_hash, err) = LegacySignatureHash(script_pubkey, tx5, 0, SIGHASH_ALL)
- signature = key.sign_ecdsa(sig_hash) + b'\x01' # 0x1 is SIGHASH_ALL
- tx5.vin[0].scriptSig = CScript([signature, pubkey])
- tx5.rehash()
+ tx5.vin[0].scriptSig = CScript([pubkey])
+ sign_input_legacy(tx5, 0, script_pubkey, key)
# Should pass policy and consensus.
test_transaction_acceptance(self.nodes[0], self.test_node, tx5, True, True)
block = self.build_next_block()
diff --git a/test/functional/rpc_blockchain.py b/test/functional/rpc_blockchain.py
index 5f2bece733..18a0a0c6cc 100755
--- a/test/functional/rpc_blockchain.py
+++ b/test/functional/rpc_blockchain.py
@@ -577,8 +577,8 @@ class BlockchainTest(BitcoinTestFramework):
self.log.info("Test that getblock with verbosity 2 and 3 still works with pruned Undo data")
def move_block_file(old, new):
- old_path = self.nodes[0].chain_path / "blocks" / old
- new_path = self.nodes[0].chain_path / "blocks" / new
+ old_path = self.nodes[0].blocks_path / old
+ new_path = self.nodes[0].blocks_path / new
old_path.rename(new_path)
# Move instead of deleting so we can restore chain state afterwards
diff --git a/test/functional/rpc_decodescript.py b/test/functional/rpc_decodescript.py
index 673836bd04..f37e61ab50 100755
--- a/test/functional/rpc_decodescript.py
+++ b/test/functional/rpc_decodescript.py
@@ -271,7 +271,7 @@ class DecodeScriptTest(BitcoinTestFramework):
assert res["segwit"]["desc"] == "wsh(and_v(and_v(v:hash160(ffffffffffffffffffffffffffffffffffffffff),v:pk(0250929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0)),older(1)))#gm8xz4fl"
# Miniscript-incompatible offered HTLC
res = self.nodes[0].decodescript("82012088a914ffffffffffffffffffffffffffffffffffffffff882102ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffacb2")
- assert res["segwit"]["desc"] == "wsh(raw(82012088a914ffffffffffffffffffffffffffffffffffffffff882102ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffacb2))#ra6w2xa7"
+ assert res["segwit"]["desc"] == "addr(bcrt1q73qyfypp47hvgnkjqnav0j3k2lq3v76wg22dk8tmwuz5sfgv66xsvxg6uu)#9p3q328s"
# Miniscript-compatible multisig bigger than 520 byte P2SH limit.
res = self.nodes[0].decodescript("5b21020e0338c96a8870479f2396c373cc7696ba124e8635d41b0ea581112b678172612102675333a4e4b8fb51d9d4e22fa5a8eaced3fdac8a8cbf9be8c030f75712e6af992102896807d54bc55c24981f24a453c60ad3e8993d693732288068a23df3d9f50d4821029e51a5ef5db3137051de8323b001749932f2ff0d34c82e96a2c2461de96ae56c2102a4e1a9638d46923272c266631d94d36bdb03a64ee0e14c7518e49d2f29bc401021031c41fdbcebe17bec8d49816e00ca1b5ac34766b91c9f2ac37d39c63e5e008afb2103079e252e85abffd3c401a69b087e590a9b86f33f574f08129ccbd3521ecf516b2103111cf405b627e22135b3b3733a4a34aa5723fb0f58379a16d32861bf576b0ec2210318f331b3e5d38156da6633b31929c5b220349859cc9ca3d33fb4e68aa08401742103230dae6b4ac93480aeab26d000841298e3b8f6157028e47b0897c1e025165de121035abff4281ff00660f99ab27bb53e6b33689c2cd8dcd364bc3c90ca5aea0d71a62103bd45cddfacf2083b14310ae4a84e25de61e451637346325222747b157446614c2103cc297026b06c71cbfa52089149157b5ff23de027ac5ab781800a578192d175462103d3bde5d63bdb3a6379b461be64dad45eabff42f758543a9645afd42f6d4248282103ed1e8d5109c9ed66f7941bc53cc71137baa76d50d274bda8d5e8ffbd6e61fe9a5fae736402c00fb269522103aab896d53a8e7d6433137bbba940f9c521e085dd07e60994579b64a6d992cf79210291b7d0b1b692f8f524516ed950872e5da10fb1b808b5a526dedc6fed1cf29807210386aa9372fbab374593466bc5451dc59954e90787f08060964d95c87ef34ca5bb53ae68")
assert_equal(res["segwit"]["desc"], "wsh(or_d(multi(11,020e0338c96a8870479f2396c373cc7696ba124e8635d41b0ea581112b67817261,02675333a4e4b8fb51d9d4e22fa5a8eaced3fdac8a8cbf9be8c030f75712e6af99,02896807d54bc55c24981f24a453c60ad3e8993d693732288068a23df3d9f50d48,029e51a5ef5db3137051de8323b001749932f2ff0d34c82e96a2c2461de96ae56c,02a4e1a9638d46923272c266631d94d36bdb03a64ee0e14c7518e49d2f29bc4010,031c41fdbcebe17bec8d49816e00ca1b5ac34766b91c9f2ac37d39c63e5e008afb,03079e252e85abffd3c401a69b087e590a9b86f33f574f08129ccbd3521ecf516b,03111cf405b627e22135b3b3733a4a34aa5723fb0f58379a16d32861bf576b0ec2,0318f331b3e5d38156da6633b31929c5b220349859cc9ca3d33fb4e68aa0840174,03230dae6b4ac93480aeab26d000841298e3b8f6157028e47b0897c1e025165de1,035abff4281ff00660f99ab27bb53e6b33689c2cd8dcd364bc3c90ca5aea0d71a6,03bd45cddfacf2083b14310ae4a84e25de61e451637346325222747b157446614c,03cc297026b06c71cbfa52089149157b5ff23de027ac5ab781800a578192d17546,03d3bde5d63bdb3a6379b461be64dad45eabff42f758543a9645afd42f6d424828,03ed1e8d5109c9ed66f7941bc53cc71137baa76d50d274bda8d5e8ffbd6e61fe9a),and_v(v:older(4032),multi(2,03aab896d53a8e7d6433137bbba940f9c521e085dd07e60994579b64a6d992cf79,0291b7d0b1b692f8f524516ed950872e5da10fb1b808b5a526dedc6fed1cf29807,0386aa9372fbab374593466bc5451dc59954e90787f08060964d95c87ef34ca5bb))))#7jwwklk4")
diff --git a/test/functional/rpc_dumptxoutset.py b/test/functional/rpc_dumptxoutset.py
index 4260e95629..2cae602cc2 100755
--- a/test/functional/rpc_dumptxoutset.py
+++ b/test/functional/rpc_dumptxoutset.py
@@ -4,13 +4,15 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test the generation of UTXO snapshots using `dumptxoutset`.
"""
+from pathlib import Path
from test_framework.blocktools import COINBASE_MATURITY
from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import assert_equal, assert_raises_rpc_error
-
-import hashlib
-from pathlib import Path
+from test_framework.util import (
+ assert_equal,
+ assert_raises_rpc_error,
+ sha256sum_file,
+)
class DumptxoutsetTest(BitcoinTestFramework):
@@ -39,11 +41,10 @@ class DumptxoutsetTest(BitcoinTestFramework):
out['base_hash'],
'09abf0e7b510f61ca6cf33bab104e9ee99b3528b371d27a2d4b39abb800fba7e')
- with open(str(expected_path), 'rb') as f:
- digest = hashlib.sha256(f.read()).hexdigest()
- # UTXO snapshot hash should be deterministic based on mocked time.
- assert_equal(
- digest, 'b1bacb602eacf5fbc9a7c2ef6eeb0d229c04e98bdf0c2ea5929012cd0eae3830')
+ # UTXO snapshot hash should be deterministic based on mocked time.
+ assert_equal(
+ sha256sum_file(str(expected_path)).hex(),
+ 'b1bacb602eacf5fbc9a7c2ef6eeb0d229c04e98bdf0c2ea5929012cd0eae3830')
assert_equal(
out['txoutset_hash'], '1f7e3befd45dc13ae198dfbb22869a9c5c4196f8e9ef9735831af1288033f890')
diff --git a/test/functional/rpc_psbt.py b/test/functional/rpc_psbt.py
index c4ed4da0f2..b574a370d6 100755
--- a/test/functional/rpc_psbt.py
+++ b/test/functional/rpc_psbt.py
@@ -327,7 +327,7 @@ class PSBTTest(BitcoinTestFramework):
assert_raises_rpc_error(-3, "Invalid amount",
self.nodes[1].walletcreatefundedpsbt, inputs, outputs, 0, {param: invalid_value, "add_inputs": True})
# Test fee_rate values that cannot be represented in sat/vB.
- for invalid_value in [0.0001, 0.00000001, 0.00099999, 31.99999999, "0.0001", "0.00000001", "0.00099999", "31.99999999"]:
+ for invalid_value in [0.0001, 0.00000001, 0.00099999, 31.99999999]:
assert_raises_rpc_error(-3, "Invalid amount",
self.nodes[1].walletcreatefundedpsbt, inputs, outputs, 0, {"fee_rate": invalid_value, "add_inputs": True})
@@ -883,6 +883,9 @@ class PSBTTest(BitcoinTestFramework):
comb_psbt = self.nodes[0].combinepsbt([psbt, parsed_psbt.to_base64()])
assert_equal(comb_psbt, psbt)
+ self.log.info("Test walletprocesspsbt raises if an invalid sighashtype is passed")
+ assert_raises_rpc_error(-8, "all is not a valid sighash parameter.", self.nodes[0].walletprocesspsbt, psbt, sighashtype="all")
+
self.log.info("Test decoding PSBT with per-input preimage types")
# note that the decodepsbt RPC doesn't check whether preimages and hashes match
hash_ripemd160, preimage_ripemd160 = random_bytes(20), random_bytes(50)
@@ -982,5 +985,9 @@ class PSBTTest(BitcoinTestFramework):
rawtx = self.nodes[2].finalizepsbt(psbt)["hex"]
self.nodes[2].sendrawtransaction(rawtx)
+ self.log.info("Test descriptorprocesspsbt raises if an invalid sighashtype is passed")
+ assert_raises_rpc_error(-8, "all is not a valid sighash parameter.", self.nodes[2].descriptorprocesspsbt, psbt, [descriptor], sighashtype="all")
+
+
if __name__ == '__main__':
PSBTTest().main()
diff --git a/test/functional/rpc_signrawtransactionwithkey.py b/test/functional/rpc_signrawtransactionwithkey.py
index ac7a86704f..0913f5057e 100755
--- a/test/functional/rpc_signrawtransactionwithkey.py
+++ b/test/functional/rpc_signrawtransactionwithkey.py
@@ -14,6 +14,7 @@ from test_framework.address import (
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
+ assert_raises_rpc_error,
find_vout_for_address,
)
from test_framework.script_util import (
@@ -33,6 +34,14 @@ from decimal import (
Decimal,
)
+INPUTS = [
+ # Valid pay-to-pubkey scripts
+ {'txid': '9b907ef1e3c26fc71fe4a4b3580bc75264112f95050014157059c736f0202e71', 'vout': 0,
+ 'scriptPubKey': '76a91460baa0f494b38ce3c940dea67f3804dc52d1fb9488ac'},
+ {'txid': '83a4f6a6b73660e13ee6cb3c6063fa3759c50c9b7521d0536022961898f4fb02', 'vout': 0,
+ 'scriptPubKey': '76a914669b857c03a5ed269d5d85a1ffac9ed5d663072788ac'},
+]
+OUTPUTS = {'mpLQjfK79b7CCV4VMJWEWAj5Mpx8Up5zxB': 0.1}
class SignRawTransactionWithKeyTest(BitcoinTestFramework):
def set_test_params(self):
@@ -47,6 +56,11 @@ class SignRawTransactionWithKeyTest(BitcoinTestFramework):
txid = self.nodes[0].sendrawtransaction(self.nodes[0].signrawtransactionwithkey(rawtx, [self.nodes[0].get_deterministic_priv_key().key])["hex"], 0)
return txid
+ def assert_signing_completed_successfully(self, signed_tx):
+ assert 'errors' not in signed_tx
+ assert 'complete' in signed_tx
+ assert_equal(signed_tx['complete'], True)
+
def successful_signing_test(self):
"""Create and sign a valid raw transaction with one input.
@@ -56,25 +70,10 @@ class SignRawTransactionWithKeyTest(BitcoinTestFramework):
2) No script verification error occurred"""
self.log.info("Test valid raw transaction with one input")
privKeys = ['cUeKHd5orzT3mz8P9pxyREHfsWtVfgsfDjiZZBcjUBAaGk1BTj7N', 'cVKpPfVKSJxKqVpE9awvXNWuLHCa5j5tiE7K6zbUSptFpTEtiFrA']
+ rawTx = self.nodes[0].createrawtransaction(INPUTS, OUTPUTS)
+ rawTxSigned = self.nodes[0].signrawtransactionwithkey(rawTx, privKeys, INPUTS)
- inputs = [
- # Valid pay-to-pubkey scripts
- {'txid': '9b907ef1e3c26fc71fe4a4b3580bc75264112f95050014157059c736f0202e71', 'vout': 0,
- 'scriptPubKey': '76a91460baa0f494b38ce3c940dea67f3804dc52d1fb9488ac'},
- {'txid': '83a4f6a6b73660e13ee6cb3c6063fa3759c50c9b7521d0536022961898f4fb02', 'vout': 0,
- 'scriptPubKey': '76a914669b857c03a5ed269d5d85a1ffac9ed5d663072788ac'},
- ]
-
- outputs = {'mpLQjfK79b7CCV4VMJWEWAj5Mpx8Up5zxB': 0.1}
-
- rawTx = self.nodes[0].createrawtransaction(inputs, outputs)
- rawTxSigned = self.nodes[0].signrawtransactionwithkey(rawTx, privKeys, inputs)
-
- # 1) The transaction has a complete set of signatures
- assert rawTxSigned['complete']
-
- # 2) No script verification error occurred
- assert 'errors' not in rawTxSigned
+ self.assert_signing_completed_successfully(rawTxSigned)
def witness_script_test(self):
self.log.info("Test signing transaction to P2SH-P2WSH addresses without wallet")
@@ -95,9 +94,7 @@ class SignRawTransactionWithKeyTest(BitcoinTestFramework):
# Now create and sign a transaction spending that output on node[0], which doesn't know the scripts or keys
spending_tx = self.nodes[0].createrawtransaction([unspent_output], {getnewdestination()[2]: Decimal("49.998")})
spending_tx_signed = self.nodes[0].signrawtransactionwithkey(spending_tx, [embedded_privkey], [unspent_output])
- # Check the signing completed successfully
- assert 'complete' in spending_tx_signed
- assert_equal(spending_tx_signed['complete'], True)
+ self.assert_signing_completed_successfully(spending_tx_signed)
# Now test with P2PKH and P2PK scripts as the witnessScript
for tx_type in ['P2PKH', 'P2PK']: # these tests are order-independent
@@ -120,14 +117,19 @@ class SignRawTransactionWithKeyTest(BitcoinTestFramework):
# Now create and sign a transaction spending that output on node[0], which doesn't know the scripts or keys
spending_tx = self.nodes[0].createrawtransaction([{'txid': txid, 'vout': vout}], {getnewdestination()[2]: Decimal("9.999")})
spending_tx_signed = self.nodes[0].signrawtransactionwithkey(spending_tx, [embedded_privkey], [{'txid': txid, 'vout': vout, 'scriptPubKey': script_pub_key, 'redeemScript': redeem_script, 'witnessScript': witness_script, 'amount': 10}])
- # Check the signing completed successfully
- assert 'complete' in spending_tx_signed
- assert_equal(spending_tx_signed['complete'], True)
+ self.assert_signing_completed_successfully(spending_tx_signed)
self.nodes[0].sendrawtransaction(spending_tx_signed['hex'])
+ def invalid_sighashtype_test(self):
+ self.log.info("Test signing transaction with invalid sighashtype")
+ tx = self.nodes[0].createrawtransaction(INPUTS, OUTPUTS)
+ privkeys = [self.nodes[0].get_deterministic_priv_key().key]
+ assert_raises_rpc_error(-8, "all is not a valid sighash parameter.", self.nodes[0].signrawtransactionwithkey, tx, privkeys, sighashtype="all")
+
def run_test(self):
self.successful_signing_test()
self.witness_script_test()
+ self.invalid_sighashtype_test()
if __name__ == '__main__':
diff --git a/test/functional/test_framework/script.py b/test/functional/test_framework/script.py
index 443cae86a1..78f58cf11f 100644
--- a/test/functional/test_framework/script.py
+++ b/test/functional/test_framework/script.py
@@ -689,6 +689,16 @@ def LegacySignatureHash(*args, **kwargs):
else:
return (hash256(msg), err)
+def sign_input_legacy(tx, input_index, input_scriptpubkey, privkey, sighash_type=SIGHASH_ALL):
+ """Add legacy ECDSA signature for a given transaction input. Note that the signature
+ is prepended to the scriptSig field, i.e. additional data pushes necessary for more
+ complex spends than P2PK (e.g. pubkey for P2PKH) can be already set before."""
+ (sighash, err) = LegacySignatureHash(input_scriptpubkey, tx, input_index, sighash_type)
+ assert err is None
+ der_sig = privkey.sign_ecdsa(sighash)
+ tx.vin[input_index].scriptSig = bytes(CScript([der_sig + bytes([sighash_type])])) + tx.vin[input_index].scriptSig
+ tx.rehash()
+
# TODO: Allow cached hashPrevouts/hashSequence/hashOutputs to be provided.
# Performance optimization probably not necessary for python tests, however.
# Note that this corresponds to sigversion == 1 in EvalScript, which is used
diff --git a/test/functional/test_framework/siphash.py b/test/functional/test_framework/siphash.py
index 884dbcab46..bd13b2c948 100644
--- a/test/functional/test_framework/siphash.py
+++ b/test/functional/test_framework/siphash.py
@@ -31,7 +31,7 @@ def siphash_round(v0, v1, v2, v3):
def siphash(k0, k1, data):
- assert type(data) == bytes
+ assert type(data) is bytes
v0 = 0x736f6d6570736575 ^ k0
v1 = 0x646f72616e646f6d ^ k1
v2 = 0x6c7967656e657261 ^ k0
@@ -61,5 +61,5 @@ def siphash(k0, k1, data):
def siphash256(k0, k1, num):
- assert type(num) == int
+ assert type(num) is int
return siphash(k0, k1, num.to_bytes(32, 'little'))
diff --git a/test/functional/test_framework/test_node.py b/test/functional/test_framework/test_node.py
index 5111d88e15..544a81602e 100755
--- a/test/functional/test_framework/test_node.py
+++ b/test/functional/test_framework/test_node.py
@@ -250,7 +250,7 @@ class TestNode():
# Wait for the node to finish reindex, block import, and
# loading the mempool. Usually importing happens fast or
# even "immediate" when the node is started. However, there
- # is no guarantee and sometimes ThreadImport might finish
+ # is no guarantee and sometimes ImportBlocks might finish
# later. This is going to cause intermittent test failures,
# because generally the tests assume the node is fully
# ready after being started.
@@ -353,21 +353,13 @@ class TestNode():
for profile_name in tuple(self.perf_subprocesses.keys()):
self._stop_perf(profile_name)
- # Check that stderr is as expected
- self.stderr.seek(0)
- stderr = self.stderr.read().decode('utf-8').strip()
- if stderr != expected_stderr:
- raise AssertionError("Unexpected stderr {} != {}".format(stderr, expected_stderr))
-
- self.stdout.close()
- self.stderr.close()
-
del self.p2ps[:]
+ assert (not expected_stderr) or wait_until_stopped # Must wait to check stderr
if wait_until_stopped:
- self.wait_until_stopped()
+ self.wait_until_stopped(expected_stderr=expected_stderr)
- def is_node_stopped(self, expected_ret_code=0):
+ def is_node_stopped(self, *, expected_stderr="", expected_ret_code=0):
"""Checks whether the node has stopped.
Returns True if the node has stopped. False otherwise.
@@ -381,6 +373,15 @@ class TestNode():
# process has stopped. Assert that it didn't return an error code.
assert return_code == expected_ret_code, self._node_msg(
f"Node returned unexpected exit code ({return_code}) vs ({expected_ret_code}) when stopping")
+ # Check that stderr is as expected
+ self.stderr.seek(0)
+ stderr = self.stderr.read().decode('utf-8').strip()
+ if stderr != expected_stderr:
+ raise AssertionError("Unexpected stderr {} != {}".format(stderr, expected_stderr))
+
+ self.stdout.close()
+ self.stderr.close()
+
self.running = False
self.process = None
self.rpc_connected = False
@@ -388,9 +389,9 @@ class TestNode():
self.log.debug("Node stopped")
return True
- def wait_until_stopped(self, timeout=BITCOIND_PROC_WAIT_TIMEOUT, expect_error=False):
+ def wait_until_stopped(self, *, timeout=BITCOIND_PROC_WAIT_TIMEOUT, expect_error=False, **kwargs):
expected_ret_code = 1 if expect_error else 0 # Whether node shutdown return EXIT_FAILURE or EXIT_SUCCESS
- wait_until_helper(lambda: self.is_node_stopped(expected_ret_code=expected_ret_code), timeout=timeout, timeout_factor=self.timeout_factor)
+ wait_until_helper(lambda: self.is_node_stopped(expected_ret_code=expected_ret_code, **kwargs), timeout=timeout, timeout_factor=self.timeout_factor)
def replace_in_config(self, replacements):
"""
@@ -420,11 +421,15 @@ class TestNode():
return self.chain_path / 'debug.log'
@property
+ def blocks_path(self) -> Path:
+ return self.chain_path / "blocks"
+
+ @property
def wallets_path(self) -> Path:
return self.chain_path / "wallets"
- def debug_log_bytes(self) -> int:
- with open(self.debug_log_path, encoding='utf-8') as dl:
+ def debug_log_size(self, **kwargs) -> int:
+ with open(self.debug_log_path, **kwargs) as dl:
dl.seek(0, 2)
return dl.tell()
@@ -433,13 +438,13 @@ class TestNode():
if unexpected_msgs is None:
unexpected_msgs = []
time_end = time.time() + timeout * self.timeout_factor
- prev_size = self.debug_log_bytes()
+ prev_size = self.debug_log_size(encoding="utf-8") # Must use same encoding that is used to read() below
yield
while True:
found = True
- with open(self.debug_log_path, encoding='utf-8') as dl:
+ with open(self.debug_log_path, encoding="utf-8", errors="replace") as dl:
dl.seek(prev_size)
log = dl.read()
print_log = " - " + "\n - ".join(log.splitlines())
@@ -464,7 +469,7 @@ class TestNode():
the number of log lines we encountered when matching
"""
time_end = time.time() + timeout * self.timeout_factor
- prev_size = self.debug_log_bytes()
+ prev_size = self.debug_log_size(mode="rb") # Must use same mode that is used to read() below
yield
@@ -643,10 +648,14 @@ class TestNode():
# in comparison to the upside of making tests less fragile and unexpected intermittent errors less likely.
p2p_conn.sync_with_ping()
- # Consistency check that the Bitcoin Core has received our user agent string. This checks the
- # node's newest peer. It could be racy if another Bitcoin Core node has connected since we opened
- # our connection, but we don't expect that to happen.
- assert_equal(self.getpeerinfo()[-1]['subver'], P2P_SUBVERSION)
+ # Consistency check that the node received our user agent string.
+ # Find our connection in getpeerinfo by our address:port and theirs, as this combination is unique.
+ sockname = p2p_conn._transport.get_extra_info("socket").getsockname()
+ our_addr_and_port = f"{sockname[0]}:{sockname[1]}"
+ dst_addr_and_port = f"{p2p_conn.dstaddr}:{p2p_conn.dstport}"
+ info = [peer for peer in self.getpeerinfo() if peer["addr"] == our_addr_and_port and peer["addrbind"] == dst_addr_and_port]
+ assert_equal(len(info), 1)
+ assert_equal(info[0]["subver"], P2P_SUBVERSION)
return p2p_conn
diff --git a/test/functional/test_framework/wallet.py b/test/functional/test_framework/wallet.py
index 271095ea21..4d75194353 100644
--- a/test/functional/test_framework/wallet.py
+++ b/test/functional/test_framework/wallet.py
@@ -36,12 +36,11 @@ from test_framework.messages import (
)
from test_framework.script import (
CScript,
- LegacySignatureHash,
LEAF_VERSION_TAPSCRIPT,
OP_NOP,
OP_RETURN,
OP_TRUE,
- SIGHASH_ALL,
+ sign_input_legacy,
taproot_construct,
)
from test_framework.script_util import (
@@ -166,18 +165,16 @@ class MiniWallet:
def sign_tx(self, tx, fixed_length=True):
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)
+ # with the DER header/skeleton data of 6 bytes added, plus 2 bytes scriptSig overhead
+ # (OP_PUSHn and SIGHASH_ALL), this leads to a scriptSig target size of 73 bytes
+ tx.vin[0].scriptSig = b''
+ while not len(tx.vin[0].scriptSig) == 73:
+ tx.vin[0].scriptSig = b''
+ sign_input_legacy(tx, 0, self._scriptPubKey, self._priv_key)
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
diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py
index a04ea4d978..d93e6fd6da 100755
--- a/test/functional/test_runner.py
+++ b/test/functional/test_runner.py
@@ -784,8 +784,8 @@ def check_script_prefixes():
def check_script_list(*, src_dir, fail_on_warn):
"""Check scripts directory.
- Check that there are no scripts in the functional tests directory which are
- not being run by pull-tester.py."""
+ Check that all python files in this directory are categorized
+ as a test script or meta script."""
script_dir = src_dir + '/test/functional/'
python_files = set([test_file for test_file in os.listdir(script_dir) if test_file.endswith(".py")])
missed_tests = list(python_files - set(map(lambda x: x.split()[0], ALL_SCRIPTS + NON_SCRIPTS)))
diff --git a/test/functional/tool_wallet.py b/test/functional/tool_wallet.py
index 327dd43e5a..9d381a2cd2 100755
--- a/test/functional/tool_wallet.py
+++ b/test/functional/tool_wallet.py
@@ -4,7 +4,6 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test bitcoin-wallet."""
-import hashlib
import os
import stat
import subprocess
@@ -13,9 +12,10 @@ import textwrap
from collections import OrderedDict
from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import assert_equal
-
-BUFFER_SIZE = 16 * 1024
+from test_framework.util import (
+ assert_equal,
+ sha256sum_file,
+)
class ToolWalletTest(BitcoinTestFramework):
@@ -53,12 +53,7 @@ class ToolWalletTest(BitcoinTestFramework):
assert_equal(p.poll(), 0)
def wallet_shasum(self):
- h = hashlib.sha1()
- mv = memoryview(bytearray(BUFFER_SIZE))
- with open(self.wallet_path, 'rb', buffering=0) as f:
- for n in iter(lambda: f.readinto(mv), 0):
- h.update(mv[:n])
- return h.hexdigest()
+ return sha256sum_file(self.wallet_path).hex()
def wallet_timestamp(self):
return os.path.getmtime(self.wallet_path)
diff --git a/test/functional/wallet_backwards_compatibility.py b/test/functional/wallet_backwards_compatibility.py
index 7d88e009c7..49e36b21c5 100755
--- a/test/functional/wallet_backwards_compatibility.py
+++ b/test/functional/wallet_backwards_compatibility.py
@@ -264,10 +264,11 @@ class BackwardsCompatibilityTest(BitcoinTestFramework):
os.path.join(node_master_wallets_dir, "u1_v16")
)
load_res = node_master.loadwallet("u1_v16")
- # Make sure this wallet opens without warnings. See https://github.com/bitcoin/bitcoin/pull/19054
+ # Make sure this wallet opens with only the migration warning. See https://github.com/bitcoin/bitcoin/pull/19054
if int(node_master.getnetworkinfo()["version"]) >= 249900:
# loadwallet#warnings (added in v25) -- only present if there is a warning
- assert "warnings" not in load_res
+ # Legacy wallets will have only a deprecation warning
+ assert_equal(load_res["warnings"], ["Wallet loaded successfully. The legacy wallet type is being deprecated and support for creating and opening legacy wallets will be removed in the future. Legacy wallets can be migrated to a descriptor wallet with migratewallet."])
else:
# loadwallet#warning (deprecated in v25) -- always present, but empty string if no warning
assert_equal(load_res["warning"], '')
diff --git a/test/functional/wallet_basic.py b/test/functional/wallet_basic.py
index a1b805c09e..a4eb6ee29a 100755
--- a/test/functional/wallet_basic.py
+++ b/test/functional/wallet_basic.py
@@ -328,7 +328,7 @@ class WalletTest(BitcoinTestFramework):
for invalid_value in ["", 0.000000001, 1e-09, 1.111111111, 1111111111111111, "31.999999999999999999999"]:
assert_raises_rpc_error(-3, msg, self.nodes[2].sendmany, amounts={address: 1.0}, fee_rate=invalid_value)
# Test fee_rate values that cannot be represented in sat/vB.
- for invalid_value in [0.0001, 0.00000001, 0.00099999, 31.99999999, "0.0001", "0.00000001", "0.00099999", "31.99999999"]:
+ for invalid_value in [0.0001, 0.00000001, 0.00099999, 31.99999999]:
assert_raises_rpc_error(-3, msg, self.nodes[2].sendmany, amounts={address: 10}, fee_rate=invalid_value)
# Test fee_rate out of range (negative number).
assert_raises_rpc_error(-3, OUT_OF_RANGE, self.nodes[2].sendmany, amounts={address: 10}, fee_rate=-1)
@@ -523,7 +523,7 @@ class WalletTest(BitcoinTestFramework):
for invalid_value in ["", 0.000000001, 1e-09, 1.111111111, 1111111111111111, "31.999999999999999999999"]:
assert_raises_rpc_error(-3, msg, self.nodes[2].sendtoaddress, address=address, amount=1.0, fee_rate=invalid_value)
# Test fee_rate values that cannot be represented in sat/vB.
- for invalid_value in [0.0001, 0.00000001, 0.00099999, 31.99999999, "0.0001", "0.00000001", "0.00099999", "31.99999999"]:
+ for invalid_value in [0.0001, 0.00000001, 0.00099999, 31.99999999]:
assert_raises_rpc_error(-3, msg, self.nodes[2].sendtoaddress, address=address, amount=10, fee_rate=invalid_value)
# Test fee_rate out of range (negative number).
assert_raises_rpc_error(-3, OUT_OF_RANGE, self.nodes[2].sendtoaddress, address=address, amount=1.0, fee_rate=-1)
diff --git a/test/functional/wallet_bumpfee.py b/test/functional/wallet_bumpfee.py
index b9ebf64c22..e69c1829ca 100755
--- a/test/functional/wallet_bumpfee.py
+++ b/test/functional/wallet_bumpfee.py
@@ -24,9 +24,11 @@ from test_framework.messages import (
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
+ assert_fee_amount,
assert_greater_than,
assert_raises_rpc_error,
get_fee,
+ find_vout_for_address,
)
from test_framework.wallet import MiniWallet
@@ -109,6 +111,8 @@ class BumpFeeTest(BitcoinTestFramework):
test_small_output_with_feerate_succeeds(self, rbf_node, dest_address)
test_no_more_inputs_fails(self, rbf_node, dest_address)
self.test_bump_back_to_yourself()
+ self.test_provided_change_pos(rbf_node)
+ self.test_single_output()
# Context independent tests
test_feerate_checks_replaced_outputs(self, rbf_node, peer_node)
@@ -137,7 +141,7 @@ class BumpFeeTest(BitcoinTestFramework):
for invalid_value in ["", 0.000000001, 1e-09, 1.111111111, 1111111111111111, "31.999999999999999999999"]:
assert_raises_rpc_error(-3, msg, rbf_node.bumpfee, rbfid, fee_rate=invalid_value)
# Test fee_rate values that cannot be represented in sat/vB.
- for invalid_value in [0.0001, 0.00000001, 0.00099999, 31.99999999, "0.0001", "0.00000001", "0.00099999", "31.99999999"]:
+ for invalid_value in [0.0001, 0.00000001, 0.00099999, 31.99999999]:
assert_raises_rpc_error(-3, msg, rbf_node.bumpfee, rbfid, fee_rate=invalid_value)
# Test fee_rate out of range (negative number).
assert_raises_rpc_error(-3, "Amount out of range", rbf_node.bumpfee, rbfid, fee_rate=-1)
@@ -174,6 +178,13 @@ class BumpFeeTest(BitcoinTestFramework):
assert_raises_rpc_error(-8, "Invalid parameter, duplicate key: data",
rbf_node.bumpfee, rbfid, {"outputs": [{"data": "deadbeef"}, {"data": "deadbeef"}]})
+ self.log.info("Test reduce_output option")
+ assert_raises_rpc_error(-1, "JSON integer out of range", rbf_node.bumpfee, rbfid, {"reduce_output": -1})
+ assert_raises_rpc_error(-8, "Change position is out of range", rbf_node.bumpfee, rbfid, {"reduce_output": 2})
+
+ self.log.info("Test outputs and reduce_output cannot both be provided")
+ assert_raises_rpc_error(-8, "Cannot specify both new outputs to use and an output index to reduce", rbf_node.bumpfee, rbfid, {"reduce_output": 2, "outputs": [{dest_address: 0.1}]})
+
self.clear_mempool()
def test_bump_back_to_yourself(self):
@@ -225,6 +236,72 @@ class BumpFeeTest(BitcoinTestFramework):
node.unloadwallet("back_to_yourself")
+ def test_provided_change_pos(self, rbf_node):
+ self.log.info("Test the reduce_output option")
+
+ change_addr = rbf_node.getnewaddress()
+ dest_addr = rbf_node.getnewaddress()
+ assert_equal(rbf_node.getaddressinfo(change_addr)["ischange"], False)
+ assert_equal(rbf_node.getaddressinfo(dest_addr)["ischange"], False)
+
+ send_res = rbf_node.send(outputs=[{dest_addr: 1}], options={"change_address": change_addr})
+ assert send_res["complete"]
+ txid = send_res["txid"]
+
+ tx = rbf_node.gettransaction(txid=txid, verbose=True)
+ assert_equal(len(tx["decoded"]["vout"]), 2)
+
+ change_pos = find_vout_for_address(rbf_node, txid, change_addr)
+ change_value = tx["decoded"]["vout"][change_pos]["value"]
+
+ bumped = rbf_node.bumpfee(txid, {"reduce_output": change_pos})
+ new_txid = bumped["txid"]
+
+ new_tx = rbf_node.gettransaction(txid=new_txid, verbose=True)
+ assert_equal(len(new_tx["decoded"]["vout"]), 2)
+ new_change_pos = find_vout_for_address(rbf_node, new_txid, change_addr)
+ new_change_value = new_tx["decoded"]["vout"][new_change_pos]["value"]
+
+ assert_greater_than(change_value, new_change_value)
+
+
+ def test_single_output(self):
+ self.log.info("Test that single output txs can be bumped")
+ node = self.nodes[1]
+
+ node.createwallet("single_out_rbf")
+ wallet = node.get_wallet_rpc("single_out_rbf")
+
+ addr = wallet.getnewaddress()
+ amount = Decimal("0.001")
+ # Make 2 UTXOs
+ self.nodes[0].sendtoaddress(addr, amount)
+ self.nodes[0].sendtoaddress(addr, amount)
+ self.generate(self.nodes[0], 1)
+ utxos = wallet.listunspent()
+
+ tx = wallet.sendall(recipients=[wallet.getnewaddress()], fee_rate=2, options={"inputs": [utxos[0]]})
+
+ # Reduce the only output with a crazy high feerate, should fail as the output would be dust
+ assert_raises_rpc_error(-4, "The transaction amount is too small to pay the fee", wallet.bumpfee, txid=tx["txid"], options={"fee_rate": 1100, "reduce_output": 0})
+
+ # Reduce the only output successfully
+ bumped = wallet.bumpfee(txid=tx["txid"], options={"fee_rate": 10, "reduce_output": 0})
+ bumped_tx = wallet.gettransaction(txid=bumped["txid"], verbose=True)
+ assert_equal(len(bumped_tx["decoded"]["vout"]), 1)
+ assert_equal(len(bumped_tx["decoded"]["vin"]), 1)
+ assert_equal(bumped_tx["decoded"]["vout"][0]["value"] + bumped["fee"], amount)
+ assert_fee_amount(bumped["fee"], bumped_tx["decoded"]["vsize"], Decimal(10) / Decimal(1e8) * 1000)
+
+ # Bumping without reducing adds a new input and output
+ bumped = wallet.bumpfee(txid=bumped["txid"], options={"fee_rate": 20})
+ bumped_tx = wallet.gettransaction(txid=bumped["txid"], verbose=True)
+ assert_equal(len(bumped_tx["decoded"]["vout"]), 2)
+ assert_equal(len(bumped_tx["decoded"]["vin"]), 2)
+ assert_fee_amount(bumped["fee"], bumped_tx["decoded"]["vsize"], Decimal(20) / Decimal(1e8) * 1000)
+
+ wallet.unloadwallet()
+
def test_simple_bumpfee_succeeds(self, mode, rbf_node, peer_node, dest_address):
self.log.info('Test simple bumpfee: {}'.format(mode))
rbfid = spend_one_input(rbf_node, dest_address)
diff --git a/test/functional/wallet_fundrawtransaction.py b/test/functional/wallet_fundrawtransaction.py
index 46706d6ad2..e367daae2c 100755
--- a/test/functional/wallet_fundrawtransaction.py
+++ b/test/functional/wallet_fundrawtransaction.py
@@ -183,7 +183,6 @@ class RawTransactionsTest(BitcoinTestFramework):
inputs = [ ]
outputs = { self.nodes[0].getnewaddress() : 1.0 }
rawtx = self.nodes[2].createrawtransaction(inputs, outputs)
- dec_tx = self.nodes[2].decoderawtransaction(rawtx)
rawtxfund = self.nodes[2].fundrawtransaction(rawtx)
dec_tx = self.nodes[2].decoderawtransaction(rawtxfund['hex'])
assert len(dec_tx['vin']) > 0 #test that we have enough inputs
@@ -193,8 +192,6 @@ class RawTransactionsTest(BitcoinTestFramework):
inputs = [ ]
outputs = { self.nodes[0].getnewaddress() : 2.2 }
rawtx = self.nodes[2].createrawtransaction(inputs, outputs)
- dec_tx = self.nodes[2].decoderawtransaction(rawtx)
-
rawtxfund = self.nodes[2].fundrawtransaction(rawtx)
dec_tx = self.nodes[2].decoderawtransaction(rawtxfund['hex'])
assert len(dec_tx['vin']) > 0 #test if we have enough inputs
@@ -206,13 +203,9 @@ class RawTransactionsTest(BitcoinTestFramework):
inputs = [ ]
outputs = { self.nodes[0].getnewaddress() : 2.6, self.nodes[1].getnewaddress() : 2.5 }
rawtx = self.nodes[2].createrawtransaction(inputs, outputs)
- dec_tx = self.nodes[2].decoderawtransaction(rawtx)
rawtxfund = self.nodes[2].fundrawtransaction(rawtx)
dec_tx = self.nodes[2].decoderawtransaction(rawtxfund['hex'])
- totalOut = 0
- for out in dec_tx['vout']:
- totalOut += out['value']
assert len(dec_tx['vin']) > 0
assert_equal(dec_tx['vin'][0]['scriptSig']['hex'], '')
@@ -335,10 +328,8 @@ class RawTransactionsTest(BitcoinTestFramework):
rawtxfund = self.nodes[2].fundrawtransaction(rawtx)
dec_tx = self.nodes[2].decoderawtransaction(rawtxfund['hex'])
- totalOut = 0
matchingOuts = 0
for i, out in enumerate(dec_tx['vout']):
- totalOut += out['value']
if out['scriptPubKey']['address'] in outputs:
matchingOuts+=1
else:
@@ -364,12 +355,9 @@ class RawTransactionsTest(BitcoinTestFramework):
# Should fail without add_inputs:
assert_raises_rpc_error(-4, ERR_NOT_ENOUGH_PRESET_INPUTS, self.nodes[2].fundrawtransaction, rawtx, add_inputs=False)
rawtxfund = self.nodes[2].fundrawtransaction(rawtx, add_inputs=True)
-
dec_tx = self.nodes[2].decoderawtransaction(rawtxfund['hex'])
- totalOut = 0
matchingOuts = 0
for out in dec_tx['vout']:
- totalOut += out['value']
if out['scriptPubKey']['address'] in outputs:
matchingOuts+=1
@@ -400,10 +388,8 @@ class RawTransactionsTest(BitcoinTestFramework):
rawtxfund = self.nodes[2].fundrawtransaction(rawtx, add_inputs=True)
dec_tx = self.nodes[2].decoderawtransaction(rawtxfund['hex'])
- totalOut = 0
matchingOuts = 0
for out in dec_tx['vout']:
- totalOut += out['value']
if out['scriptPubKey']['address'] in outputs:
matchingOuts+=1
@@ -581,11 +567,20 @@ class RawTransactionsTest(BitcoinTestFramework):
def test_locked_wallet(self):
self.log.info("Test fundrawtxn with locked wallet and hardened derivation")
- self.nodes[1].encryptwallet("test")
+ df_wallet = self.nodes[1].get_wallet_rpc(self.default_wallet_name)
+ self.nodes[1].createwallet(wallet_name="locked_wallet", descriptors=self.options.descriptors)
+ wallet = self.nodes[1].get_wallet_rpc("locked_wallet")
+
+ # Add some balance to the wallet (this will be reverted at the end of the test)
+ df_wallet.sendall(recipients=[wallet.getnewaddress()])
+ self.generate(self.nodes[1], 1)
+
+ # Encrypt wallet and import descriptors
+ wallet.encryptwallet("test")
if self.options.descriptors:
- self.nodes[1].walletpassphrase('test', 10)
- self.nodes[1].importdescriptors([{
+ wallet.walletpassphrase('test', 10)
+ wallet.importdescriptors([{
'desc': descsum_create('wpkh(tprv8ZgxMBicQKsPdYeeZbPSKd2KYLmeVKtcFA7kqCxDvDR13MQ6us8HopUR2wLcS2ZKPhLyKsqpDL2FtL73LMHcgoCL7DXsciA8eX8nbjCR2eG/0h/*h)'),
'timestamp': 'now',
'active': True
@@ -596,49 +591,57 @@ class RawTransactionsTest(BitcoinTestFramework):
'active': True,
'internal': True
}])
- self.nodes[1].walletlock()
+ wallet.walletlock()
# Drain the keypool.
- self.nodes[1].getnewaddress()
- self.nodes[1].getrawchangeaddress()
+ wallet.getnewaddress()
+ wallet.getrawchangeaddress()
- # Choose 2 inputs
- inputs = self.nodes[1].listunspent()[0:2]
- value = sum(inp["amount"] for inp in inputs) - Decimal("0.00000500") # Pay a 500 sat fee
+ # Choose input
+ inputs = wallet.listunspent()
+ # Deduce fee to produce a changeless transaction
+ value = inputs[0]["amount"] - Decimal("0.00002200")
outputs = {self.nodes[0].getnewaddress():value}
- rawtx = self.nodes[1].createrawtransaction(inputs, outputs)
+ rawtx = wallet.createrawtransaction(inputs, outputs)
# fund a transaction that does not require a new key for the change output
- self.nodes[1].fundrawtransaction(rawtx)
+ funded_tx = wallet.fundrawtransaction(rawtx)
+ assert_equal(funded_tx["changepos"], -1)
# fund a transaction that requires a new key for the change output
# creating the key must be impossible because the wallet is locked
outputs = {self.nodes[0].getnewaddress():value - Decimal("0.1")}
- rawtx = self.nodes[1].createrawtransaction(inputs, outputs)
- assert_raises_rpc_error(-4, "Transaction needs a change address, but we can't generate it.", self.nodes[1].fundrawtransaction, rawtx)
+ rawtx = wallet.createrawtransaction(inputs, outputs)
+ assert_raises_rpc_error(-4, "Transaction needs a change address, but we can't generate it.", wallet.fundrawtransaction, rawtx)
# Refill the keypool.
- self.nodes[1].walletpassphrase("test", 100)
- self.nodes[1].keypoolrefill(8) #need to refill the keypool to get an internal change address
- self.nodes[1].walletlock()
+ wallet.walletpassphrase("test", 100)
+ wallet.keypoolrefill(8) #need to refill the keypool to get an internal change address
+ wallet.walletlock()
- assert_raises_rpc_error(-13, "walletpassphrase", self.nodes[1].sendtoaddress, self.nodes[0].getnewaddress(), 1.2)
+ assert_raises_rpc_error(-13, "walletpassphrase", wallet.sendtoaddress, self.nodes[0].getnewaddress(), 1.2)
oldBalance = self.nodes[0].getbalance()
inputs = []
outputs = {self.nodes[0].getnewaddress():1.1}
- rawtx = self.nodes[1].createrawtransaction(inputs, outputs)
- fundedTx = self.nodes[1].fundrawtransaction(rawtx)
+ rawtx = wallet.createrawtransaction(inputs, outputs)
+ fundedTx = wallet.fundrawtransaction(rawtx)
+ assert fundedTx["changepos"] != -1
# Now we need to unlock.
- self.nodes[1].walletpassphrase("test", 600)
- signedTx = self.nodes[1].signrawtransactionwithwallet(fundedTx['hex'])
- self.nodes[1].sendrawtransaction(signedTx['hex'])
+ wallet.walletpassphrase("test", 600)
+ signedTx = wallet.signrawtransactionwithwallet(fundedTx['hex'])
+ wallet.sendrawtransaction(signedTx['hex'])
self.generate(self.nodes[1], 1)
# Make sure funds are received at node1.
assert_equal(oldBalance+Decimal('51.10000000'), self.nodes[0].getbalance())
+ # Restore pre-test wallet state
+ wallet.sendall(recipients=[df_wallet.getnewaddress(), df_wallet.getnewaddress(), df_wallet.getnewaddress()])
+ wallet.unloadwallet()
+ self.generate(self.nodes[1], 1)
+
def test_many_inputs_fee(self):
"""Multiple (~19) inputs tx test | Compare fee."""
self.log.info("Test fundrawtxn fee with many inputs")
@@ -829,7 +832,7 @@ class RawTransactionsTest(BitcoinTestFramework):
for invalid_value in ["", 0.000000001, 1e-09, 1.111111111, 1111111111111111, "31.999999999999999999999"]:
assert_raises_rpc_error(-3, "Invalid amount", node.fundrawtransaction, rawtx, add_inputs=True, **{param: invalid_value})
# Test fee_rate values that cannot be represented in sat/vB.
- for invalid_value in [0.0001, 0.00000001, 0.00099999, 31.99999999, "0.0001", "0.00000001", "0.00099999", "31.99999999"]:
+ for invalid_value in [0.0001, 0.00000001, 0.00099999, 31.99999999]:
assert_raises_rpc_error(-3, "Invalid amount",
node.fundrawtransaction, rawtx, fee_rate=invalid_value, add_inputs=True)
diff --git a/test/functional/wallet_migration.py b/test/functional/wallet_migration.py
index b64e5a8cd8..925376e8cd 100755
--- a/test/functional/wallet_migration.py
+++ b/test/functional/wallet_migration.py
@@ -8,6 +8,8 @@ import random
import shutil
from test_framework.descriptors import descsum_create
from test_framework.test_framework import BitcoinTestFramework
+from test_framework.messages import COIN, CTransaction, CTxOut
+from test_framework.script_util import key_to_p2pkh_script, script_to_p2sh_script, script_to_p2wsh_script
from test_framework.util import (
assert_equal,
assert_raises_rpc_error,
@@ -67,6 +69,15 @@ class WalletMigrationTest(BitcoinTestFramework):
del d["parent_descs"]
assert_equal(received_list_txs, expected_list_txs)
+ def check_address(self, wallet, addr, is_mine, is_change, label):
+ addr_info = wallet.getaddressinfo(addr)
+ assert_equal(addr_info['ismine'], is_mine)
+ assert_equal(addr_info['ischange'], is_change)
+ if label is not None:
+ assert_equal(addr_info['labels'], [label]),
+ else:
+ assert_equal(addr_info['labels'], []),
+
def test_basic(self):
default = self.nodes[0].get_wallet_rpc(self.default_wallet_name)
@@ -504,6 +515,179 @@ class WalletMigrationTest(BitcoinTestFramework):
assert wallet_path.is_dir()
assert wallet_dat_path.is_file()
+ def test_addressbook(self):
+ df_wallet = self.nodes[0].get_wallet_rpc(self.default_wallet_name)
+
+ self.log.info("Test migration of address book data")
+ wallet = self.create_legacy_wallet("legacy_addrbook")
+ df_wallet.sendtoaddress(wallet.getnewaddress(), 3)
+
+ # Import watch-only script to create a watch-only wallet after migration
+ watch_addr = df_wallet.getnewaddress()
+ wallet.importaddress(watch_addr)
+ df_wallet.sendtoaddress(watch_addr, 2)
+
+ # Import solvable script
+ multi_addr1 = wallet.getnewaddress()
+ multi_addr2 = wallet.getnewaddress()
+ multi_addr3 = df_wallet.getnewaddress()
+ wallet.importpubkey(df_wallet.getaddressinfo(multi_addr3)["pubkey"])
+ ms_addr_info = wallet.addmultisigaddress(2, [multi_addr1, multi_addr2, multi_addr3])
+
+ self.generate(self.nodes[0], 1)
+
+ # Test vectors
+ addr_external = {
+ "addr": df_wallet.getnewaddress(),
+ "is_mine": False,
+ "is_change": False,
+ "label": ""
+ }
+ addr_external_with_label = {
+ "addr": df_wallet.getnewaddress(),
+ "is_mine": False,
+ "is_change": False,
+ "label": "external"
+ }
+ addr_internal = {
+ "addr": wallet.getnewaddress(),
+ "is_mine": True,
+ "is_change": False,
+ "label": ""
+ }
+ addr_internal_with_label = {
+ "addr": wallet.getnewaddress(),
+ "is_mine": True,
+ "is_change": False,
+ "label": "internal"
+ }
+ change_address = {
+ "addr": wallet.getrawchangeaddress(),
+ "is_mine": True,
+ "is_change": True,
+ "label": None
+ }
+ watch_only_addr = {
+ "addr": watch_addr,
+ "is_mine": False,
+ "is_change": False,
+ "label": "imported"
+ }
+ ms_addr = {
+ "addr": ms_addr_info['address'],
+ "is_mine": False,
+ "is_change": False,
+ "label": "multisig"
+ }
+
+ # To store the change address in the addressbook need to send coins to it
+ wallet.send(outputs=[{wallet.getnewaddress(): 2}], options={"change_address": change_address['addr']})
+ self.generate(self.nodes[0], 1)
+
+ # Util wrapper func for 'addr_info'
+ def check(info, node):
+ self.check_address(node, info['addr'], info['is_mine'], info['is_change'], info["label"])
+
+ # Pre-migration: set label and perform initial checks
+ for addr_info in [addr_external, addr_external_with_label, addr_internal, addr_internal_with_label, change_address, watch_only_addr, ms_addr]:
+ if not addr_info['is_change']:
+ wallet.setlabel(addr_info['addr'], addr_info["label"])
+ check(addr_info, wallet)
+
+ # Migrate wallet
+ info_migration = wallet.migratewallet()
+ wallet_wo = self.nodes[0].get_wallet_rpc(info_migration["watchonly_name"])
+ wallet_solvables = self.nodes[0].get_wallet_rpc(info_migration["solvables_name"])
+
+ #########################
+ # Post migration checks #
+ #########################
+
+ # First check the main wallet
+ for addr_info in [addr_external, addr_external_with_label, addr_internal, addr_internal_with_label, change_address, ms_addr]:
+ check(addr_info, wallet)
+
+ # Watch-only wallet will contain the watch-only entry (with 'is_mine=True') and all external addresses ('send')
+ self.check_address(wallet_wo, watch_only_addr['addr'], is_mine=True, is_change=watch_only_addr['is_change'], label=watch_only_addr["label"])
+ for addr_info in [addr_external, addr_external_with_label, ms_addr]:
+ check(addr_info, wallet_wo)
+
+ # Solvables wallet will contain the multisig entry (with 'is_mine=True') and all external addresses ('send')
+ self.check_address(wallet_solvables, ms_addr['addr'], is_mine=True, is_change=ms_addr['is_change'], label=ms_addr["label"])
+ for addr_info in [addr_external, addr_external_with_label]:
+ check(addr_info, wallet_solvables)
+
+ ########################################################################################
+ # Now restart migrated wallets and verify that the addressbook entries are still there #
+ ########################################################################################
+
+ # First the main wallet
+ self.nodes[0].unloadwallet("legacy_addrbook")
+ self.nodes[0].loadwallet("legacy_addrbook")
+ for addr_info in [addr_external, addr_external_with_label, addr_internal, addr_internal_with_label, change_address, ms_addr]:
+ check(addr_info, wallet)
+
+ # Watch-only wallet
+ self.nodes[0].unloadwallet(info_migration["watchonly_name"])
+ self.nodes[0].loadwallet(info_migration["watchonly_name"])
+ self.check_address(wallet_wo, watch_only_addr['addr'], is_mine=True, is_change=watch_only_addr['is_change'], label=watch_only_addr["label"])
+ for addr_info in [addr_external, addr_external_with_label, ms_addr]:
+ check(addr_info, wallet_wo)
+
+ # Solvables wallet
+ self.nodes[0].unloadwallet(info_migration["solvables_name"])
+ self.nodes[0].loadwallet(info_migration["solvables_name"])
+ self.check_address(wallet_solvables, ms_addr['addr'], is_mine=True, is_change=ms_addr['is_change'], label=ms_addr["label"])
+ for addr_info in [addr_external, addr_external_with_label]:
+ check(addr_info, wallet_solvables)
+
+ def test_migrate_raw_p2sh(self):
+ self.log.info("Test migration of watch-only raw p2sh script")
+ df_wallet = self.nodes[0].get_wallet_rpc(self.default_wallet_name)
+ wallet = self.create_legacy_wallet("raw_p2sh")
+
+ def send_to_script(script, amount):
+ tx = CTransaction()
+ tx.vout.append(CTxOut(nValue=amount*COIN, scriptPubKey=script))
+
+ hex_tx = df_wallet.fundrawtransaction(tx.serialize().hex())['hex']
+ signed_tx = df_wallet.signrawtransactionwithwallet(hex_tx)
+ df_wallet.sendrawtransaction(signed_tx['hex'])
+ self.generate(self.nodes[0], 1)
+
+ # Craft sh(pkh(key)) script and send coins to it
+ pubkey = df_wallet.getaddressinfo(df_wallet.getnewaddress())["pubkey"]
+ script_pkh = key_to_p2pkh_script(pubkey)
+ script_sh_pkh = script_to_p2sh_script(script_pkh)
+ send_to_script(script=script_sh_pkh, amount=2)
+
+ # Import script and check balance
+ wallet.rpc.importaddress(address=script_pkh.hex(), label="raw_spk", rescan=True, p2sh=True)
+ assert_equal(wallet.getbalances()['watchonly']['trusted'], 2)
+
+ # Craft wsh(pkh(key)) and send coins to it
+ pubkey = df_wallet.getaddressinfo(df_wallet.getnewaddress())["pubkey"]
+ script_wsh_pkh = script_to_p2wsh_script(key_to_p2pkh_script(pubkey))
+ send_to_script(script=script_wsh_pkh, amount=3)
+
+ # Import script and check balance
+ wallet.rpc.importaddress(address=script_wsh_pkh.hex(), label="raw_spk2", rescan=True, p2sh=False)
+ assert_equal(wallet.getbalances()['watchonly']['trusted'], 5)
+
+ # Migrate wallet and re-check balance
+ info_migration = wallet.migratewallet()
+ wallet_wo = self.nodes[0].get_wallet_rpc(info_migration["watchonly_name"])
+
+ # Watch-only balance is under "mine".
+ assert_equal(wallet_wo.getbalances()['mine']['trusted'], 5)
+ # The watch-only scripts are no longer part of the main wallet
+ assert_equal(wallet.getbalances()['mine']['trusted'], 0)
+
+ # Just in case, also verify wallet restart
+ self.nodes[0].unloadwallet(info_migration["watchonly_name"])
+ self.nodes[0].loadwallet(info_migration["watchonly_name"])
+ assert_equal(wallet_wo.getbalances()['mine']['trusted'], 5)
+
def run_test(self):
self.generate(self.nodes[0], 101)
@@ -518,6 +702,8 @@ class WalletMigrationTest(BitcoinTestFramework):
self.test_unloaded_by_path()
self.test_default_wallet()
self.test_direct_file()
+ self.test_addressbook()
+ self.test_migrate_raw_p2sh()
if __name__ == '__main__':
WalletMigrationTest().main()
diff --git a/test/functional/wallet_miniscript.py b/test/functional/wallet_miniscript.py
index 7bc3424bf4..45f0df1c76 100755
--- a/test/functional/wallet_miniscript.py
+++ b/test/functional/wallet_miniscript.py
@@ -277,6 +277,18 @@ class WalletMiniscriptTest(BitcoinTestFramework):
assert not res["success"]
assert "is not sane: witnesses without signature exist" in res["error"]["message"]
+ # Sanity check we wouldn't let an unspendable Miniscript descriptor in
+ res = self.ms_wo_wallet.importdescriptors(
+ [
+ {
+ "desc": descsum_create("wsh(0)"),
+ "active": False,
+ "timestamp": "now",
+ }
+ ]
+ )[0]
+ assert not res["success"] and "is not satisfiable" in res["error"]["message"]
+
# Test we can track any type of Miniscript
for ms in MINISCRIPTS:
self.watchonly_test(ms)
diff --git a/test/functional/wallet_resendwallettransactions.py b/test/functional/wallet_resendwallettransactions.py
index 7bdb6f5e3a..f36d8efda7 100755
--- a/test/functional/wallet_resendwallettransactions.py
+++ b/test/functional/wallet_resendwallettransactions.py
@@ -108,9 +108,13 @@ class ResendWalletTransactionsTest(BitcoinTestFramework):
# Set correct m_best_block_time, which is used in ResubmitWalletTransactions
node.syncwithvalidationinterfacequeue()
- # Evict these txs from the mempool
evict_time = block_time + 60 * 60 * DEFAULT_MEMPOOL_EXPIRY_HOURS + 5
- node.setmocktime(evict_time)
+ # Flush out currently scheduled resubmit attempt now so that there can't be one right between eviction and check.
+ with node.assert_debug_log(['resubmit 2 unconfirmed transactions']):
+ node.setmocktime(evict_time)
+ node.mockscheduler(60)
+
+ # Evict these txs from the mempool
indep_send = node.send(outputs=[{node.getnewaddress(): 1}], inputs=[indep_utxo])
node.getmempoolentry(indep_send["txid"])
assert_raises_rpc_error(-5, "Transaction not in mempool", node.getmempoolentry, txid)
diff --git a/test/functional/wallet_send.py b/test/functional/wallet_send.py
index d7bb6ab1e7..4728f53be7 100755
--- a/test/functional/wallet_send.py
+++ b/test/functional/wallet_send.py
@@ -387,7 +387,7 @@ class WalletSendTest(BitcoinTestFramework):
self.test_send(from_wallet=w0, to_wallet=w1, amount=1, fee_rate=invalid_value, expect_error=(-3, msg))
self.test_send(from_wallet=w0, to_wallet=w1, amount=1, arg_fee_rate=invalid_value, expect_error=(-3, msg))
# Test fee_rate values that cannot be represented in sat/vB.
- for invalid_value in [0.0001, 0.00000001, 0.00099999, 31.99999999, "0.0001", "0.00000001", "0.00099999", "31.99999999"]:
+ for invalid_value in [0.0001, 0.00000001, 0.00099999, 31.99999999]:
self.test_send(from_wallet=w0, to_wallet=w1, amount=1, fee_rate=invalid_value, expect_error=(-3, msg))
self.test_send(from_wallet=w0, to_wallet=w1, amount=1, arg_fee_rate=invalid_value, expect_error=(-3, msg))
# Test fee_rate out of range (negative number).
diff --git a/test/functional/wallet_signrawtransactionwithwallet.py b/test/functional/wallet_signrawtransactionwithwallet.py
index 3d2f41cb83..d560dfdc11 100755
--- a/test/functional/wallet_signrawtransactionwithwallet.py
+++ b/test/functional/wallet_signrawtransactionwithwallet.py
@@ -33,6 +33,10 @@ from decimal import (
getcontext,
)
+
+RAW_TX = '020000000156b958f78e3f24e0b2f4e4db1255426b0902027cb37e3ddadb52e37c3557dddb0000000000ffffffff01c0a6b929010000001600149a2ee8c77140a053f36018ac8124a6ececc1668a00000000'
+
+
class SignRawTransactionWithWalletTest(BitcoinTestFramework):
def add_options(self, parser):
self.add_wallet_options(parser)
@@ -47,10 +51,12 @@ class SignRawTransactionWithWalletTest(BitcoinTestFramework):
def test_with_lock_outputs(self):
self.log.info("Test correct error reporting when trying to sign a locked output")
self.nodes[0].encryptwallet("password")
+ assert_raises_rpc_error(-13, "Please enter the wallet passphrase with walletpassphrase first", self.nodes[0].signrawtransactionwithwallet, RAW_TX)
+ self.nodes[0].walletpassphrase("password", 9999)
- rawTx = '020000000156b958f78e3f24e0b2f4e4db1255426b0902027cb37e3ddadb52e37c3557dddb0000000000ffffffff01c0a6b929010000001600149a2ee8c77140a053f36018ac8124a6ececc1668a00000000'
-
- assert_raises_rpc_error(-13, "Please enter the wallet passphrase with walletpassphrase first", self.nodes[0].signrawtransactionwithwallet, rawTx)
+ def test_with_invalid_sighashtype(self):
+ self.log.info("Test signrawtransactionwithwallet raises if an invalid sighashtype is passed")
+ assert_raises_rpc_error(-8, "all is not a valid sighash parameter.", self.nodes[0].signrawtransactionwithwallet, hexstring=RAW_TX, sighashtype="all")
def script_verification_error_test(self):
"""Create and sign a raw transaction with valid (vin 0), invalid (vin 1) and one missing (vin 2) input script.
@@ -299,6 +305,7 @@ class SignRawTransactionWithWalletTest(BitcoinTestFramework):
self.script_verification_error_test()
self.OP_1NEGATE_test()
self.test_with_lock_outputs()
+ self.test_with_invalid_sighashtype()
self.test_fully_signed_tx()
self.test_signing_with_csv()
self.test_signing_with_cltv()
diff --git a/test/fuzz/test_runner.py b/test/fuzz/test_runner.py
index 84028f3dc5..c9975af225 100755
--- a/test/fuzz/test_runner.py
+++ b/test/fuzz/test_runner.py
@@ -20,8 +20,7 @@ def get_fuzz_env(*, target, source_dir):
'FUZZ': target,
'UBSAN_OPTIONS':
f'suppressions={source_dir}/test/sanitizer_suppressions/ubsan:print_stacktrace=1:halt_on_error=1:report_error_type=1',
- 'ASAN_OPTIONS': # symbolizer disabled due to https://github.com/google/sanitizers/issues/1364#issuecomment-761072085
- 'symbolize=0:detect_stack_use_after_return=1:check_initialization_order=1:strict_init_order=1',
+ "ASAN_OPTIONS": "detect_stack_use_after_return=1:check_initialization_order=1:strict_init_order=1",
}
@@ -193,6 +192,42 @@ def main():
)
+def transform_process_message_target(targets, src_dir):
+ """Add a target per process message, and also keep ("process_message", {}) to allow for
+ cross-pollination, or unlimited search"""
+
+ p2p_msg_target = "process_message"
+ if (p2p_msg_target, {}) in targets:
+ lines = subprocess.run(
+ ["git", "grep", "--function-context", "g_all_net_message_types{", src_dir / "src" / "protocol.cpp"],
+ check=True,
+ stdout=subprocess.PIPE,
+ text=True,
+ ).stdout.splitlines()
+ lines = [l.split("::", 1)[1].split(",")[0].lower() for l in lines if l.startswith("src/protocol.cpp- NetMsgType::")]
+ assert len(lines)
+ targets += [(p2p_msg_target, {"LIMIT_TO_MESSAGE_TYPE": m}) for m in lines]
+ return targets
+
+
+def transform_rpc_target(targets, src_dir):
+ """Add a target per RPC command, and also keep ("rpc", {}) to allow for cross-pollination,
+ or unlimited search"""
+
+ rpc_target = "rpc"
+ if (rpc_target, {}) in targets:
+ lines = subprocess.run(
+ ["git", "grep", "--function-context", "RPC_COMMANDS_SAFE_FOR_FUZZING{", src_dir / "src" / "test" / "fuzz" / "rpc.cpp"],
+ check=True,
+ stdout=subprocess.PIPE,
+ text=True,
+ ).stdout.splitlines()
+ lines = [l.split("\"", 1)[1].split("\"")[0] for l in lines if l.startswith("src/test/fuzz/rpc.cpp- \"")]
+ assert len(lines)
+ targets += [(rpc_target, {"LIMIT_TO_RPC_COMMAND": r}) for r in lines]
+ return targets
+
+
def generate_corpus(*, fuzz_pool, src_dir, build_dir, corpus_dir, targets):
"""Generates new corpus.
@@ -200,29 +235,36 @@ def generate_corpus(*, fuzz_pool, src_dir, build_dir, corpus_dir, targets):
{corpus_dir}.
"""
logging.info("Generating corpus to {}".format(corpus_dir))
+ targets = [(t, {}) for t in targets] # expand to add dictionary for target-specific env variables
+ targets = transform_process_message_target(targets, Path(src_dir))
+ targets = transform_rpc_target(targets, Path(src_dir))
- def job(command, t):
- logging.debug("Running '{}'\n".format(" ".join(command)))
+ def job(command, t, t_env):
+ logging.debug(f"Running '{command}'")
logging.debug("Command '{}' output:\n'{}'\n".format(
- ' '.join(command),
+ command,
subprocess.run(
command,
- env=get_fuzz_env(target=t, source_dir=src_dir),
+ env={
+ **t_env,
+ **get_fuzz_env(target=t, source_dir=src_dir),
+ },
check=True,
stderr=subprocess.PIPE,
text=True,
- ).stderr))
+ ).stderr,
+ ))
futures = []
- for target in targets:
- target_corpus_dir = os.path.join(corpus_dir, target)
+ for target, t_env in targets:
+ target_corpus_dir = corpus_dir / target
os.makedirs(target_corpus_dir, exist_ok=True)
command = [
os.path.join(build_dir, 'src', 'test', 'fuzz', 'fuzz'),
"-runs=100000",
target_corpus_dir,
]
- futures.append(fuzz_pool.submit(job, command, target))
+ futures.append(fuzz_pool.submit(job, command, target, t_env))
for future in as_completed(futures):
future.result()
diff --git a/test/lint/README.md b/test/lint/README.md
index 704922d7ab..d9cfeb50ed 100644
--- a/test/lint/README.md
+++ b/test/lint/README.md
@@ -7,10 +7,8 @@ To run linters locally with the same versions as the CI environment, use the inc
Dockerfile:
```sh
-cd ./ci/lint
-docker build -t bitcoin-linter .
+DOCKER_BUILDKIT=1 docker build -t bitcoin-linter --file "./ci/lint_imagefile" ./
-cd /root/of/bitcoin/repo
docker run --rm -v $(pwd):/bitcoin -it bitcoin-linter
```
diff --git a/test/lint/lint-format-strings.py b/test/lint/lint-format-strings.py
index f54126e023..43addab2f3 100755
--- a/test/lint/lint-format-strings.py
+++ b/test/lint/lint-format-strings.py
@@ -16,7 +16,7 @@ import re
import sys
FUNCTION_NAMES_AND_NUMBER_OF_LEADING_ARGUMENTS = [
- 'FatalError,0',
+ 'FatalErrorf,0',
'fprintf,1',
'tfm::format,1', # Assuming tfm::::format(std::ostream&, ...
'LogConnectFailure,1',
diff --git a/test/lint/lint-python-utf8-encoding.py b/test/lint/lint-python-utf8-encoding.py
index 64d04bff57..8c9266470f 100755
--- a/test/lint/lint-python-utf8-encoding.py
+++ b/test/lint/lint-python-utf8-encoding.py
@@ -28,7 +28,7 @@ def check_fileopens():
if e.returncode > 1:
raise e
- filtered_fileopens = [fileopen for fileopen in fileopens if not re.search(r"encoding=.(ascii|utf8|utf-8).|open\([^,]*, ['\"][^'\"]*b[^'\"]*['\"]", fileopen)]
+ filtered_fileopens = [fileopen for fileopen in fileopens if not re.search(r"encoding=.(ascii|utf8|utf-8).|open\([^,]*, (\*\*kwargs|['\"][^'\"]*b[^'\"]*['\"])", fileopen)]
return filtered_fileopens
diff --git a/test/lint/run-lint-format-strings.py b/test/lint/run-lint-format-strings.py
index d1896dba84..ed98b1b2f8 100755
--- a/test/lint/run-lint-format-strings.py
+++ b/test/lint/run-lint-format-strings.py
@@ -14,7 +14,8 @@ import sys
FALSE_POSITIVES = [
("src/dbwrapper.cpp", "vsnprintf(p, limit - p, format, backup_ap)"),
- ("src/index/base.cpp", "FatalError(const char* fmt, const Args&... args)"),
+ ("src/index/base.cpp", "FatalErrorf(const char* fmt, const Args&... args)"),
+ ("src/index/base.h", "FatalErrorf(const char* fmt, const Args&... args)"),
("src/netbase.cpp", "LogConnectFailure(bool manual_connection, const char* fmt, const Args&... args)"),
("src/clientversion.cpp", "strprintf(_(COPYRIGHT_HOLDERS).translated, COPYRIGHT_HOLDERS_SUBSTITUTION)"),
("src/test/translation_tests.cpp", "strprintf(format, arg)"),
diff --git a/test/sanitizer_suppressions/ubsan b/test/sanitizer_suppressions/ubsan
index 74703b04ec..533e2eae51 100644
--- a/test/sanitizer_suppressions/ubsan
+++ b/test/sanitizer_suppressions/ubsan
@@ -1,9 +1,7 @@
+# Suppressions should use `sanitize-type:ClassName::MethodName`.
+
# -fsanitize=undefined suppressions
# =================================
-# The suppressions would be `sanitize-type:ClassName::MethodName`,
-# however due to a bug in clang the symbolizer is disabled and thus no symbol
-# names can be used.
-# See https://github.com/google/sanitizers/issues/1364
# -fsanitize=integer suppressions
# ===============================
@@ -11,26 +9,28 @@
# ------------
# Suppressions in dependencies that are developed outside this repository.
unsigned-integer-overflow:*/include/c++/
-# unsigned-integer-overflow in FuzzedDataProvider's ConsumeIntegralInRange
-unsigned-integer-overflow:FuzzedDataProvider.h
+unsigned-integer-overflow:FuzzedDataProvider::ConsumeIntegralInRange
unsigned-integer-overflow:leveldb/
unsigned-integer-overflow:minisketch/
+unsigned-integer-overflow:secp256k1/
unsigned-integer-overflow:test/fuzz/crypto_diff_fuzz_chacha20.cpp
implicit-integer-sign-change:*/include/boost/
implicit-integer-sign-change:*/include/c++/
implicit-integer-sign-change:*/new_allocator.h
implicit-integer-sign-change:crc32c/
-# implicit-integer-sign-change in FuzzedDataProvider's ConsumeIntegralInRange
-implicit-integer-sign-change:FuzzedDataProvider.h
implicit-integer-sign-change:minisketch/
+implicit-integer-sign-change:secp256k1/
implicit-signed-integer-truncation:*/include/c++/
implicit-signed-integer-truncation:leveldb/
+implicit-signed-integer-truncation:secp256k1/
implicit-unsigned-integer-truncation:*/include/c++/
implicit-unsigned-integer-truncation:leveldb/
+implicit-unsigned-integer-truncation:secp256k1/
implicit-unsigned-integer-truncation:test/fuzz/crypto_diff_fuzz_chacha20.cpp
shift-base:*/include/c++/
shift-base:leveldb/
shift-base:minisketch/
+shift-base:secp256k1/
shift-base:test/fuzz/crypto_diff_fuzz_chacha20.cpp
# Unsigned integer overflow occurs when the result of an unsigned integer
# computation cannot be represented in its type. Unlike signed integer overflow,