aboutsummaryrefslogtreecommitdiff
path: root/test/functional
diff options
context:
space:
mode:
Diffstat (limited to 'test/functional')
-rwxr-xr-xtest/functional/feature_asmap.py9
-rwxr-xr-xtest/functional/feature_assumeutxo.py27
-rwxr-xr-xtest/functional/feature_filelock.py6
-rwxr-xr-xtest/functional/feature_init.py11
-rwxr-xr-xtest/functional/feature_segwit.py23
-rwxr-xr-xtest/functional/feature_taproot.py43
-rwxr-xr-xtest/functional/feature_txindex_compatibility.py66
-rwxr-xr-xtest/functional/feature_utxo_set_hash.py2
-rwxr-xr-xtest/functional/interface_rest.py2
-rwxr-xr-xtest/functional/mempool_compatibility.py4
-rwxr-xr-xtest/functional/mempool_datacarrier.py17
-rwxr-xr-xtest/functional/mempool_dust.py3
-rwxr-xr-xtest/functional/mempool_limit.py16
-rwxr-xr-xtest/functional/mempool_sigoplimit.py6
-rwxr-xr-xtest/functional/p2p_addr_relay.py5
-rwxr-xr-xtest/functional/p2p_filter.py17
-rwxr-xr-xtest/functional/p2p_invalid_messages.py2
-rwxr-xr-xtest/functional/p2p_net_deadlock.py4
-rwxr-xr-xtest/functional/p2p_node_network_limited.py12
-rwxr-xr-xtest/functional/p2p_timeouts.py15
-rwxr-xr-xtest/functional/p2p_v2_transport.py18
-rwxr-xr-xtest/functional/rpc_blockchain.py40
-rwxr-xr-xtest/functional/rpc_net.py33
-rwxr-xr-xtest/functional/rpc_packages.py20
-rwxr-xr-xtest/functional/rpc_psbt.py57
-rwxr-xr-xtest/functional/rpc_rawtransaction.py13
-rw-r--r--test/functional/test_framework/blockfilter.py4
-rw-r--r--test/functional/test_framework/crypto/bip324_cipher.py201
-rw-r--r--test/functional/test_framework/crypto/chacha20.py162
-rw-r--r--test/functional/test_framework/crypto/ellswift.py (renamed from test/functional/test_framework/ellswift.py)2
-rw-r--r--test/functional/test_framework/crypto/ellswift_decode_test_vectors.csv (renamed from test/functional/test_framework/ellswift_decode_test_vectors.csv)0
-rw-r--r--test/functional/test_framework/crypto/hkdf.py33
-rw-r--r--test/functional/test_framework/crypto/muhash.py55
-rw-r--r--test/functional/test_framework/crypto/poly1305.py104
-rw-r--r--test/functional/test_framework/crypto/ripemd160.py (renamed from test/functional/test_framework/ripemd160.py)0
-rw-r--r--test/functional/test_framework/crypto/secp256k1.py (renamed from test/functional/test_framework/secp256k1.py)0
-rw-r--r--test/functional/test_framework/crypto/siphash.py (renamed from test/functional/test_framework/siphash.py)0
-rw-r--r--test/functional/test_framework/crypto/xswiftec_inv_test_vectors.csv (renamed from test/functional/test_framework/xswiftec_inv_test_vectors.csv)0
-rw-r--r--test/functional/test_framework/key.py2
-rwxr-xr-xtest/functional/test_framework/messages.py2
-rw-r--r--test/functional/test_framework/muhash.py110
-rwxr-xr-xtest/functional/test_framework/p2p.py7
-rw-r--r--test/functional/test_framework/script.py7
-rwxr-xr-xtest/functional/test_framework/test_framework.py25
-rwxr-xr-xtest/functional/test_framework/test_node.py51
-rw-r--r--test/functional/test_framework/util.py24
-rw-r--r--test/functional/test_framework/wallet.py3
-rwxr-xr-xtest/functional/test_runner.py20
-rwxr-xr-xtest/functional/wallet_avoidreuse.py2
-rwxr-xr-xtest/functional/wallet_basic.py8
-rwxr-xr-xtest/functional/wallet_fast_rescan.py4
-rwxr-xr-xtest/functional/wallet_fundrawtransaction.py37
-rwxr-xr-xtest/functional/wallet_importdescriptors.py11
-rwxr-xr-xtest/functional/wallet_listtransactions.py29
-rwxr-xr-xtest/functional/wallet_migration.py13
-rwxr-xr-xtest/functional/wallet_miniscript.py2
-rwxr-xr-xtest/functional/wallet_reindex.py108
-rwxr-xr-xtest/functional/wallet_signrawtransactionwithwallet.py19
-rwxr-xr-xtest/functional/wallet_txn_clone.py19
-rwxr-xr-xtest/functional/wallet_txn_doublespend.py28
60 files changed, 1100 insertions, 463 deletions
diff --git a/test/functional/feature_asmap.py b/test/functional/feature_asmap.py
index 9cff8042a8..ae483fe449 100755
--- a/test/functional/feature_asmap.py
+++ b/test/functional/feature_asmap.py
@@ -111,6 +111,14 @@ class AsmapTest(BitcoinTestFramework):
self.node.assert_start_raises_init_error(extra_args=['-asmap'], expected_msg=msg)
os.remove(self.default_asmap)
+ def test_asmap_health_check(self):
+ self.log.info('Test bitcoind -asmap logs ASMap Health Check with basic stats')
+ shutil.copyfile(self.asmap_raw, self.default_asmap)
+ msg = "ASMap Health Check: 2 clearnet peers are mapped to 1 ASNs with 0 peers being unmapped"
+ with self.node.assert_debug_log(expected_msgs=[msg]):
+ self.start_node(0, extra_args=['-asmap'])
+ os.remove(self.default_asmap)
+
def run_test(self):
self.node = self.nodes[0]
self.datadir = self.node.chain_path
@@ -124,6 +132,7 @@ class AsmapTest(BitcoinTestFramework):
self.test_asmap_interaction_with_addrman_containing_entries()
self.test_default_asmap_with_missing_file()
self.test_empty_asmap()
+ self.test_asmap_health_check()
if __name__ == '__main__':
diff --git a/test/functional/feature_assumeutxo.py b/test/functional/feature_assumeutxo.py
index 9c265649d5..2e3589b020 100755
--- a/test/functional/feature_assumeutxo.py
+++ b/test/functional/feature_assumeutxo.py
@@ -33,6 +33,8 @@ Interesting starting states could be loading a snapshot when the current chain t
- TODO: Not an ancestor or a descendant of the snapshot block and has more work
"""
+from shutil import rmtree
+
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
@@ -73,7 +75,7 @@ class AssumeutxoTest(BitcoinTestFramework):
with self.nodes[1].assert_debug_log([log_msg]):
assert_raises_rpc_error(-32603, f"Unable to load UTXO snapshot{rpc_details}", self.nodes[1].loadtxoutset, bad_snapshot_path)
- self.log.info(" - snapshot file refering to a block that is not in the assumeutxo parameters")
+ self.log.info(" - snapshot file referring to a block that is not in the assumeutxo parameters")
prev_block_hash = self.nodes[0].getblockhash(SNAPSHOT_BASE_HEIGHT - 1)
bogus_block_hash = "0" * 64 # Represents any unknown block hash
for bad_block_hash in [bogus_block_hash, prev_block_hash]:
@@ -107,6 +109,28 @@ class AssumeutxoTest(BitcoinTestFramework):
f.write(valid_snapshot_contents[(32 + 8 + offset + len(content)):])
expected_error(log_msg=f"[snapshot] bad snapshot content hash: expected 61d9c2b29a2571a5fe285fe2d8554f91f93309666fc9b8223ee96338de25ff53, got {wrong_hash}")
+ def test_invalid_chainstate_scenarios(self):
+ self.log.info("Test different scenarios of invalid snapshot chainstate in datadir")
+
+ self.log.info(" - snapshot chainstate referring to a block that is not in the assumeutxo parameters")
+ self.stop_node(0)
+ chainstate_snapshot_path = self.nodes[0].chain_path / "chainstate_snapshot"
+ chainstate_snapshot_path.mkdir()
+ with open(chainstate_snapshot_path / "base_blockhash", 'wb') as f:
+ f.write(b'z' * 32)
+
+ def expected_error(log_msg="", error_msg=""):
+ with self.nodes[0].assert_debug_log([log_msg]):
+ self.nodes[0].assert_start_raises_init_error(expected_msg=error_msg)
+
+ expected_error_msg = f"Error: A fatal internal error occurred, see debug.log for details"
+ error_details = f"Assumeutxo data not found for the given blockhash"
+ expected_error(log_msg=error_details, error_msg=expected_error_msg)
+
+ # resurrect node again
+ rmtree(chainstate_snapshot_path)
+ self.start_node(0)
+
def run_test(self):
"""
Bring up two (disconnected) nodes, mine some new blocks on the first,
@@ -166,6 +190,7 @@ class AssumeutxoTest(BitcoinTestFramework):
assert_equal(n0.getblockchaininfo()["blocks"], FINAL_HEIGHT)
self.test_invalid_snapshot_scenarios(dump_output['path'])
+ self.test_invalid_chainstate_scenarios()
self.log.info(f"Loading snapshot into second node from {dump_output['path']}")
loaded = n1.loadtxoutset(dump_output['path'])
diff --git a/test/functional/feature_filelock.py b/test/functional/feature_filelock.py
index 24a68a04bf..567207915e 100755
--- a/test/functional/feature_filelock.py
+++ b/test/functional/feature_filelock.py
@@ -30,6 +30,12 @@ class FilelockTest(BitcoinTestFramework):
expected_msg = f"Error: Cannot obtain a lock on data directory {datadir}. {self.config['environment']['PACKAGE_NAME']} is probably already running."
self.nodes[1].assert_start_raises_init_error(extra_args=[f'-datadir={self.nodes[0].datadir_path}', '-noserver'], expected_msg=expected_msg)
+ self.log.info("Check that cookie and PID file are not deleted when attempting to start a second bitcoind using the same datadir")
+ cookie_file = datadir / ".cookie"
+ assert cookie_file.exists() # should not be deleted during the second bitcoind instance shutdown
+ pid_file = datadir / "bitcoind.pid"
+ assert pid_file.exists()
+
if self.is_wallet_compiled():
def check_wallet_filelock(descriptors):
wallet_name = ''.join([random.choice(string.ascii_lowercase) for _ in range(6)])
diff --git a/test/functional/feature_init.py b/test/functional/feature_init.py
index 94f5116f9b..142d75a851 100755
--- a/test/functional/feature_init.py
+++ b/test/functional/feature_init.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2021-2022 The Bitcoin Core developers
+# Copyright (c) 2021-present The Bitcoin Core developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Stress tests related to node initialization."""
@@ -133,15 +133,12 @@ class InitStressTest(BitcoinTestFramework):
for target_file in target_files:
self.log.info(f"Perturbing file to ensure failure {target_file}")
- with open(target_file, "rb") as tf_read:
- contents = tf_read.read()
- tweaked_contents = bytearray(contents)
+ with open(target_file, "r+b") as tf:
# Since the genesis block is not checked by -checkblocks, the
# perturbation window must be chosen such that a higher block
# in blk*.dat is affected.
- tweaked_contents[150:350] = b'1' * 200
- with open(target_file, "wb") as tf_write:
- tf_write.write(bytes(tweaked_contents))
+ tf.seek(150)
+ tf.write(b"1" * 200)
start_expecting_error(err_fragment)
diff --git a/test/functional/feature_segwit.py b/test/functional/feature_segwit.py
index 6c467fa613..4dc19222c4 100755
--- a/test/functional/feature_segwit.py
+++ b/test/functional/feature_segwit.py
@@ -88,14 +88,11 @@ class SegWitTest(BitcoinTestFramework):
self.extra_args = [
[
"-acceptnonstdtxn=1",
- "-rpcserialversion=0",
- "-deprecatedrpc=serialversion",
"-testactivationheight=segwit@165",
"-addresstype=legacy",
],
[
"-acceptnonstdtxn=1",
- "-rpcserialversion=1",
"-testactivationheight=segwit@165",
"-addresstype=legacy",
],
@@ -224,18 +221,6 @@ class SegWitTest(BitcoinTestFramework):
self.fail_accept(self.nodes[0], "mandatory-script-verify-flag-failed (Witness program hash mismatch)", p2sh_ids[NODE_0][P2WPKH][0], sign=False, redeem_script=witness_script(False, self.pubkey[0]))
self.fail_accept(self.nodes[0], "mandatory-script-verify-flag-failed (Witness program was passed an empty witness)", p2sh_ids[NODE_0][P2WSH][0], sign=False, redeem_script=witness_script(True, self.pubkey[0]))
- self.log.info("Verify block and transaction serialization rpcs return differing serializations depending on rpc serialization flag")
- assert self.nodes[2].getblock(blockhash, False) != self.nodes[0].getblock(blockhash, False)
- assert self.nodes[1].getblock(blockhash, False) == self.nodes[2].getblock(blockhash, False)
-
- for tx_id in segwit_tx_list:
- tx = tx_from_hex(self.nodes[2].gettransaction(tx_id)["hex"])
- assert self.nodes[2].getrawtransaction(tx_id, False, blockhash) != self.nodes[0].getrawtransaction(tx_id, False, blockhash)
- assert self.nodes[1].getrawtransaction(tx_id, False, blockhash) == self.nodes[2].getrawtransaction(tx_id, False, blockhash)
- assert self.nodes[0].getrawtransaction(tx_id, False, blockhash) != self.nodes[2].gettransaction(tx_id)["hex"]
- assert self.nodes[1].getrawtransaction(tx_id, False, blockhash) == self.nodes[2].gettransaction(tx_id)["hex"]
- assert self.nodes[0].getrawtransaction(tx_id, False, blockhash) == tx.serialize_without_witness().hex()
-
# Coinbase contains the witness commitment nonce, check that RPC shows us
coinbase_txid = self.nodes[2].getblock(blockhash)['tx'][0]
coinbase_tx = self.nodes[2].gettransaction(txid=coinbase_txid, verbose=True)
@@ -276,9 +261,6 @@ class SegWitTest(BitcoinTestFramework):
# tx3 (non-segwit input, paying to a non-segwit output).
# tx1 is allowed to appear in the block, but no others.
txid1 = send_to_witness(1, self.nodes[0], find_spendable_utxo(self.nodes[0], 50), self.pubkey[0], False, Decimal("49.996"))
- hex_tx = self.nodes[0].gettransaction(txid)['hex']
- tx = tx_from_hex(hex_tx)
- assert tx.wit.is_null() # This should not be a segwit input
assert txid1 in self.nodes[0].getrawmempool()
tx1_hex = self.nodes[0].gettransaction(txid1)['hex']
@@ -613,11 +595,6 @@ class SegWitTest(BitcoinTestFramework):
assert_equal(self.nodes[1].gettransaction(txid, True)["txid"], txid)
assert_equal(self.nodes[1].listtransactions("*", 1, 0, True)[0]["txid"], txid)
- self.log.info('Test negative and unknown rpcserialversion throw an init error')
- self.stop_node(0)
- self.nodes[0].assert_start_raises_init_error(["-rpcserialversion=-1"], "Error: rpcserialversion must be non-negative.")
- self.nodes[0].assert_start_raises_init_error(["-rpcserialversion=100"], "Error: Unknown rpcserialversion requested.")
-
def mine_and_test_listunspent(self, script_list, ismine):
utxo = find_spendable_utxo(self.nodes[0], 50)
tx = CTransaction()
diff --git a/test/functional/feature_taproot.py b/test/functional/feature_taproot.py
index e32319961e..e85541d0ec 100755
--- a/test/functional/feature_taproot.py
+++ b/test/functional/feature_taproot.py
@@ -95,7 +95,6 @@ from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_raises_rpc_error,
assert_equal,
- random_bytes,
)
from test_framework.wallet_util import generate_keypair
from test_framework.key import (
@@ -105,7 +104,7 @@ from test_framework.key import (
tweak_add_privkey,
ECKey,
)
-from test_framework import secp256k1
+from test_framework.crypto import secp256k1
from test_framework.address import (
hash160,
program_to_witness,
@@ -694,7 +693,7 @@ def spenders_taproot_active():
# Generate an invalid public key
while True:
- invalid_pub = random_bytes(32)
+ invalid_pub = random.randbytes(32)
if not secp256k1.GE.is_valid_x(int.from_bytes(invalid_pub, 'big')):
break
@@ -710,7 +709,7 @@ def spenders_taproot_active():
# == Tests for signature hashing ==
# Run all tests once with no annex, and once with a valid random annex.
- for annex in [None, lambda _: bytes([ANNEX_TAG]) + random_bytes(random.randrange(0, 250))]:
+ for annex in [None, lambda _: bytes([ANNEX_TAG]) + random.randbytes(random.randrange(0, 250))]:
# Non-empty annex is non-standard
no_annex = annex is None
@@ -739,7 +738,7 @@ def spenders_taproot_active():
scripts = [
("pk_codesep", CScript(random_checksig_style(pubs[1]) + bytes([OP_CODESEPARATOR]))), # codesep after checksig
("codesep_pk", CScript(bytes([OP_CODESEPARATOR]) + random_checksig_style(pubs[1]))), # codesep before checksig
- ("branched_codesep", CScript([random_bytes(random.randrange(2, 511)), OP_DROP, OP_IF, OP_CODESEPARATOR, pubs[0], OP_ELSE, OP_CODESEPARATOR, pubs[1], OP_ENDIF, OP_CHECKSIG])), # branch dependent codesep
+ ("branched_codesep", CScript([random.randbytes(random.randrange(2, 511)), OP_DROP, OP_IF, OP_CODESEPARATOR, pubs[0], OP_ELSE, OP_CODESEPARATOR, pubs[1], OP_ENDIF, OP_CHECKSIG])), # branch dependent codesep
# Note that the first data push in the "branched_codesep" script has the purpose of
# randomizing the sighash, both by varying script size and content. In order to
# avoid MINIMALDATA script verification errors caused by not-minimal-encoded data
@@ -792,8 +791,8 @@ def spenders_taproot_active():
add_spender(spenders, "siglen/empty_csv", tap=tap, key=secs[2], leaf="csv", hashtype=hashtype, **SINGLE_SIG, failure={"sign": b""}, **ERR_CHECKSIGVERIFY)
add_spender(spenders, "siglen/empty_cs", tap=tap, key=secs[2], leaf="cs_pos", hashtype=hashtype, **SINGLE_SIG, failure={"sign": b""}, **ERR_NO_SUCCESS)
add_spender(spenders, "siglen/empty_csa", tap=tap, key=secs[2], leaf="csa_pos", hashtype=hashtype, **SINGLE_SIG, failure={"sign": b""}, **ERR_NO_SUCCESS)
- add_spender(spenders, "siglen/empty_cs_neg", tap=tap, key=secs[2], leaf="cs_neg", hashtype=hashtype, **SINGLE_SIG, sign=b"", failure={"sign": lambda _: random_bytes(random.randrange(1, 63))}, **ERR_SIG_SIZE)
- add_spender(spenders, "siglen/empty_csa_neg", tap=tap, key=secs[2], leaf="csa_neg", hashtype=hashtype, **SINGLE_SIG, sign=b"", failure={"sign": lambda _: random_bytes(random.randrange(66, 100))}, **ERR_SIG_SIZE)
+ add_spender(spenders, "siglen/empty_cs_neg", tap=tap, key=secs[2], leaf="cs_neg", hashtype=hashtype, **SINGLE_SIG, sign=b"", failure={"sign": lambda _: random.randbytes(random.randrange(1, 63))}, **ERR_SIG_SIZE)
+ add_spender(spenders, "siglen/empty_csa_neg", tap=tap, key=secs[2], leaf="csa_neg", hashtype=hashtype, **SINGLE_SIG, sign=b"", failure={"sign": lambda _: random.randbytes(random.randrange(66, 100))}, **ERR_SIG_SIZE)
# Appending a zero byte to signatures invalidates them
add_spender(spenders, "siglen/padzero_keypath", tap=tap, key=secs[3], hashtype=hashtype, **SIG_ADD_ZERO, **(ERR_SIG_HASHTYPE if hashtype == SIGHASH_DEFAULT else ERR_SIG_SIZE))
add_spender(spenders, "siglen/padzero_csv", tap=tap, key=secs[2], leaf="csv", hashtype=hashtype, **SINGLE_SIG, **SIG_ADD_ZERO, **(ERR_SIG_HASHTYPE if hashtype == SIGHASH_DEFAULT else ERR_SIG_SIZE))
@@ -874,14 +873,14 @@ def spenders_taproot_active():
# Test that empty witnesses are invalid.
add_spender(spenders, "spendpath/emptywit", tap=tap, leaf="128deep", **SINGLE_SIG, key=secs[0], failure={"witness": []}, **ERR_EMPTY_WITNESS)
# Test that adding garbage to the control block invalidates it.
- add_spender(spenders, "spendpath/padlongcontrol", tap=tap, leaf="128deep", **SINGLE_SIG, key=secs[0], failure={"controlblock": lambda ctx: default_controlblock(ctx) + random_bytes(random.randrange(1, 32))}, **ERR_CONTROLBLOCK_SIZE)
+ add_spender(spenders, "spendpath/padlongcontrol", tap=tap, leaf="128deep", **SINGLE_SIG, key=secs[0], failure={"controlblock": lambda ctx: default_controlblock(ctx) + random.randbytes(random.randrange(1, 32))}, **ERR_CONTROLBLOCK_SIZE)
# Test that truncating the control block invalidates it.
add_spender(spenders, "spendpath/trunclongcontrol", tap=tap, leaf="128deep", **SINGLE_SIG, key=secs[0], failure={"controlblock": lambda ctx: default_merklebranch(ctx)[0:random.randrange(1, 32)]}, **ERR_CONTROLBLOCK_SIZE)
scripts = [("s", CScript([pubs[0], OP_CHECKSIG]))]
tap = taproot_construct(pubs[1], scripts)
# Test that adding garbage to the control block invalidates it.
- add_spender(spenders, "spendpath/padshortcontrol", tap=tap, leaf="s", **SINGLE_SIG, key=secs[0], failure={"controlblock": lambda ctx: default_controlblock(ctx) + random_bytes(random.randrange(1, 32))}, **ERR_CONTROLBLOCK_SIZE)
+ add_spender(spenders, "spendpath/padshortcontrol", tap=tap, leaf="s", **SINGLE_SIG, key=secs[0], failure={"controlblock": lambda ctx: default_controlblock(ctx) + random.randbytes(random.randrange(1, 32))}, **ERR_CONTROLBLOCK_SIZE)
# Test that truncating the control block invalidates it.
add_spender(spenders, "spendpath/truncshortcontrol", tap=tap, leaf="s", **SINGLE_SIG, key=secs[0], failure={"controlblock": lambda ctx: default_merklebranch(ctx)[0:random.randrange(1, 32)]}, **ERR_CONTROLBLOCK_SIZE)
# Test that truncating the control block to 1 byte ("-1 Merkle length") invalidates it
@@ -970,9 +969,9 @@ def spenders_taproot_active():
# 24) Script that expects an input stack of 1001 elements
("t24", CScript([OP_DROP] * 1000 + [pubs[1], OP_CHECKSIG])),
# 25) Script that pushes a MAX_SCRIPT_ELEMENT_SIZE-bytes element
- ("t25", CScript([random_bytes(MAX_SCRIPT_ELEMENT_SIZE), OP_DROP, pubs[1], OP_CHECKSIG])),
+ ("t25", CScript([random.randbytes(MAX_SCRIPT_ELEMENT_SIZE), OP_DROP, pubs[1], OP_CHECKSIG])),
# 26) Script that pushes a (MAX_SCRIPT_ELEMENT_SIZE+1)-bytes element
- ("t26", CScript([random_bytes(MAX_SCRIPT_ELEMENT_SIZE+1), OP_DROP, pubs[1], OP_CHECKSIG])),
+ ("t26", CScript([random.randbytes(MAX_SCRIPT_ELEMENT_SIZE+1), OP_DROP, pubs[1], OP_CHECKSIG])),
# 27) CHECKSIGADD that must fail because numeric argument number is >4 bytes
("t27", CScript([CScriptNum(OVERSIZE_NUMBER), pubs[1], OP_CHECKSIGADD])),
# 28) Pushes random CScriptNum value, checks OP_CHECKSIGADD result
@@ -1005,9 +1004,9 @@ def spenders_taproot_active():
"tap": tap,
}
# Test that MAX_SCRIPT_ELEMENT_SIZE byte stack element inputs are valid, but not one more (and 80 bytes is standard but 81 is not).
- add_spender(spenders, "tapscript/inputmaxlimit", leaf="t0", **common, standard=False, inputs=[getter("sign"), random_bytes(MAX_SCRIPT_ELEMENT_SIZE)], failure={"inputs": [getter("sign"), random_bytes(MAX_SCRIPT_ELEMENT_SIZE+1)]}, **ERR_PUSH_LIMIT)
- add_spender(spenders, "tapscript/input80limit", leaf="t0", **common, inputs=[getter("sign"), random_bytes(80)])
- add_spender(spenders, "tapscript/input81limit", leaf="t0", **common, standard=False, inputs=[getter("sign"), random_bytes(81)])
+ add_spender(spenders, "tapscript/inputmaxlimit", leaf="t0", **common, standard=False, inputs=[getter("sign"), random.randbytes(MAX_SCRIPT_ELEMENT_SIZE)], failure={"inputs": [getter("sign"), random.randbytes(MAX_SCRIPT_ELEMENT_SIZE+1)]}, **ERR_PUSH_LIMIT)
+ add_spender(spenders, "tapscript/input80limit", leaf="t0", **common, inputs=[getter("sign"), random.randbytes(80)])
+ add_spender(spenders, "tapscript/input81limit", leaf="t0", **common, standard=False, inputs=[getter("sign"), random.randbytes(81)])
# Test that OP_CHECKMULTISIG and OP_CHECKMULTISIGVERIFY cause failure, but OP_CHECKSIG and OP_CHECKSIGVERIFY work.
add_spender(spenders, "tapscript/disabled_checkmultisig", leaf="t1", **common, **SINGLE_SIG, failure={"leaf": "t3"}, **ERR_TAPSCRIPT_CHECKMULTISIG)
add_spender(spenders, "tapscript/disabled_checkmultisigverify", leaf="t2", **common, **SINGLE_SIG, failure={"leaf": "t4"}, **ERR_TAPSCRIPT_CHECKMULTISIG)
@@ -1062,7 +1061,7 @@ def spenders_taproot_active():
# n OP_CHECKSIGVERIFYs and 1 OP_CHECKSIGADD, but also one unexecuted OP_CHECKSIGVERIFY.
lambda n, pk: (CScript([OP_DROP, pk, OP_0, OP_IF, OP_2DUP, OP_CHECKSIGVERIFY, OP_ENDIF] + [OP_2DUP, OP_CHECKSIGVERIFY] * n + [OP_2, OP_SWAP, OP_CHECKSIGADD, OP_3, OP_EQUAL]), n + 1),
# n OP_CHECKSIGVERIFYs and 1 OP_CHECKSIGADD, but also one unexecuted OP_CHECKSIG.
- lambda n, pk: (CScript([random_bytes(220), OP_2DROP, pk, OP_1, OP_NOTIF, OP_2DUP, OP_CHECKSIG, OP_VERIFY, OP_ENDIF] + [OP_2DUP, OP_CHECKSIGVERIFY] * n + [OP_4, OP_SWAP, OP_CHECKSIGADD, OP_5, OP_EQUAL]), n + 1),
+ lambda n, pk: (CScript([random.randbytes(220), OP_2DROP, pk, OP_1, OP_NOTIF, OP_2DUP, OP_CHECKSIG, OP_VERIFY, OP_ENDIF] + [OP_2DUP, OP_CHECKSIGVERIFY] * n + [OP_4, OP_SWAP, OP_CHECKSIGADD, OP_5, OP_EQUAL]), n + 1),
# n OP_CHECKSIGVERIFYs and 1 OP_CHECKSIGADD, but also one unexecuted OP_CHECKSIGADD.
lambda n, pk: (CScript([OP_DROP, pk, OP_1, OP_IF, OP_ELSE, OP_2DUP, OP_6, OP_SWAP, OP_CHECKSIGADD, OP_7, OP_EQUALVERIFY, OP_ENDIF] + [OP_2DUP, OP_CHECKSIGVERIFY] * n + [OP_8, OP_SWAP, OP_CHECKSIGADD, OP_9, OP_EQUAL]), n + 1),
# n+1 OP_CHECKSIGs, but also one OP_CHECKSIG with an empty signature.
@@ -1070,9 +1069,9 @@ def spenders_taproot_active():
# n OP_CHECKSIGADDs and 1 OP_CHECKSIG, but also an OP_CHECKSIGADD with an empty signature.
lambda n, pk: (CScript([OP_DROP, OP_0, OP_10, pk, OP_CHECKSIGADD, OP_10, OP_EQUALVERIFY, pk] + [OP_2DUP, OP_16, OP_SWAP, OP_CHECKSIGADD, b'\x11', OP_EQUALVERIFY] * n + [OP_CHECKSIG]), n + 1),
]
- for annex in [None, bytes([ANNEX_TAG]) + random_bytes(random.randrange(1000))]:
+ for annex in [None, bytes([ANNEX_TAG]) + random.randbytes(random.randrange(1000))]:
for hashtype in [SIGHASH_DEFAULT, SIGHASH_ALL]:
- for pubkey in [pubs[1], random_bytes(random.choice([x for x in range(2, 81) if x != 32]))]:
+ for pubkey in [pubs[1], random.randbytes(random.choice([x for x in range(2, 81) if x != 32]))]:
for fn_num, fn in enumerate(SIGOPS_RATIO_SCRIPTS):
merkledepth = random.randrange(129)
@@ -1109,7 +1108,7 @@ def spenders_taproot_active():
scripts = [scripts, random.choice(PARTNER_MERKLE_FN)]
tap = taproot_construct(pubs[0], scripts)
standard = annex is None and dummylen <= 80 and len(pubkey) == 32
- add_spender(spenders, "tapscript/sigopsratio_%i" % fn_num, tap=tap, leaf="s", annex=annex, hashtype=hashtype, key=secs[1], inputs=[getter("sign"), random_bytes(dummylen)], standard=standard, failure={"inputs": [getter("sign"), random_bytes(dummylen - 1)]}, **ERR_SIGOPS_RATIO)
+ add_spender(spenders, "tapscript/sigopsratio_%i" % fn_num, tap=tap, leaf="s", annex=annex, hashtype=hashtype, key=secs[1], inputs=[getter("sign"), random.randbytes(dummylen)], standard=standard, failure={"inputs": [getter("sign"), random.randbytes(dummylen - 1)]}, **ERR_SIGOPS_RATIO)
# Future leaf versions
for leafver in range(0, 0x100, 2):
@@ -1123,8 +1122,8 @@ def spenders_taproot_active():
("return_unkver", CScript([OP_RETURN]), leafver),
("undecodable_c0", CScript([OP_PUSHDATA1])),
("undecodable_unkver", CScript([OP_PUSHDATA1]), leafver),
- ("bigpush_c0", CScript([random_bytes(MAX_SCRIPT_ELEMENT_SIZE+1), OP_DROP])),
- ("bigpush_unkver", CScript([random_bytes(MAX_SCRIPT_ELEMENT_SIZE+1), OP_DROP]), leafver),
+ ("bigpush_c0", CScript([random.randbytes(MAX_SCRIPT_ELEMENT_SIZE+1), OP_DROP])),
+ ("bigpush_unkver", CScript([random.randbytes(MAX_SCRIPT_ELEMENT_SIZE+1), OP_DROP]), leafver),
("1001push_c0", CScript([OP_0] * 1001)),
("1001push_unkver", CScript([OP_0] * 1001), leafver),
]
@@ -1153,8 +1152,8 @@ def spenders_taproot_active():
("undecodable_success", CScript([opcode, OP_PUSHDATA1])),
("undecodable_nop", CScript([OP_NOP, OP_PUSHDATA1])),
("undecodable_bypassed_success", CScript([OP_PUSHDATA1, OP_2, opcode])),
- ("bigpush_success", CScript([random_bytes(MAX_SCRIPT_ELEMENT_SIZE+1), OP_DROP, opcode])),
- ("bigpush_nop", CScript([random_bytes(MAX_SCRIPT_ELEMENT_SIZE+1), OP_DROP, OP_NOP])),
+ ("bigpush_success", CScript([random.randbytes(MAX_SCRIPT_ELEMENT_SIZE+1), OP_DROP, opcode])),
+ ("bigpush_nop", CScript([random.randbytes(MAX_SCRIPT_ELEMENT_SIZE+1), OP_DROP, OP_NOP])),
("1001push_success", CScript([OP_0] * 1001 + [opcode])),
("1001push_nop", CScript([OP_0] * 1001 + [OP_NOP])),
]
diff --git a/test/functional/feature_txindex_compatibility.py b/test/functional/feature_txindex_compatibility.py
deleted file mode 100755
index 939271b385..0000000000
--- a/test/functional/feature_txindex_compatibility.py
+++ /dev/null
@@ -1,66 +0,0 @@
-#!/usr/bin/env python3
-# Copyright (c) 2021-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 that legacy txindex will be disabled on upgrade.
-
-Previous releases are required by this test, see test/README.md.
-"""
-
-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 = 2
- self.extra_args = [
- ["-reindex", "-txindex"],
- [],
- ]
-
- def skip_test_if_missing_module(self):
- self.skip_if_no_previous_releases()
-
- def setup_network(self):
- self.add_nodes(
- self.num_nodes,
- self.extra_args,
- versions=[
- 160300, # Last release with legacy txindex
- None, # For MiniWallet, without migration code
- ],
- )
- self.start_nodes()
- self.connect_nodes(0, 1)
-
- def run_test(self):
- mini_wallet = MiniWallet(self.nodes[1])
- 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)
-
- 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("Drop legacy txindex")
- drop_index_chain_dir = self.nodes[1].chain_path
- shutil.rmtree(drop_index_chain_dir)
- shutil.copytree(legacy_chain_dir, drop_index_chain_dir)
- # Build txindex from scratch and check there is no error this time
- self.start_node(1, extra_args=["-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()
-
-
-if __name__ == "__main__":
- TxindexCompatibilityTest().main()
diff --git a/test/functional/feature_utxo_set_hash.py b/test/functional/feature_utxo_set_hash.py
index ce2a5ab8ac..be154b411f 100755
--- a/test/functional/feature_utxo_set_hash.py
+++ b/test/functional/feature_utxo_set_hash.py
@@ -11,7 +11,7 @@ from test_framework.messages import (
COutPoint,
from_hex,
)
-from test_framework.muhash import MuHash3072
+from test_framework.crypto.muhash import MuHash3072
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_equal
from test_framework.wallet import MiniWallet
diff --git a/test/functional/interface_rest.py b/test/functional/interface_rest.py
index c0679c5ba9..b81eae2506 100755
--- a/test/functional/interface_rest.py
+++ b/test/functional/interface_rest.py
@@ -65,7 +65,7 @@ class RESTTest (BitcoinTestFramework):
body: str = '',
status: int = 200,
ret_type: RetType = RetType.JSON,
- query_params: Optional[typing.Dict[str, typing.Any]] = None,
+ query_params: Optional[dict[str, typing.Any]] = None,
) -> typing.Union[http.client.HTTPResponse, bytes, str, None]:
rest_uri = '/rest' + uri
if req_type in ReqType:
diff --git a/test/functional/mempool_compatibility.py b/test/functional/mempool_compatibility.py
index fd3e219586..a126f164aa 100755
--- a/test/functional/mempool_compatibility.py
+++ b/test/functional/mempool_compatibility.py
@@ -28,7 +28,7 @@ class MempoolCompatibilityTest(BitcoinTestFramework):
def setup_network(self):
self.add_nodes(self.num_nodes, versions=[
- 200100, # Last release with previous mempool format
+ 200100, # Last release without unbroadcast serialization and without XOR
None,
])
self.start_nodes()
@@ -59,7 +59,7 @@ class MempoolCompatibilityTest(BitcoinTestFramework):
old_node_mempool.rename(new_node_mempool)
self.log.info("Start new node and verify mempool contains the tx")
- self.start_node(1)
+ self.start_node(1, extra_args=["-persistmempoolv1=1"])
assert old_tx_hash in new_node.getrawmempool()
self.log.info("Add unbroadcasted tx to mempool on new node and shutdown")
diff --git a/test/functional/mempool_datacarrier.py b/test/functional/mempool_datacarrier.py
index 951bf37ae8..2e27aa988e 100755
--- a/test/functional/mempool_datacarrier.py
+++ b/test/functional/mempool_datacarrier.py
@@ -13,12 +13,11 @@ from test_framework.script import (
)
from test_framework.test_framework import BitcoinTestFramework
from test_framework.test_node import TestNode
-from test_framework.util import (
- assert_raises_rpc_error,
- random_bytes,
-)
+from test_framework.util import assert_raises_rpc_error
from test_framework.wallet import MiniWallet
+from random import randbytes
+
class DataCarrierTest(BitcoinTestFramework):
def set_test_params(self):
@@ -48,11 +47,11 @@ class DataCarrierTest(BitcoinTestFramework):
self.wallet = MiniWallet(self.nodes[0])
# 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)
- too_long_data = random_bytes(MAX_OP_RETURN_RELAY - 2)
- small_data = random_bytes(MAX_OP_RETURN_RELAY - 4)
- one_byte = random_bytes(1)
- zero_bytes = random_bytes(0)
+ default_size_data = randbytes(MAX_OP_RETURN_RELAY - 3)
+ too_long_data = randbytes(MAX_OP_RETURN_RELAY - 2)
+ small_data = randbytes(MAX_OP_RETURN_RELAY - 4)
+ one_byte = randbytes(1)
+ zero_bytes = randbytes(0)
self.log.info("Testing null data transaction with default -datacarrier and -datacarriersize values.")
self.test_null_data_transaction(node=self.nodes[0], data=default_size_data, success=True)
diff --git a/test/functional/mempool_dust.py b/test/functional/mempool_dust.py
index f4e385a112..e0c026207a 100755
--- a/test/functional/mempool_dust.py
+++ b/test/functional/mempool_dust.py
@@ -40,6 +40,7 @@ DUST_RELAY_TX_FEE = 3000 # default setting [sat/kvB]
class DustRelayFeeTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 1
+ self.extra_args = [['-permitbaremultisig']]
def test_dust_output(self, node: TestNode, dust_relay_fee: Decimal,
output_script: CScript, type_desc: str) -> None:
@@ -101,7 +102,7 @@ class DustRelayFeeTest(BitcoinTestFramework):
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])
+ self.restart_node(0, extra_args=[dust_parameter, "-permitbaremultisig"])
for output_script, description in output_scripts:
self.test_dust_output(self.nodes[0], dustfee_btc_kvb, output_script, description)
diff --git a/test/functional/mempool_limit.py b/test/functional/mempool_limit.py
index a1147f70f3..6215610c31 100755
--- a/test/functional/mempool_limit.py
+++ b/test/functional/mempool_limit.py
@@ -125,8 +125,9 @@ class MempoolLimitTest(BitcoinTestFramework):
utxo_to_spend=tx_B["new_utxo"],
confirmed_only=True
)
-
- assert_raises_rpc_error(-26, "too-long-mempool-chain", node.submitpackage, [tx_B["hex"], tx_C["hex"]])
+ res = node.submitpackage([tx_B["hex"], tx_C["hex"]])
+ assert_equal(res["package_msg"], "transaction failed")
+ assert "too-long-mempool-chain" in res["tx-results"][tx_C["wtxid"]]["error"]
def test_mid_package_eviction(self):
node = self.nodes[0]
@@ -205,7 +206,7 @@ class MempoolLimitTest(BitcoinTestFramework):
# Package should be submitted, temporarily exceeding maxmempool, and then evicted.
with node.assert_debug_log(expected_msgs=["rolling minimum fee bumped"]):
- assert_raises_rpc_error(-26, "mempool full", node.submitpackage, package_hex)
+ assert_equal(node.submitpackage(package_hex)["package_msg"], "transaction failed")
# Maximum size must never be exceeded.
assert_greater_than(node.getmempoolinfo()["maxmempool"], node.getmempoolinfo()["bytes"])
@@ -273,7 +274,9 @@ class MempoolLimitTest(BitcoinTestFramework):
package_hex = [cpfp_parent["hex"], replacement_tx["hex"], child["hex"]]
# Package should be submitted, temporarily exceeding maxmempool, and then evicted.
- assert_raises_rpc_error(-26, "bad-txns-inputs-missingorspent", node.submitpackage, package_hex)
+ res = node.submitpackage(package_hex)
+ assert_equal(res["package_msg"], "transaction failed")
+ assert len([tx_res for _, tx_res in res["tx-results"].items() if "error" in tx_res and tx_res["error"] == "bad-txns-inputs-missingorspent"])
# Maximum size must never be exceeded.
assert_greater_than(node.getmempoolinfo()["maxmempool"], node.getmempoolinfo()["bytes"])
@@ -321,6 +324,7 @@ class MempoolLimitTest(BitcoinTestFramework):
package_txns.append(tx_child)
submitpackage_result = node.submitpackage([tx["hex"] for tx in package_txns])
+ assert_equal(submitpackage_result["package_msg"], "success")
rich_parent_result = submitpackage_result["tx-results"][tx_rich["wtxid"]]
poor_parent_result = submitpackage_result["tx-results"][tx_poor["wtxid"]]
@@ -366,7 +370,9 @@ class MempoolLimitTest(BitcoinTestFramework):
assert_greater_than(worst_feerate_btcvb, (parent_fee + child_fee) / (tx_parent_just_below["tx"].get_vsize() + tx_child_just_above["tx"].get_vsize()))
assert_greater_than(mempoolmin_feerate, (parent_fee) / (tx_parent_just_below["tx"].get_vsize()))
assert_greater_than((parent_fee + child_fee) / (tx_parent_just_below["tx"].get_vsize() + tx_child_just_above["tx"].get_vsize()), mempoolmin_feerate / 1000)
- assert_raises_rpc_error(-26, "mempool full", node.submitpackage, [tx_parent_just_below["hex"], tx_child_just_above["hex"]])
+ res = node.submitpackage([tx_parent_just_below["hex"], tx_child_just_above["hex"]])
+ for wtxid in [tx_parent_just_below["wtxid"], tx_child_just_above["wtxid"]]:
+ assert_equal(res["tx-results"][wtxid]["error"], "mempool full")
self.log.info('Test passing a value below the minimum (5 MB) to -maxmempool throws an error')
self.stop_node(0)
diff --git a/test/functional/mempool_sigoplimit.py b/test/functional/mempool_sigoplimit.py
index fbec6d0dc8..2e7850fb40 100755
--- a/test/functional/mempool_sigoplimit.py
+++ b/test/functional/mempool_sigoplimit.py
@@ -34,7 +34,6 @@ from test_framework.util import (
assert_equal,
assert_greater_than,
assert_greater_than_or_equal,
- assert_raises_rpc_error,
)
from test_framework.wallet import MiniWallet
from test_framework.wallet_util import generate_keypair
@@ -140,7 +139,7 @@ class BytesPerSigOpTest(BitcoinTestFramework):
self.log.info("Test a overly-large sigops-vbyte hits package limits")
# Make a 2-transaction package which fails vbyte checks even though
# separately they would work.
- self.restart_node(0, extra_args=["-bytespersigop=5000"] + self.extra_args[0])
+ self.restart_node(0, extra_args=["-bytespersigop=5000","-permitbaremultisig=1"] + self.extra_args[0])
def create_bare_multisig_tx(utxo_to_spend=None):
_, pubkey = generate_keypair()
@@ -169,7 +168,8 @@ class BytesPerSigOpTest(BitcoinTestFramework):
assert_equal([x["package-error"] for x in packet_test], ["package-mempool-limits", "package-mempool-limits"])
# When we actually try to submit, the parent makes it into the mempool, but the child would exceed ancestor vsize limits
- assert_raises_rpc_error(-26, "too-long-mempool-chain", self.nodes[0].submitpackage, [tx_parent.serialize().hex(), tx_child.serialize().hex()])
+ res = self.nodes[0].submitpackage([tx_parent.serialize().hex(), tx_child.serialize().hex()])
+ assert "too-long-mempool-chain" in res["tx-results"][tx_child.getwtxid()]["error"]
assert tx_parent.rehash() in self.nodes[0].getrawmempool()
# Transactions are tiny in weight
diff --git a/test/functional/p2p_addr_relay.py b/test/functional/p2p_addr_relay.py
index 63cd10896d..2adcaf178c 100755
--- a/test/functional/p2p_addr_relay.py
+++ b/test/functional/p2p_addr_relay.py
@@ -270,15 +270,16 @@ class AddrTest(BitcoinTestFramework):
full_outbound_peer.sync_with_ping()
assert full_outbound_peer.getaddr_received()
- self.log.info('Check that we do not send a getaddr message upon connecting to a block-relay-only peer')
+ self.log.info('Check that we do not send a getaddr message to a block-relay-only or inbound peer')
block_relay_peer = self.nodes[0].add_outbound_p2p_connection(AddrReceiver(), p2p_idx=1, connection_type="block-relay-only")
block_relay_peer.sync_with_ping()
assert_equal(block_relay_peer.getaddr_received(), False)
- self.log.info('Check that we answer getaddr messages only from inbound peers')
inbound_peer = self.nodes[0].add_p2p_connection(AddrReceiver(send_getaddr=False))
inbound_peer.sync_with_ping()
+ assert_equal(inbound_peer.getaddr_received(), False)
+ self.log.info('Check that we answer getaddr messages only from inbound peers')
# Add some addresses to addrman
for i in range(1000):
first_octet = i >> 8
diff --git a/test/functional/p2p_filter.py b/test/functional/p2p_filter.py
index 665f57365f..62d55cc101 100755
--- a/test/functional/p2p_filter.py
+++ b/test/functional/p2p_filter.py
@@ -11,6 +11,7 @@ from test_framework.messages import (
COIN,
MAX_BLOOM_FILTER_SIZE,
MAX_BLOOM_HASH_FUNCS,
+ MSG_WTX,
MSG_BLOCK,
MSG_FILTERED_BLOCK,
msg_filteradd,
@@ -135,14 +136,22 @@ class FilterTest(BitcoinTestFramework):
self.log.info("Check that a node with bloom filters enabled services p2p mempool messages")
filter_peer = P2PBloomFilter()
- self.log.debug("Create a tx relevant to the peer before connecting")
- txid = self.wallet.send_to(from_node=self.nodes[0], scriptPubKey=filter_peer.watch_script_pubkey, amount=9 * COIN)["txid"]
+ self.log.info("Create two tx before connecting, one relevant to the node another that is not")
+ rel_txid = self.wallet.send_to(from_node=self.nodes[0], scriptPubKey=filter_peer.watch_script_pubkey, amount=1 * COIN)["txid"]
+ irr_result = self.wallet.send_to(from_node=self.nodes[0], scriptPubKey=getnewdestination()[1], amount=2 * COIN)
+ irr_txid = irr_result["txid"]
+ irr_wtxid = irr_result["wtxid"]
- self.log.debug("Send a mempool msg after connecting and check that the tx is received")
+ self.log.info("Send a mempool msg after connecting and check that the relevant tx is announced")
self.nodes[0].add_p2p_connection(filter_peer)
filter_peer.send_and_ping(filter_peer.watch_filter_init)
filter_peer.send_message(msg_mempool())
- filter_peer.wait_for_tx(txid)
+ filter_peer.wait_for_tx(rel_txid)
+
+ self.log.info("Request the irrelevant transaction even though it was not announced")
+ filter_peer.send_message(msg_getdata([CInv(t=MSG_WTX, h=int(irr_wtxid, 16))]))
+ self.log.info("We should get it anyway because it was in the mempool on connection to peer")
+ filter_peer.wait_for_tx(irr_txid)
def test_frelay_false(self, filter_peer):
self.log.info("Check that a node with fRelay set to false does not receive invs until the filter is set")
diff --git a/test/functional/p2p_invalid_messages.py b/test/functional/p2p_invalid_messages.py
index 2fb88b828f..4916d36ab7 100755
--- a/test/functional/p2p_invalid_messages.py
+++ b/test/functional/p2p_invalid_messages.py
@@ -216,7 +216,7 @@ class InvalidMessagesTest(BitcoinTestFramework):
self.test_addrv2('unrecognized network',
[
'received: addrv2 (25 bytes)',
- '9.9.9.9:8333 mapped',
+ '9.9.9.9:8333',
'Added 1 addresses',
],
bytes.fromhex(
diff --git a/test/functional/p2p_net_deadlock.py b/test/functional/p2p_net_deadlock.py
index f69fe52146..1a357b944b 100755
--- a/test/functional/p2p_net_deadlock.py
+++ b/test/functional/p2p_net_deadlock.py
@@ -5,7 +5,7 @@
import threading
from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import random_bytes
+from random import randbytes
class NetDeadlockTest(BitcoinTestFramework):
@@ -18,7 +18,7 @@ class NetDeadlockTest(BitcoinTestFramework):
node1 = self.nodes[1]
self.log.info("Simultaneously send a large message on both sides")
- rand_msg = random_bytes(4000000).hex()
+ rand_msg = randbytes(4000000).hex()
thread0 = threading.Thread(target=node0.sendmsgtopeer, args=(0, "unknown", rand_msg))
thread1 = threading.Thread(target=node1.sendmsgtopeer, args=(0, "unknown", rand_msg))
diff --git a/test/functional/p2p_node_network_limited.py b/test/functional/p2p_node_network_limited.py
index a56afbcf7b..89c35e943b 100755
--- a/test/functional/p2p_node_network_limited.py
+++ b/test/functional/p2p_node_network_limited.py
@@ -8,7 +8,15 @@ Tests that a node configured with -prune=550 signals NODE_NETWORK_LIMITED correc
and that it responds to getdata requests for blocks correctly:
- send a block within 288 + 2 of the tip
- disconnect peers who request blocks older than that."""
-from test_framework.messages import CInv, MSG_BLOCK, msg_getdata, msg_verack, NODE_NETWORK_LIMITED, NODE_WITNESS
+from test_framework.messages import (
+ CInv,
+ MSG_BLOCK,
+ NODE_NETWORK_LIMITED,
+ NODE_P2P_V2,
+ NODE_WITNESS,
+ msg_getdata,
+ msg_verack,
+)
from test_framework.p2p import P2PInterface
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
@@ -50,6 +58,8 @@ class NodeNetworkLimitedTest(BitcoinTestFramework):
node = self.nodes[0].add_p2p_connection(P2PIgnoreInv())
expected_services = NODE_WITNESS | NODE_NETWORK_LIMITED
+ if self.options.v2transport:
+ expected_services |= NODE_P2P_V2
self.log.info("Check that node has signalled expected services.")
assert_equal(node.nServices, expected_services)
diff --git a/test/functional/p2p_timeouts.py b/test/functional/p2p_timeouts.py
index a308577c02..b4fa5099d8 100755
--- a/test/functional/p2p_timeouts.py
+++ b/test/functional/p2p_timeouts.py
@@ -68,11 +68,14 @@ class TimeoutsTest(BitcoinTestFramework):
with self.nodes[0].assert_debug_log(['Unsupported message "ping" prior to verack from peer=0']):
no_verack_node.send_message(msg_ping())
- with self.nodes[0].assert_debug_log(['non-version message before version handshake. Message "ping" from peer=1']):
- no_version_node.send_message(msg_ping())
- self.mock_forward(1)
+ # With v2, non-version messages before the handshake would be interpreted as part of the key exchange.
+ # Therefore, don't execute this part of the test if v2transport is chosen.
+ if not self.options.v2transport:
+ with self.nodes[0].assert_debug_log(['non-version message before version handshake. Message "ping" from peer=1']):
+ no_version_node.send_message(msg_ping())
+ self.mock_forward(1)
assert "version" in no_verack_node.last_message
assert no_verack_node.is_connected
@@ -80,11 +83,12 @@ class TimeoutsTest(BitcoinTestFramework):
assert no_send_node.is_connected
no_verack_node.send_message(msg_ping())
- no_version_node.send_message(msg_ping())
+ if not self.options.v2transport:
+ no_version_node.send_message(msg_ping())
expected_timeout_logs = [
"version handshake timeout peer=0",
- "socket no message in first 3 seconds, 1 0 peer=1",
+ f"socket no message in first 3 seconds, {'0' if self.options.v2transport else '1'} 0 peer=1",
"socket no message in first 3 seconds, 0 0 peer=2",
]
@@ -100,5 +104,6 @@ class TimeoutsTest(BitcoinTestFramework):
extra_args=['-peertimeout=0'],
)
+
if __name__ == '__main__':
TimeoutsTest().main()
diff --git a/test/functional/p2p_v2_transport.py b/test/functional/p2p_v2_transport.py
index 5ad2194b84..72d22cb77f 100755
--- a/test/functional/p2p_v2_transport.py
+++ b/test/functional/p2p_v2_transport.py
@@ -48,7 +48,7 @@ class V2TransportTest(BitcoinTestFramework):
assert_equal(self.nodes[1].getblockcount(), 5)
# verify there is a v2 connection between node 0 and 1
node_0_info = self.nodes[0].getpeerinfo()
- node_1_info = self.nodes[0].getpeerinfo()
+ node_1_info = self.nodes[1].getpeerinfo()
assert_equal(len(node_0_info), 1)
assert_equal(len(node_1_info), 1)
assert_equal(node_0_info[0]["transport_protocol_type"], "v2")
@@ -133,9 +133,8 @@ class V2TransportTest(BitcoinTestFramework):
V1_PREFIX = MAGIC_BYTES["regtest"] + b"version\x00\x00\x00\x00\x00"
assert_equal(len(V1_PREFIX), 16)
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
- num_peers = len(self.nodes[0].getpeerinfo())
- s.connect(("127.0.0.1", p2p_port(0)))
- self.wait_until(lambda: len(self.nodes[0].getpeerinfo()) == num_peers + 1)
+ with self.nodes[0].wait_for_new_peer():
+ s.connect(("127.0.0.1", p2p_port(0)))
s.sendall(V1_PREFIX[:-1])
assert_equal(self.nodes[0].getpeerinfo()[-1]["transport_protocol_type"], "detecting")
s.sendall(bytes([V1_PREFIX[-1]])) # send out last prefix byte
@@ -144,22 +143,23 @@ class V2TransportTest(BitcoinTestFramework):
# Check wrong network prefix detection (hits if the next 12 bytes correspond to a v1 version message)
wrong_network_magic_prefix = MAGIC_BYTES["signet"] + V1_PREFIX[4:]
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
- s.connect(("127.0.0.1", p2p_port(0)))
+ with self.nodes[0].wait_for_new_peer():
+ s.connect(("127.0.0.1", p2p_port(0)))
with self.nodes[0].assert_debug_log(["V2 transport error: V1 peer with wrong MessageStart"]):
s.sendall(wrong_network_magic_prefix + b"somepayload")
# Check detection of missing garbage terminator (hits after fixed amount of data if terminator never matches garbage)
MAX_KEY_GARB_AND_GARBTERM_LEN = 64 + 4095 + 16
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
- num_peers = len(self.nodes[0].getpeerinfo())
- s.connect(("127.0.0.1", p2p_port(0)))
- self.wait_until(lambda: len(self.nodes[0].getpeerinfo()) == num_peers + 1)
+ with self.nodes[0].wait_for_new_peer():
+ s.connect(("127.0.0.1", p2p_port(0)))
s.sendall(b'\x00' * (MAX_KEY_GARB_AND_GARBTERM_LEN - 1))
self.wait_until(lambda: self.nodes[0].getpeerinfo()[-1]["bytesrecv"] == MAX_KEY_GARB_AND_GARBTERM_LEN - 1)
with self.nodes[0].assert_debug_log(["V2 transport error: missing garbage terminator"]):
+ peer_id = self.nodes[0].getpeerinfo()[-1]["id"]
s.sendall(b'\x00') # send out last byte
# should disconnect immediately
- self.wait_until(lambda: len(self.nodes[0].getpeerinfo()) == num_peers)
+ self.wait_until(lambda: not peer_id in [p["id"] for p in self.nodes[0].getpeerinfo()])
if __name__ == '__main__':
diff --git a/test/functional/rpc_blockchain.py b/test/functional/rpc_blockchain.py
index 53163720bb..9b7743cafa 100755
--- a/test/functional/rpc_blockchain.py
+++ b/test/functional/rpc_blockchain.py
@@ -58,6 +58,7 @@ TIME_RANGE_STEP = 600 # ten-minute steps
TIME_RANGE_MTP = TIME_GENESIS_BLOCK + (HEIGHT - 6) * TIME_RANGE_STEP
TIME_RANGE_TIP = TIME_GENESIS_BLOCK + (HEIGHT - 1) * TIME_RANGE_STEP
TIME_RANGE_END = TIME_GENESIS_BLOCK + HEIGHT * TIME_RANGE_STEP
+DIFFICULTY_ADJUSTMENT_INTERVAL = 2016
class BlockchainTest(BitcoinTestFramework):
@@ -436,7 +437,6 @@ 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("""
@@ -448,9 +448,47 @@ class BlockchainTest(BitcoinTestFramework):
""").strip(),
lambda: self.nodes[0].getnetworkhashps("a", []),
)
+ assert_raises_rpc_error(
+ -8,
+ "Block does not exist at specified height",
+ lambda: self.nodes[0].getnetworkhashps(100, self.nodes[0].getblockcount() + 1),
+ )
+ assert_raises_rpc_error(
+ -8,
+ "Block does not exist at specified height",
+ lambda: self.nodes[0].getnetworkhashps(100, -10),
+ )
+ assert_raises_rpc_error(
+ -8,
+ "Invalid nblocks. Must be a positive number or -1.",
+ lambda: self.nodes[0].getnetworkhashps(-100),
+ )
+ assert_raises_rpc_error(
+ -8,
+ "Invalid nblocks. Must be a positive number or -1.",
+ lambda: self.nodes[0].getnetworkhashps(0),
+ )
+
+ # Genesis block height estimate should return 0
+ hashes_per_second = self.nodes[0].getnetworkhashps(100, 0)
+ assert_equal(hashes_per_second, 0)
+
# This should be 2 hashes every 10 minutes or 1/300
+ hashes_per_second = self.nodes[0].getnetworkhashps()
assert abs(hashes_per_second * 300 - 1) < 0.0001
+ # Test setting the first param of getnetworkhashps to -1 returns the average network
+ # hashes per second from the last difficulty change.
+ current_block_height = self.nodes[0].getmininginfo()['blocks']
+ blocks_since_last_diff_change = current_block_height % DIFFICULTY_ADJUSTMENT_INTERVAL + 1
+ expected_hashes_per_second_since_diff_change = self.nodes[0].getnetworkhashps(blocks_since_last_diff_change)
+
+ assert_equal(self.nodes[0].getnetworkhashps(-1), expected_hashes_per_second_since_diff_change)
+
+ # Ensure long lookups get truncated to chain length
+ hashes_per_second = self.nodes[0].getnetworkhashps(self.nodes[0].getblockcount() + 1000)
+ assert hashes_per_second > 0.003
+
def _test_stopatheight(self):
self.log.info("Test stopping at height")
assert_equal(self.nodes[0].getblockcount(), HEIGHT)
diff --git a/test/functional/rpc_net.py b/test/functional/rpc_net.py
index 50a022fc7e..e1820b0f55 100755
--- a/test/functional/rpc_net.py
+++ b/test/functional/rpc_net.py
@@ -9,6 +9,7 @@ Tests correspond to code in rpc/net.cpp.
from decimal import Decimal
from itertools import product
+import platform
import time
import test_framework.messages
@@ -110,7 +111,7 @@ class NetTest(BitcoinTestFramework):
no_version_peer_id = 2
no_version_peer_conntime = int(time.time())
self.nodes[0].setmocktime(no_version_peer_conntime)
- with self.nodes[0].assert_debug_log([f"Added connection peer={no_version_peer_id}"]):
+ with self.nodes[0].wait_for_new_peer():
no_version_peer = self.nodes[0].add_p2p_connection(P2PInterface(), send_version=False, wait_for_verack=False)
self.nodes[0].setmocktime(0)
peer_info = self.nodes[0].getpeerinfo()[no_version_peer_id]
@@ -150,7 +151,7 @@ class NetTest(BitcoinTestFramework):
"synced_blocks": -1,
"synced_headers": -1,
"timeoffset": 0,
- "transport_protocol_type": "v1",
+ "transport_protocol_type": "v1" if not self.options.v2transport else "detecting",
"version": 0,
},
)
@@ -160,19 +161,23 @@ class NetTest(BitcoinTestFramework):
def test_getnettotals(self):
self.log.info("Test getnettotals")
# Test getnettotals and getpeerinfo by doing a ping. The bytes
- # sent/received should increase by at least the size of one ping (32
- # bytes) and one pong (32 bytes).
+ # sent/received should increase by at least the size of one ping
+ # and one pong. Both have a payload size of 8 bytes, but the total
+ # size depends on the used p2p version:
+ # - p2p v1: 24 bytes (header) + 8 bytes (payload) = 32 bytes
+ # - p2p v2: 21 bytes (header/tag with short-id) + 8 bytes (payload) = 29 bytes
+ ping_size = 32 if not self.options.v2transport else 29
net_totals_before = self.nodes[0].getnettotals()
peer_info_before = self.nodes[0].getpeerinfo()
self.nodes[0].ping()
- self.wait_until(lambda: (self.nodes[0].getnettotals()['totalbytessent'] >= net_totals_before['totalbytessent'] + 32 * 2), timeout=1)
- self.wait_until(lambda: (self.nodes[0].getnettotals()['totalbytesrecv'] >= net_totals_before['totalbytesrecv'] + 32 * 2), timeout=1)
+ self.wait_until(lambda: (self.nodes[0].getnettotals()['totalbytessent'] >= net_totals_before['totalbytessent'] + ping_size * 2), timeout=1)
+ self.wait_until(lambda: (self.nodes[0].getnettotals()['totalbytesrecv'] >= net_totals_before['totalbytesrecv'] + ping_size * 2), timeout=1)
for peer_before in peer_info_before:
peer_after = lambda: next(p for p in self.nodes[0].getpeerinfo() if p['id'] == peer_before['id'])
- self.wait_until(lambda: peer_after()['bytesrecv_per_msg'].get('pong', 0) >= peer_before['bytesrecv_per_msg'].get('pong', 0) + 32, timeout=1)
- self.wait_until(lambda: peer_after()['bytessent_per_msg'].get('ping', 0) >= peer_before['bytessent_per_msg'].get('ping', 0) + 32, timeout=1)
+ self.wait_until(lambda: peer_after()['bytesrecv_per_msg'].get('pong', 0) >= peer_before['bytesrecv_per_msg'].get('pong', 0) + ping_size, timeout=1)
+ self.wait_until(lambda: peer_after()['bytessent_per_msg'].get('ping', 0) >= peer_before['bytessent_per_msg'].get('ping', 0) + ping_size, timeout=1)
def test_getnetworkinfo(self):
self.log.info("Test getnetworkinfo")
@@ -215,8 +220,13 @@ class NetTest(BitcoinTestFramework):
# add a node (node2) to node0
ip_port = "127.0.0.1:{}".format(p2p_port(2))
self.nodes[0].addnode(node=ip_port, command='add')
+ # try to add an equivalent ip
+ # (note that OpenBSD doesn't support the IPv4 shorthand notation with omitted zero-bytes)
+ if platform.system() != "OpenBSD":
+ ip_port2 = "127.1:{}".format(p2p_port(2))
+ assert_raises_rpc_error(-23, "Node already added", self.nodes[0].addnode, node=ip_port2, command='add')
# check that the node has indeed been added
- added_nodes = self.nodes[0].getaddednodeinfo(ip_port)
+ added_nodes = self.nodes[0].getaddednodeinfo()
assert_equal(len(added_nodes), 1)
assert_equal(added_nodes[0]['addednode'], ip_port)
# check that node cannot be added again
@@ -342,7 +352,10 @@ class NetTest(BitcoinTestFramework):
node = self.nodes[0]
self.restart_node(0)
- self.connect_nodes(0, 1)
+ # we want to use a p2p v1 connection here in order to ensure
+ # a peer id of zero (a downgrade from v2 to v1 would lead
+ # to an increase of the peer id)
+ self.connect_nodes(0, 1, peer_advertises_v2=False)
self.log.info("Test sendmsgtopeer")
self.log.debug("Send a valid message")
diff --git a/test/functional/rpc_packages.py b/test/functional/rpc_packages.py
index 5644a9f5a8..664f2df3f1 100755
--- a/test/functional/rpc_packages.py
+++ b/test/functional/rpc_packages.py
@@ -304,6 +304,7 @@ class RPCPackagesTest(BitcoinTestFramework):
submitpackage_result = node.submitpackage(package=[tx["hex"] for tx in package_txns])
# Check that each result is present, with the correct size and fees
+ assert_equal(submitpackage_result["package_msg"], "success")
for package_txn in package_txns:
tx = package_txn["tx"]
assert tx.getwtxid() in submitpackage_result["tx-results"]
@@ -334,9 +335,26 @@ 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 = [t["hex"] for t in self.wallet.create_self_transfer_chain(chain_length=25)]
+ legacy_pool = node.getrawmempool()
+ chain_hex = [t["hex"] for t in self.wallet.create_self_transfer_chain(chain_length=3)]
assert_raises_rpc_error(-25, "package topology disallowed", node.submitpackage, chain_hex)
+ assert_equal(legacy_pool, node.getrawmempool())
+ # Create a transaction chain such as only the parent gets accepted (by making the child's
+ # version non-standard). Make sure the parent does get broadcast.
+ self.log.info("If a package is partially submitted, transactions included in mempool get broadcast")
+ peer = node.add_p2p_connection(P2PTxInvStore())
+ txs = self.wallet.create_self_transfer_chain(chain_length=2)
+ bad_child = tx_from_hex(txs[1]["hex"])
+ bad_child.nVersion = -1
+ hex_partial_acceptance = [txs[0]["hex"], bad_child.serialize().hex()]
+ res = node.submitpackage(hex_partial_acceptance)
+ assert_equal(res["package_msg"], "transaction failed")
+ first_wtxid = txs[0]["tx"].getwtxid()
+ assert "error" not in res["tx-results"][first_wtxid]
+ sec_wtxid = bad_child.getwtxid()
+ assert_equal(res["tx-results"][sec_wtxid]["error"], "version")
+ peer.wait_for_broadcast([first_wtxid])
if __name__ == "__main__":
RPCPackagesTest().main()
diff --git a/test/functional/rpc_psbt.py b/test/functional/rpc_psbt.py
index 60df48f025..1fd938d18a 100755
--- a/test/functional/rpc_psbt.py
+++ b/test/functional/rpc_psbt.py
@@ -6,6 +6,7 @@
"""
from decimal import Decimal
from itertools import product
+from random import randbytes
from test_framework.descriptors import descsum_create
from test_framework.key import H_POINT
@@ -38,9 +39,7 @@ from test_framework.util import (
assert_greater_than,
assert_greater_than_or_equal,
assert_raises_rpc_error,
- find_output,
find_vout_for_address,
- random_bytes,
)
from test_framework.wallet_util import (
generate_keypair,
@@ -417,16 +416,17 @@ class PSBTTest(BitcoinTestFramework):
self.nodes[0].converttopsbt(hexstring=signedtx['hex'], permitsigdata=True)
# Create outputs to nodes 1 and 2
+ # (note that we intentionally create two different txs here, as we want
+ # to check that each node is missing prevout data for one of the two
+ # utxos, see "should only have data for one input" test below)
node1_addr = self.nodes[1].getnewaddress()
node2_addr = self.nodes[2].getnewaddress()
- txid1 = self.nodes[0].sendtoaddress(node1_addr, 13)
- txid2 = self.nodes[0].sendtoaddress(node2_addr, 13)
- blockhash = self.generate(self.nodes[0], 6)[0]
- vout1 = find_output(self.nodes[1], txid1, 13, blockhash=blockhash)
- vout2 = find_output(self.nodes[2], txid2, 13, blockhash=blockhash)
+ utxo1 = self.create_outpoints(self.nodes[0], outputs=[{node1_addr: 13}])[0]
+ utxo2 = self.create_outpoints(self.nodes[0], outputs=[{node2_addr: 13}])[0]
+ self.generate(self.nodes[0], 6)[0]
# Create a psbt spending outputs from nodes 1 and 2
- psbt_orig = self.nodes[0].createpsbt([{"txid":txid1, "vout":vout1}, {"txid":txid2, "vout":vout2}], {self.nodes[0].getnewaddress():25.999})
+ psbt_orig = self.nodes[0].createpsbt([utxo1, utxo2], {self.nodes[0].getnewaddress():25.999})
# Update psbts, should only have data for one input and not the other
psbt1 = self.nodes[1].walletprocesspsbt(psbt_orig, False, "ALL")['psbt']
@@ -603,14 +603,9 @@ class PSBTTest(BitcoinTestFramework):
# Send to all types of addresses
addr1 = self.nodes[1].getnewaddress("", "bech32")
- txid1 = self.nodes[0].sendtoaddress(addr1, 11)
- vout1 = find_output(self.nodes[0], txid1, 11)
addr2 = self.nodes[1].getnewaddress("", "legacy")
- txid2 = self.nodes[0].sendtoaddress(addr2, 11)
- vout2 = find_output(self.nodes[0], txid2, 11)
addr3 = self.nodes[1].getnewaddress("", "p2sh-segwit")
- txid3 = self.nodes[0].sendtoaddress(addr3, 11)
- vout3 = find_output(self.nodes[0], txid3, 11)
+ utxo1, utxo2, utxo3 = self.create_outpoints(self.nodes[1], outputs=[{addr1: 11}, {addr2: 11}, {addr3: 11}])
self.sync_all()
def test_psbt_input_keys(psbt_input, keys):
@@ -618,7 +613,7 @@ class PSBTTest(BitcoinTestFramework):
assert_equal(set(keys), set(psbt_input.keys()))
# Create a PSBT. None of the inputs are filled initially
- psbt = self.nodes[1].createpsbt([{"txid":txid1, "vout":vout1},{"txid":txid2, "vout":vout2},{"txid":txid3, "vout":vout3}], {self.nodes[0].getnewaddress():32.999})
+ psbt = self.nodes[1].createpsbt([utxo1, utxo2, utxo3], {self.nodes[0].getnewaddress():32.999})
decoded = self.nodes[1].decodepsbt(psbt)
test_psbt_input_keys(decoded['inputs'][0], [])
test_psbt_input_keys(decoded['inputs'][1], [])
@@ -641,15 +636,14 @@ class PSBTTest(BitcoinTestFramework):
test_psbt_input_keys(decoded['inputs'][2], ['non_witness_utxo','witness_utxo', 'bip32_derivs', 'redeem_script'])
# Two PSBTs with a common input should not be joinable
- psbt1 = self.nodes[1].createpsbt([{"txid":txid1, "vout":vout1}], {self.nodes[0].getnewaddress():Decimal('10.999')})
+ psbt1 = self.nodes[1].createpsbt([utxo1], {self.nodes[0].getnewaddress():Decimal('10.999')})
assert_raises_rpc_error(-8, "exists in multiple PSBTs", self.nodes[1].joinpsbts, [psbt1, updated])
# Join two distinct PSBTs
addr4 = self.nodes[1].getnewaddress("", "p2sh-segwit")
- txid4 = self.nodes[0].sendtoaddress(addr4, 5)
- vout4 = find_output(self.nodes[0], txid4, 5)
+ utxo4 = self.create_outpoints(self.nodes[0], outputs=[{addr4: 5}])[0]
self.generate(self.nodes[0], 6)
- psbt2 = self.nodes[1].createpsbt([{"txid":txid4, "vout":vout4}], {self.nodes[0].getnewaddress():Decimal('4.999')})
+ psbt2 = self.nodes[1].createpsbt([utxo4], {self.nodes[0].getnewaddress():Decimal('4.999')})
psbt2 = self.nodes[1].walletprocesspsbt(psbt2)['psbt']
psbt2_decoded = self.nodes[0].decodepsbt(psbt2)
assert "final_scriptwitness" in psbt2_decoded['inputs'][0] and "final_scriptSig" in psbt2_decoded['inputs'][0]
@@ -669,11 +663,10 @@ class PSBTTest(BitcoinTestFramework):
# Newly created PSBT needs UTXOs and updating
addr = self.nodes[1].getnewaddress("", "p2sh-segwit")
- txid = self.nodes[0].sendtoaddress(addr, 7)
+ utxo = self.create_outpoints(self.nodes[0], outputs=[{addr: 7}])[0]
addrinfo = self.nodes[1].getaddressinfo(addr)
- blockhash = self.generate(self.nodes[0], 6)[0]
- vout = find_output(self.nodes[0], txid, 7, blockhash=blockhash)
- psbt = self.nodes[1].createpsbt([{"txid":txid, "vout":vout}], {self.nodes[0].getnewaddress("", "p2sh-segwit"):Decimal('6.999')})
+ self.generate(self.nodes[0], 6)[0]
+ psbt = self.nodes[1].createpsbt([utxo], {self.nodes[0].getnewaddress("", "p2sh-segwit"):Decimal('6.999')})
analyzed = self.nodes[0].analyzepsbt(psbt)
assert not analyzed['inputs'][0]['has_utxo'] and not analyzed['inputs'][0]['is_final'] and analyzed['inputs'][0]['next'] == 'updater' and analyzed['next'] == 'updater'
@@ -872,9 +865,8 @@ class PSBTTest(BitcoinTestFramework):
self.log.info("Test that walletprocesspsbt both updates and signs a non-updated psbt containing Taproot inputs")
addr = self.nodes[0].getnewaddress("", "bech32m")
- txid = self.nodes[0].sendtoaddress(addr, 1)
- vout = find_vout_for_address(self.nodes[0], txid, addr)
- psbt = self.nodes[0].createpsbt([{"txid": txid, "vout": vout}], [{self.nodes[0].getnewaddress(): 0.9999}])
+ utxo = self.create_outpoints(self.nodes[0], outputs=[{addr: 1}])[0]
+ psbt = self.nodes[0].createpsbt([utxo], [{self.nodes[0].getnewaddress(): 0.9999}])
signed = self.nodes[0].walletprocesspsbt(psbt)
rawtx = signed["hex"]
self.nodes[0].sendrawtransaction(rawtx)
@@ -893,10 +885,10 @@ class PSBTTest(BitcoinTestFramework):
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)
- hash_sha256, preimage_sha256 = random_bytes(32), random_bytes(50)
- hash_hash160, preimage_hash160 = random_bytes(20), random_bytes(50)
- hash_hash256, preimage_hash256 = random_bytes(32), random_bytes(50)
+ hash_ripemd160, preimage_ripemd160 = randbytes(20), randbytes(50)
+ hash_sha256, preimage_sha256 = randbytes(32), randbytes(50)
+ hash_hash160, preimage_hash160 = randbytes(20), randbytes(50)
+ hash_hash256, preimage_hash256 = randbytes(32), randbytes(50)
tx = CTransaction()
tx.vin = [CTxIn(outpoint=COutPoint(hash=int('aa' * 32, 16), n=0), scriptSig=b""),
@@ -962,11 +954,10 @@ class PSBTTest(BitcoinTestFramework):
descriptor = descsum_create(f"wpkh({key})")
- txid = self.nodes[0].sendtoaddress(address, 1)
+ utxo = self.create_outpoints(self.nodes[0], outputs=[{address: 1}])[0]
self.sync_all()
- vout = find_output(self.nodes[0], txid, 1)
- psbt = self.nodes[2].createpsbt([{"txid": txid, "vout": vout}], {self.nodes[0].getnewaddress(): 0.99999})
+ psbt = self.nodes[2].createpsbt([utxo], {self.nodes[0].getnewaddress(): 0.99999})
decoded = self.nodes[2].decodepsbt(psbt)
test_psbt_input_keys(decoded['inputs'][0], [])
diff --git a/test/functional/rpc_rawtransaction.py b/test/functional/rpc_rawtransaction.py
index 2395935620..c12865b5e3 100755
--- a/test/functional/rpc_rawtransaction.py
+++ b/test/functional/rpc_rawtransaction.py
@@ -32,6 +32,7 @@ from test_framework.script import (
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
+ assert_greater_than,
assert_raises_rpc_error,
)
from test_framework.wallet import (
@@ -70,7 +71,7 @@ class RawTransactionsTest(BitcoinTestFramework):
self.extra_args = [
["-txindex"],
["-txindex"],
- [],
+ ["-fastprune", "-prune=1"],
]
# whitelist all peers to speed up tx relay / mempool sync
for args in self.extra_args:
@@ -85,7 +86,6 @@ class RawTransactionsTest(BitcoinTestFramework):
self.wallet = MiniWallet(self.nodes[0])
self.getrawtransaction_tests()
- self.getrawtransaction_verbosity_tests()
self.createrawtransaction_tests()
self.sendrawtransaction_tests()
self.sendrawtransaction_testmempoolaccept_tests()
@@ -94,6 +94,8 @@ class RawTransactionsTest(BitcoinTestFramework):
if self.is_specified_wallet_compiled() and not self.options.descriptors:
self.import_deterministic_coinbase_privkeys()
self.raw_multisig_transaction_legacy_tests()
+ self.getrawtransaction_verbosity_tests()
+
def getrawtransaction_tests(self):
tx = self.wallet.send_self_transfer(from_node=self.nodes[0])
@@ -243,6 +245,13 @@ class RawTransactionsTest(BitcoinTestFramework):
coin_base = self.nodes[1].getblock(block1)['tx'][0]
gottx = self.nodes[1].getrawtransaction(txid=coin_base, verbosity=2, blockhash=block1)
assert 'fee' not in gottx
+ # check that verbosity 2 for a mempool tx will fallback to verbosity 1
+ # Do this with a pruned chain, as a regression test for https://github.com/bitcoin/bitcoin/pull/29003
+ self.generate(self.nodes[2], 400)
+ assert_greater_than(self.nodes[2].pruneblockchain(250), 0)
+ mempool_tx = self.wallet.send_self_transfer(from_node=self.nodes[2])['txid']
+ gottx = self.nodes[2].getrawtransaction(txid=mempool_tx, verbosity=2)
+ assert 'fee' not in gottx
def createrawtransaction_tests(self):
self.log.info("Test createrawtransaction")
diff --git a/test/functional/test_framework/blockfilter.py b/test/functional/test_framework/blockfilter.py
index a30e37ea5b..a16aa3d34f 100644
--- a/test/functional/test_framework/blockfilter.py
+++ b/test/functional/test_framework/blockfilter.py
@@ -4,7 +4,7 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Helper routines relevant for compact block filters (BIP158).
"""
-from .siphash import siphash
+from .crypto.siphash import siphash
def bip158_basic_element_hash(script_pub_key, N, block_hash):
@@ -29,7 +29,7 @@ def bip158_basic_element_hash(script_pub_key, N, block_hash):
def bip158_relevant_scriptpubkeys(node, block_hash):
- """ Determines the basic filter relvant scriptPubKeys as defined in BIP158:
+ """ Determines the basic filter relevant scriptPubKeys as defined in BIP158:
'A basic filter MUST contain exactly the following items for each transaction in a block:
- The previous output script (the script being spent) for each input, except for
diff --git a/test/functional/test_framework/crypto/bip324_cipher.py b/test/functional/test_framework/crypto/bip324_cipher.py
new file mode 100644
index 0000000000..56190647f2
--- /dev/null
+++ b/test/functional/test_framework/crypto/bip324_cipher.py
@@ -0,0 +1,201 @@
+#!/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-only implementation of ChaCha20 Poly1305 AEAD Construction in RFC 8439 and FSChaCha20Poly1305 for BIP 324
+
+It is designed for ease of understanding, not performance.
+
+WARNING: This code is slow and trivially vulnerable to side channel attacks. Do not use for
+anything but tests.
+"""
+
+import unittest
+
+from .chacha20 import chacha20_block, REKEY_INTERVAL
+from .poly1305 import Poly1305
+
+
+def pad16(x):
+ if len(x) % 16 == 0:
+ return b''
+ return b'\x00' * (16 - (len(x) % 16))
+
+
+def aead_chacha20_poly1305_encrypt(key, nonce, aad, plaintext):
+ """Encrypt a plaintext using ChaCha20Poly1305."""
+ ret = bytearray()
+ msg_len = len(plaintext)
+ for i in range((msg_len + 63) // 64):
+ now = min(64, msg_len - 64 * i)
+ keystream = chacha20_block(key, nonce, i + 1)
+ for j in range(now):
+ ret.append(plaintext[j + 64 * i] ^ keystream[j])
+ poly1305 = Poly1305(chacha20_block(key, nonce, 0)[:32])
+ mac_data = aad + pad16(aad)
+ mac_data += ret + pad16(ret)
+ mac_data += len(aad).to_bytes(8, 'little') + msg_len.to_bytes(8, 'little')
+ ret += poly1305.tag(mac_data)
+ return bytes(ret)
+
+
+def aead_chacha20_poly1305_decrypt(key, nonce, aad, ciphertext):
+ """Decrypt a ChaCha20Poly1305 ciphertext."""
+ if len(ciphertext) < 16:
+ return None
+ msg_len = len(ciphertext) - 16
+ poly1305 = Poly1305(chacha20_block(key, nonce, 0)[:32])
+ mac_data = aad + pad16(aad)
+ mac_data += ciphertext[:-16] + pad16(ciphertext[:-16])
+ mac_data += len(aad).to_bytes(8, 'little') + msg_len.to_bytes(8, 'little')
+ if ciphertext[-16:] != poly1305.tag(mac_data):
+ return None
+ ret = bytearray()
+ for i in range((msg_len + 63) // 64):
+ now = min(64, msg_len - 64 * i)
+ keystream = chacha20_block(key, nonce, i + 1)
+ for j in range(now):
+ ret.append(ciphertext[j + 64 * i] ^ keystream[j])
+ return bytes(ret)
+
+
+class FSChaCha20Poly1305:
+ """Rekeying wrapper AEAD around ChaCha20Poly1305."""
+ def __init__(self, initial_key):
+ self._key = initial_key
+ self._packet_counter = 0
+
+ def _crypt(self, aad, text, is_decrypt):
+ nonce = ((self._packet_counter % REKEY_INTERVAL).to_bytes(4, 'little') +
+ (self._packet_counter // REKEY_INTERVAL).to_bytes(8, 'little'))
+ if is_decrypt:
+ ret = aead_chacha20_poly1305_decrypt(self._key, nonce, aad, text)
+ else:
+ ret = aead_chacha20_poly1305_encrypt(self._key, nonce, aad, text)
+ if (self._packet_counter + 1) % REKEY_INTERVAL == 0:
+ rekey_nonce = b"\xFF\xFF\xFF\xFF" + nonce[4:]
+ self._key = aead_chacha20_poly1305_encrypt(self._key, rekey_nonce, b"", b"\x00" * 32)[:32]
+ self._packet_counter += 1
+ return ret
+
+ def decrypt(self, aad, ciphertext):
+ return self._crypt(aad, ciphertext, True)
+
+ def encrypt(self, aad, plaintext):
+ return self._crypt(aad, plaintext, False)
+
+
+# Test vectors from RFC8439 consisting of plaintext, aad, 32 byte key, 12 byte nonce and ciphertext
+AEAD_TESTS = [
+ # RFC 8439 Example from section 2.8.2
+ ["4c616469657320616e642047656e746c656d656e206f662074686520636c6173"
+ "73206f66202739393a204966204920636f756c64206f6666657220796f75206f"
+ "6e6c79206f6e652074697020666f7220746865206675747572652c2073756e73"
+ "637265656e20776f756c642062652069742e",
+ "50515253c0c1c2c3c4c5c6c7",
+ "808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f",
+ [7, 0x4746454443424140],
+ "d31a8d34648e60db7b86afbc53ef7ec2a4aded51296e08fea9e2b5a736ee62d6"
+ "3dbea45e8ca9671282fafb69da92728b1a71de0a9e060b2905d6a5b67ecd3b36"
+ "92ddbd7f2d778b8c9803aee328091b58fab324e4fad675945585808b4831d7bc"
+ "3ff4def08e4b7a9de576d26586cec64b61161ae10b594f09e26a7e902ecbd060"
+ "0691"],
+ # RFC 8439 Test vector A.5
+ ["496e7465726e65742d4472616674732061726520647261667420646f63756d65"
+ "6e74732076616c696420666f722061206d6178696d756d206f6620736978206d"
+ "6f6e74687320616e64206d617920626520757064617465642c207265706c6163"
+ "65642c206f72206f62736f6c65746564206279206f7468657220646f63756d65"
+ "6e747320617420616e792074696d652e20497420697320696e617070726f7072"
+ "6961746520746f2075736520496e7465726e65742d4472616674732061732072"
+ "65666572656e6365206d6174657269616c206f7220746f206369746520746865"
+ "6d206f74686572207468616e206173202fe2809c776f726b20696e2070726f67"
+ "726573732e2fe2809d",
+ "f33388860000000000004e91",
+ "1c9240a5eb55d38af333888604f6b5f0473917c1402b80099dca5cbc207075c0",
+ [0, 0x0807060504030201],
+ "64a0861575861af460f062c79be643bd5e805cfd345cf389f108670ac76c8cb2"
+ "4c6cfc18755d43eea09ee94e382d26b0bdb7b73c321b0100d4f03b7f355894cf"
+ "332f830e710b97ce98c8a84abd0b948114ad176e008d33bd60f982b1ff37c855"
+ "9797a06ef4f0ef61c186324e2b3506383606907b6a7c02b0f9f6157b53c867e4"
+ "b9166c767b804d46a59b5216cde7a4e99040c5a40433225ee282a1b0a06c523e"
+ "af4534d7f83fa1155b0047718cbc546a0d072b04b3564eea1b422273f548271a"
+ "0bb2316053fa76991955ebd63159434ecebb4e466dae5a1073a6727627097a10"
+ "49e617d91d361094fa68f0ff77987130305beaba2eda04df997b714d6c6f2c29"
+ "a6ad5cb4022b02709beead9d67890cbb22392336fea1851f38"],
+ # Test vectors exercising aad and plaintext which are multiples of 16 bytes.
+ ["8d2d6a8befd9716fab35819eaac83b33269afb9f1a00fddf66095a6c0cd91951"
+ "a6b7ad3db580be0674c3f0b55f618e34",
+ "",
+ "72ddc73f07101282bbbcf853b9012a9f9695fc5d36b303a97fd0845d0314e0c3",
+ [0x3432b75f, 0xb3585537eb7f4024],
+ "f760b8224fb2a317b1b07875092606131232a5b86ae142df5df1c846a7f6341a"
+ "f2564483dd77f836be45e6230808ffe402a6f0a3e8be074b3d1f4ea8a7b09451"],
+ ["",
+ "36970d8a704c065de16250c18033de5a400520ac1b5842b24551e5823a3314f3"
+ "946285171e04a81ebfbe3566e312e74ab80e94c7dd2ff4e10de0098a58d0f503",
+ "77adda51d6730b9ad6c995658cbd49f581b2547e7c0c08fcc24ceec797461021",
+ [0x1f90da88, 0x75dafa3ef84471a4],
+ "aaae5bb81e8407c94b2ae86ae0c7efbe"],
+]
+
+FSAEAD_TESTS = [
+ ["d6a4cb04ef0f7c09c1866ed29dc24d820e75b0491032a51b4c3366f9ca35c19e"
+ "a3047ec6be9d45f9637b63e1cf9eb4c2523a5aab7b851ebeba87199db0e839cf"
+ "0d5c25e50168306377aedbe9089fd2463ded88b83211cf51b73b150608cc7a60"
+ "0d0f11b9a742948482e1b109d8faf15b450aa7322e892fa2208c6691e3fecf4c"
+ "711191b14d75a72147",
+ "786cb9b6ebf44288974cf0",
+ "5c9e1c3951a74fba66708bf9d2c217571684556b6a6a3573bff2847d38612654",
+ 500,
+ "9dcebbd3281ea3dd8e9a1ef7d55a97abd6743e56ebc0c190cb2c4e14160b385e"
+ "0bf508dddf754bd02c7c208447c131ce23e47a4a14dfaf5dd8bc601323950f75"
+ "4e05d46e9232f83fc5120fbbef6f5347a826ec79a93820718d4ec7a2b7cfaaa4"
+ "4b21e16d726448b62f803811aff4f6d827ed78e738ce8a507b81a8ae13131192"
+ "8039213de18a5120dc9b7370baca878f50ff254418de3da50c"],
+ ["8349b7a2690b63d01204800c288ff1138a1d473c832c90ea8b3fc102d0bb3adc"
+ "44261b247c7c3d6760bfbe979d061c305f46d94c0582ac3099f0bf249f8cb234",
+ "",
+ "3bd2093fcbcb0d034d8c569583c5425c1a53171ea299f8cc3bbf9ae3530adfce",
+ 60000,
+ "30a6757ff8439b975363f166a0fa0e36722ab35936abd704297948f45083f4d4"
+ "99433137ce931f7fca28a0acd3bc30f57b550acbc21cbd45bbef0739d9caf30c"
+ "14b94829deb27f0b1923a2af704ae5d6"],
+]
+
+
+class TestFrameworkAEAD(unittest.TestCase):
+ def test_aead(self):
+ """ChaCha20Poly1305 AEAD test vectors."""
+ for test_vector in AEAD_TESTS:
+ hex_plain, hex_aad, hex_key, hex_nonce, hex_cipher = test_vector
+ plain = bytes.fromhex(hex_plain)
+ aad = bytes.fromhex(hex_aad)
+ key = bytes.fromhex(hex_key)
+ nonce = hex_nonce[0].to_bytes(4, 'little') + hex_nonce[1].to_bytes(8, 'little')
+
+ ciphertext = aead_chacha20_poly1305_encrypt(key, nonce, aad, plain)
+ self.assertEqual(hex_cipher, ciphertext.hex())
+ plaintext = aead_chacha20_poly1305_decrypt(key, nonce, aad, ciphertext)
+ self.assertEqual(plain, plaintext)
+
+ def test_fschacha20poly1305aead(self):
+ "FSChaCha20Poly1305 AEAD test vectors."
+ for test_vector in FSAEAD_TESTS:
+ hex_plain, hex_aad, hex_key, msg_idx, hex_cipher = test_vector
+ plain = bytes.fromhex(hex_plain)
+ aad = bytes.fromhex(hex_aad)
+ key = bytes.fromhex(hex_key)
+
+ enc_aead = FSChaCha20Poly1305(key)
+ dec_aead = FSChaCha20Poly1305(key)
+
+ for _ in range(msg_idx):
+ enc_aead.encrypt(b"", b"")
+ ciphertext = enc_aead.encrypt(aad, plain)
+ self.assertEqual(hex_cipher, ciphertext.hex())
+
+ for _ in range(msg_idx):
+ dec_aead.decrypt(b"", bytes(16))
+ plaintext = dec_aead.decrypt(aad, ciphertext)
+ self.assertEqual(plain, plaintext)
diff --git a/test/functional/test_framework/crypto/chacha20.py b/test/functional/test_framework/crypto/chacha20.py
new file mode 100644
index 0000000000..19b6698dfb
--- /dev/null
+++ b/test/functional/test_framework/crypto/chacha20.py
@@ -0,0 +1,162 @@
+#!/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-only implementation of ChaCha20 cipher and FSChaCha20 for BIP 324
+
+It is designed for ease of understanding, not performance.
+
+WARNING: This code is slow and trivially vulnerable to side channel attacks. Do not use for
+anything but tests.
+"""
+
+import unittest
+
+CHACHA20_INDICES = (
+ (0, 4, 8, 12), (1, 5, 9, 13), (2, 6, 10, 14), (3, 7, 11, 15),
+ (0, 5, 10, 15), (1, 6, 11, 12), (2, 7, 8, 13), (3, 4, 9, 14)
+)
+
+CHACHA20_CONSTANTS = (0x61707865, 0x3320646e, 0x79622d32, 0x6b206574)
+REKEY_INTERVAL = 224 # packets
+
+
+def rotl32(v, bits):
+ """Rotate the 32-bit value v left by bits bits."""
+ bits %= 32 # Make sure the term below does not throw an exception
+ return ((v << bits) & 0xffffffff) | (v >> (32 - bits))
+
+
+def chacha20_doubleround(s):
+ """Apply a ChaCha20 double round to 16-element state array s.
+ See https://cr.yp.to/chacha/chacha-20080128.pdf and https://tools.ietf.org/html/rfc8439
+ """
+ for a, b, c, d in CHACHA20_INDICES:
+ s[a] = (s[a] + s[b]) & 0xffffffff
+ s[d] = rotl32(s[d] ^ s[a], 16)
+ s[c] = (s[c] + s[d]) & 0xffffffff
+ s[b] = rotl32(s[b] ^ s[c], 12)
+ s[a] = (s[a] + s[b]) & 0xffffffff
+ s[d] = rotl32(s[d] ^ s[a], 8)
+ s[c] = (s[c] + s[d]) & 0xffffffff
+ s[b] = rotl32(s[b] ^ s[c], 7)
+
+
+def chacha20_block(key, nonce, cnt):
+ """Compute the 64-byte output of the ChaCha20 block function.
+ Takes as input a 32-byte key, 12-byte nonce, and 32-bit integer counter.
+ """
+ # Initial state.
+ init = [0] * 16
+ init[:4] = CHACHA20_CONSTANTS[:4]
+ init[4:12] = [int.from_bytes(key[i:i+4], 'little') for i in range(0, 32, 4)]
+ init[12] = cnt
+ init[13:16] = [int.from_bytes(nonce[i:i+4], 'little') for i in range(0, 12, 4)]
+ # Perform 20 rounds.
+ state = list(init)
+ for _ in range(10):
+ chacha20_doubleround(state)
+ # Add initial values back into state.
+ for i in range(16):
+ state[i] = (state[i] + init[i]) & 0xffffffff
+ # Produce byte output
+ return b''.join(state[i].to_bytes(4, 'little') for i in range(16))
+
+class FSChaCha20:
+ """Rekeying wrapper stream cipher around ChaCha20."""
+ def __init__(self, initial_key, rekey_interval=REKEY_INTERVAL):
+ self._key = initial_key
+ self._rekey_interval = rekey_interval
+ self._block_counter = 0
+ self._chunk_counter = 0
+ self._keystream = b''
+
+ def _get_keystream_bytes(self, nbytes):
+ while len(self._keystream) < nbytes:
+ nonce = ((0).to_bytes(4, 'little') + (self._chunk_counter // self._rekey_interval).to_bytes(8, 'little'))
+ self._keystream += chacha20_block(self._key, nonce, self._block_counter)
+ self._block_counter += 1
+ ret = self._keystream[:nbytes]
+ self._keystream = self._keystream[nbytes:]
+ return ret
+
+ def crypt(self, chunk):
+ ks = self._get_keystream_bytes(len(chunk))
+ ret = bytes([ks[i] ^ chunk[i] for i in range(len(chunk))])
+ if ((self._chunk_counter + 1) % self._rekey_interval) == 0:
+ self._key = self._get_keystream_bytes(32)
+ self._block_counter = 0
+ self._keystream = b''
+ self._chunk_counter += 1
+ return ret
+
+
+# Test vectors from RFC7539/8439 consisting of 32 byte key, 12 byte nonce, block counter
+# and 64 byte output after applying `chacha20_block` function
+CHACHA20_TESTS = [
+ ["000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", [0x09000000, 0x4a000000], 1,
+ "10f1e7e4d13b5915500fdd1fa32071c4c7d1f4c733c068030422aa9ac3d46c4e"
+ "d2826446079faa0914c2d705d98b02a2b5129cd1de164eb9cbd083e8a2503c4e"],
+ ["0000000000000000000000000000000000000000000000000000000000000000", [0, 0], 0,
+ "76b8e0ada0f13d90405d6ae55386bd28bdd219b8a08ded1aa836efcc8b770dc7"
+ "da41597c5157488d7724e03fb8d84a376a43b8f41518a11cc387b669b2ee6586"],
+ ["0000000000000000000000000000000000000000000000000000000000000000", [0, 0], 1,
+ "9f07e7be5551387a98ba977c732d080dcb0f29a048e3656912c6533e32ee7aed"
+ "29b721769ce64e43d57133b074d839d531ed1f28510afb45ace10a1f4b794d6f"],
+ ["0000000000000000000000000000000000000000000000000000000000000001", [0, 0], 1,
+ "3aeb5224ecf849929b9d828db1ced4dd832025e8018b8160b82284f3c949aa5a"
+ "8eca00bbb4a73bdad192b5c42f73f2fd4e273644c8b36125a64addeb006c13a0"],
+ ["00ff000000000000000000000000000000000000000000000000000000000000", [0, 0], 2,
+ "72d54dfbf12ec44b362692df94137f328fea8da73990265ec1bbbea1ae9af0ca"
+ "13b25aa26cb4a648cb9b9d1be65b2c0924a66c54d545ec1b7374f4872e99f096"],
+ ["0000000000000000000000000000000000000000000000000000000000000000", [0, 0x200000000000000], 0,
+ "c2c64d378cd536374ae204b9ef933fcd1a8b2288b3dfa49672ab765b54ee27c7"
+ "8a970e0e955c14f3a88e741b97c286f75f8fc299e8148362fa198a39531bed6d"],
+ ["000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", [0, 0x4a000000], 1,
+ "224f51f3401bd9e12fde276fb8631ded8c131f823d2c06e27e4fcaec9ef3cf78"
+ "8a3b0aa372600a92b57974cded2b9334794cba40c63e34cdea212c4cf07d41b7"],
+ ["0000000000000000000000000000000000000000000000000000000000000001", [0, 0], 0,
+ "4540f05a9f1fb296d7736e7b208e3c96eb4fe1834688d2604f450952ed432d41"
+ "bbe2a0b6ea7566d2a5d1e7e20d42af2c53d792b1c43fea817e9ad275ae546963"],
+ ["0000000000000000000000000000000000000000000000000000000000000000", [0, 1], 0,
+ "ef3fdfd6c61578fbf5cf35bd3dd33b8009631634d21e42ac33960bd138e50d32"
+ "111e4caf237ee53ca8ad6426194a88545ddc497a0b466e7d6bbdb0041b2f586b"],
+ ["000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", [0, 0x0706050403020100], 0,
+ "f798a189f195e66982105ffb640bb7757f579da31602fc93ec01ac56f85ac3c1"
+ "34a4547b733b46413042c9440049176905d3be59ea1c53f15916155c2be8241a"],
+]
+
+FSCHACHA20_TESTS = [
+ ["000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f",
+ "0000000000000000000000000000000000000000000000000000000000000000", 256,
+ "a93df4ef03011f3db95f60d996e1785df5de38fc39bfcb663a47bb5561928349"],
+ ["01", "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", 5, "ea"],
+ ["e93fdb5c762804b9a706816aca31e35b11d2aa3080108ef46a5b1f1508819c0a",
+ "8ec4c3ccdaea336bdeb245636970be01266509b33f3d2642504eaf412206207a", 4096,
+ "8bfaa4eacff308fdb4a94a5ff25bd9d0c1f84b77f81239f67ff39d6e1ac280c9"],
+]
+
+
+class TestFrameworkChacha(unittest.TestCase):
+ def test_chacha20(self):
+ """ChaCha20 test vectors."""
+ for test_vector in CHACHA20_TESTS:
+ hex_key, nonce, counter, hex_output = test_vector
+ key = bytes.fromhex(hex_key)
+ nonce_bytes = nonce[0].to_bytes(4, 'little') + nonce[1].to_bytes(8, 'little')
+ keystream = chacha20_block(key, nonce_bytes, counter)
+ self.assertEqual(hex_output, keystream.hex())
+
+ def test_fschacha20(self):
+ """FSChaCha20 test vectors."""
+ for test_vector in FSCHACHA20_TESTS:
+ hex_plaintext, hex_key, rekey_interval, hex_ciphertext_after_rotation = test_vector
+ plaintext = bytes.fromhex(hex_plaintext)
+ key = bytes.fromhex(hex_key)
+ fsc20 = FSChaCha20(key, rekey_interval)
+ for _ in range(rekey_interval):
+ fsc20.crypt(plaintext)
+
+ ciphertext = fsc20.crypt(plaintext)
+ self.assertEqual(hex_ciphertext_after_rotation, ciphertext.hex())
diff --git a/test/functional/test_framework/ellswift.py b/test/functional/test_framework/crypto/ellswift.py
index 97b10118e6..429b7b9f4d 100644
--- a/test/functional/test_framework/ellswift.py
+++ b/test/functional/test_framework/crypto/ellswift.py
@@ -12,7 +12,7 @@ import os
import random
import unittest
-from test_framework.secp256k1 import FE, G, GE
+from test_framework.crypto.secp256k1 import FE, G, GE
# Precomputed constant square root of -3 (mod p).
MINUS_3_SQRT = FE(-3).sqrt()
diff --git a/test/functional/test_framework/ellswift_decode_test_vectors.csv b/test/functional/test_framework/crypto/ellswift_decode_test_vectors.csv
index 1bab96b721..1bab96b721 100644
--- a/test/functional/test_framework/ellswift_decode_test_vectors.csv
+++ b/test/functional/test_framework/crypto/ellswift_decode_test_vectors.csv
diff --git a/test/functional/test_framework/crypto/hkdf.py b/test/functional/test_framework/crypto/hkdf.py
new file mode 100644
index 0000000000..7e8958733c
--- /dev/null
+++ b/test/functional/test_framework/crypto/hkdf.py
@@ -0,0 +1,33 @@
+#!/usr/bin/env python3
+# Copyright (c) 2023 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-only HKDF-SHA256 implementation
+
+It is designed for ease of understanding, not performance.
+
+WARNING: This code is slow and trivially vulnerable to side channel attacks. Do not use for
+anything but tests.
+"""
+
+import hashlib
+import hmac
+
+
+def hmac_sha256(key, data):
+ """Compute HMAC-SHA256 from specified byte arrays key and data."""
+ return hmac.new(key, data, hashlib.sha256).digest()
+
+
+def hkdf_sha256(length, ikm, salt, info):
+ """Derive a key using HKDF-SHA256."""
+ if len(salt) == 0:
+ salt = bytes([0] * 32)
+ prk = hmac_sha256(salt, ikm)
+ t = b""
+ okm = b""
+ for i in range((length + 32 - 1) // 32):
+ t = hmac_sha256(prk, t + info + bytes([i + 1]))
+ okm += t
+ return okm[:length]
diff --git a/test/functional/test_framework/crypto/muhash.py b/test/functional/test_framework/crypto/muhash.py
new file mode 100644
index 0000000000..09241f6203
--- /dev/null
+++ b/test/functional/test_framework/crypto/muhash.py
@@ -0,0 +1,55 @@
+# Copyright (c) 2020 Pieter Wuille
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+"""Native Python MuHash3072 implementation."""
+
+import hashlib
+import unittest
+
+from .chacha20 import chacha20_block
+
+def data_to_num3072(data):
+ """Hash a 32-byte array data to a 3072-bit number using 6 Chacha20 operations."""
+ bytes384 = b""
+ for counter in range(6):
+ bytes384 += chacha20_block(data, bytes(12), counter)
+ return int.from_bytes(bytes384, 'little')
+
+class MuHash3072:
+ """Class representing the MuHash3072 computation of a set.
+
+ See https://cseweb.ucsd.edu/~mihir/papers/inchash.pdf and https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2017-May/014337.html
+ """
+
+ MODULUS = 2**3072 - 1103717
+
+ def __init__(self):
+ """Initialize for an empty set."""
+ self.numerator = 1
+ self.denominator = 1
+
+ def insert(self, data):
+ """Insert a byte array data in the set."""
+ data_hash = hashlib.sha256(data).digest()
+ self.numerator = (self.numerator * data_to_num3072(data_hash)) % self.MODULUS
+
+ def remove(self, data):
+ """Remove a byte array from the set."""
+ data_hash = hashlib.sha256(data).digest()
+ self.denominator = (self.denominator * data_to_num3072(data_hash)) % self.MODULUS
+
+ def digest(self):
+ """Extract the final hash. Does not modify this object."""
+ val = (self.numerator * pow(self.denominator, -1, self.MODULUS)) % self.MODULUS
+ bytes384 = val.to_bytes(384, 'little')
+ return hashlib.sha256(bytes384).digest()
+
+class TestFrameworkMuhash(unittest.TestCase):
+ def test_muhash(self):
+ muhash = MuHash3072()
+ muhash.insert(b'\x00' * 32)
+ muhash.insert((b'\x01' + b'\x00' * 31))
+ muhash.remove((b'\x02' + b'\x00' * 31))
+ finalized = muhash.digest()
+ # This mirrors the result in the C++ MuHash3072 unit test
+ self.assertEqual(finalized[::-1].hex(), "10d312b100cbd32ada024a6646e40d3482fcff103668d2625f10002a607d5863")
diff --git a/test/functional/test_framework/crypto/poly1305.py b/test/functional/test_framework/crypto/poly1305.py
new file mode 100644
index 0000000000..967b90254d
--- /dev/null
+++ b/test/functional/test_framework/crypto/poly1305.py
@@ -0,0 +1,104 @@
+#!/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-only implementation of Poly1305 authenticator
+
+It is designed for ease of understanding, not performance.
+
+WARNING: This code is slow and trivially vulnerable to side channel attacks. Do not use for
+anything but tests.
+"""
+
+import unittest
+
+
+class Poly1305:
+ """Class representing a running poly1305 computation."""
+ MODULUS = 2**130 - 5
+
+ def __init__(self, key):
+ self.r = int.from_bytes(key[:16], 'little') & 0xffffffc0ffffffc0ffffffc0fffffff
+ self.s = int.from_bytes(key[16:], 'little')
+
+ def tag(self, data):
+ """Compute the poly1305 tag."""
+ acc, length = 0, len(data)
+ for i in range((length + 15) // 16):
+ chunk = data[i * 16:min(length, (i + 1) * 16)]
+ val = int.from_bytes(chunk, 'little') + 256**len(chunk)
+ acc = (self.r * (acc + val)) % Poly1305.MODULUS
+ return ((acc + self.s) & 0xffffffffffffffffffffffffffffffff).to_bytes(16, 'little')
+
+
+# Test vectors from RFC7539/8439 consisting of message to be authenticated, 32 byte key and computed 16 byte tag
+POLY1305_TESTS = [
+ # RFC 7539, section 2.5.2.
+ ["43727970746f6772617068696320466f72756d2052657365617263682047726f7570",
+ "85d6be7857556d337f4452fe42d506a80103808afb0db2fd4abff6af4149f51b",
+ "a8061dc1305136c6c22b8baf0c0127a9"],
+ # RFC 7539, section A.3.
+ ["00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
+ "000000000000000000000000000",
+ "0000000000000000000000000000000000000000000000000000000000000000",
+ "00000000000000000000000000000000"],
+ ["416e79207375626d697373696f6e20746f20746865204945544620696e74656e6465642062792074686520436f6e747269627"
+ "5746f7220666f72207075626c69636174696f6e20617320616c6c206f722070617274206f6620616e204945544620496e7465"
+ "726e65742d4472616674206f722052464320616e6420616e792073746174656d656e74206d6164652077697468696e2074686"
+ "520636f6e74657874206f6620616e204945544620616374697669747920697320636f6e7369646572656420616e2022494554"
+ "4620436f6e747269627574696f6e222e20537563682073746174656d656e747320696e636c756465206f72616c20737461746"
+ "56d656e747320696e20494554462073657373696f6e732c2061732077656c6c206173207772697474656e20616e6420656c65"
+ "6374726f6e696320636f6d6d756e69636174696f6e73206d61646520617420616e792074696d65206f7220706c6163652c207"
+ "768696368206172652061646472657373656420746f",
+ "0000000000000000000000000000000036e5f6b5c5e06070f0efca96227a863e",
+ "36e5f6b5c5e06070f0efca96227a863e"],
+ ["416e79207375626d697373696f6e20746f20746865204945544620696e74656e6465642062792074686520436f6e747269627"
+ "5746f7220666f72207075626c69636174696f6e20617320616c6c206f722070617274206f6620616e204945544620496e7465"
+ "726e65742d4472616674206f722052464320616e6420616e792073746174656d656e74206d6164652077697468696e2074686"
+ "520636f6e74657874206f6620616e204945544620616374697669747920697320636f6e7369646572656420616e2022494554"
+ "4620436f6e747269627574696f6e222e20537563682073746174656d656e747320696e636c756465206f72616c20737461746"
+ "56d656e747320696e20494554462073657373696f6e732c2061732077656c6c206173207772697474656e20616e6420656c65"
+ "6374726f6e696320636f6d6d756e69636174696f6e73206d61646520617420616e792074696d65206f7220706c6163652c207"
+ "768696368206172652061646472657373656420746f",
+ "36e5f6b5c5e06070f0efca96227a863e00000000000000000000000000000000",
+ "f3477e7cd95417af89a6b8794c310cf0"],
+ ["2754776173206272696c6c69672c20616e642074686520736c6974687920746f7665730a446964206779726520616e6420676"
+ "96d626c6520696e2074686520776162653a0a416c6c206d696d737920776572652074686520626f726f676f7665732c0a416e"
+ "6420746865206d6f6d65207261746873206f757467726162652e",
+ "1c9240a5eb55d38af333888604f6b5f0473917c1402b80099dca5cbc207075c0",
+ "4541669a7eaaee61e708dc7cbcc5eb62"],
+ ["ffffffffffffffffffffffffffffffff",
+ "0200000000000000000000000000000000000000000000000000000000000000",
+ "03000000000000000000000000000000"],
+ ["02000000000000000000000000000000",
+ "02000000000000000000000000000000ffffffffffffffffffffffffffffffff",
+ "03000000000000000000000000000000"],
+ ["fffffffffffffffffffffffffffffffff0ffffffffffffffffffffffffffffff11000000000000000000000000000000",
+ "0100000000000000000000000000000000000000000000000000000000000000",
+ "05000000000000000000000000000000"],
+ ["fffffffffffffffffffffffffffffffffbfefefefefefefefefefefefefefefe01010101010101010101010101010101",
+ "0100000000000000000000000000000000000000000000000000000000000000",
+ "00000000000000000000000000000000"],
+ ["fdffffffffffffffffffffffffffffff",
+ "0200000000000000000000000000000000000000000000000000000000000000",
+ "faffffffffffffffffffffffffffffff"],
+ ["e33594d7505e43b900000000000000003394d7505e4379cd01000000000000000000000000000000000000000000000001000000000000000000000000000000",
+ "0100000000000000040000000000000000000000000000000000000000000000",
+ "14000000000000005500000000000000"],
+ ["e33594d7505e43b900000000000000003394d7505e4379cd010000000000000000000000000000000000000000000000",
+ "0100000000000000040000000000000000000000000000000000000000000000",
+ "13000000000000000000000000000000"],
+]
+
+
+class TestFrameworkPoly1305(unittest.TestCase):
+ def test_poly1305(self):
+ """Poly1305 test vectors."""
+ for test_vector in POLY1305_TESTS:
+ hex_message, hex_key, hex_tag = test_vector
+ message = bytes.fromhex(hex_message)
+ key = bytes.fromhex(hex_key)
+ tag = bytes.fromhex(hex_tag)
+ comp_tag = Poly1305(key).tag(message)
+ self.assertEqual(tag, comp_tag)
diff --git a/test/functional/test_framework/ripemd160.py b/test/functional/test_framework/crypto/ripemd160.py
index 12801364b4..12801364b4 100644
--- a/test/functional/test_framework/ripemd160.py
+++ b/test/functional/test_framework/crypto/ripemd160.py
diff --git a/test/functional/test_framework/secp256k1.py b/test/functional/test_framework/crypto/secp256k1.py
index 2e9e419da5..2e9e419da5 100644
--- a/test/functional/test_framework/secp256k1.py
+++ b/test/functional/test_framework/crypto/secp256k1.py
diff --git a/test/functional/test_framework/siphash.py b/test/functional/test_framework/crypto/siphash.py
index bd13b2c948..bd13b2c948 100644
--- a/test/functional/test_framework/siphash.py
+++ b/test/functional/test_framework/crypto/siphash.py
diff --git a/test/functional/test_framework/xswiftec_inv_test_vectors.csv b/test/functional/test_framework/crypto/xswiftec_inv_test_vectors.csv
index 138c4cf85c..138c4cf85c 100644
--- a/test/functional/test_framework/xswiftec_inv_test_vectors.csv
+++ b/test/functional/test_framework/crypto/xswiftec_inv_test_vectors.csv
diff --git a/test/functional/test_framework/key.py b/test/functional/test_framework/key.py
index 6c1892539f..06252f8996 100644
--- a/test/functional/test_framework/key.py
+++ b/test/functional/test_framework/key.py
@@ -13,7 +13,7 @@ import os
import random
import unittest
-from test_framework import secp256k1
+from test_framework.crypto import secp256k1
# Point with no known discrete log.
H_POINT = "50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0"
diff --git a/test/functional/test_framework/messages.py b/test/functional/test_framework/messages.py
index 8f3aea8785..d008cb39aa 100755
--- a/test/functional/test_framework/messages.py
+++ b/test/functional/test_framework/messages.py
@@ -29,7 +29,7 @@ import struct
import time
import unittest
-from test_framework.siphash import siphash256
+from test_framework.crypto.siphash import siphash256
from test_framework.util import assert_equal
MAX_LOCATOR_SZ = 101
diff --git a/test/functional/test_framework/muhash.py b/test/functional/test_framework/muhash.py
deleted file mode 100644
index 0d96114e3e..0000000000
--- a/test/functional/test_framework/muhash.py
+++ /dev/null
@@ -1,110 +0,0 @@
-# Copyright (c) 2020 Pieter Wuille
-# Distributed under the MIT software license, see the accompanying
-# file COPYING or http://www.opensource.org/licenses/mit-license.php.
-"""Native Python MuHash3072 implementation."""
-
-import hashlib
-import unittest
-
-def rot32(v, bits):
- """Rotate the 32-bit value v left by bits bits."""
- bits %= 32 # Make sure the term below does not throw an exception
- return ((v << bits) & 0xffffffff) | (v >> (32 - bits))
-
-def chacha20_doubleround(s):
- """Apply a ChaCha20 double round to 16-element state array s.
-
- See https://cr.yp.to/chacha/chacha-20080128.pdf and https://tools.ietf.org/html/rfc8439
- """
- QUARTER_ROUNDS = [(0, 4, 8, 12),
- (1, 5, 9, 13),
- (2, 6, 10, 14),
- (3, 7, 11, 15),
- (0, 5, 10, 15),
- (1, 6, 11, 12),
- (2, 7, 8, 13),
- (3, 4, 9, 14)]
-
- for a, b, c, d in QUARTER_ROUNDS:
- s[a] = (s[a] + s[b]) & 0xffffffff
- s[d] = rot32(s[d] ^ s[a], 16)
- s[c] = (s[c] + s[d]) & 0xffffffff
- s[b] = rot32(s[b] ^ s[c], 12)
- s[a] = (s[a] + s[b]) & 0xffffffff
- s[d] = rot32(s[d] ^ s[a], 8)
- s[c] = (s[c] + s[d]) & 0xffffffff
- s[b] = rot32(s[b] ^ s[c], 7)
-
-def chacha20_32_to_384(key32):
- """Specialized ChaCha20 implementation with 32-byte key, 0 IV, 384-byte output."""
- # See RFC 8439 section 2.3 for chacha20 parameters
- CONSTANTS = [0x61707865, 0x3320646e, 0x79622d32, 0x6b206574]
-
- key_bytes = [0]*8
- for i in range(8):
- key_bytes[i] = int.from_bytes(key32[(4 * i):(4 * (i+1))], 'little')
-
- INITIALIZATION_VECTOR = [0] * 4
- init = CONSTANTS + key_bytes + INITIALIZATION_VECTOR
- out = bytearray()
- for counter in range(6):
- init[12] = counter
- s = init.copy()
- for _ in range(10):
- chacha20_doubleround(s)
- for i in range(16):
- out.extend(((s[i] + init[i]) & 0xffffffff).to_bytes(4, 'little'))
- return bytes(out)
-
-def data_to_num3072(data):
- """Hash a 32-byte array data to a 3072-bit number using 6 Chacha20 operations."""
- bytes384 = chacha20_32_to_384(data)
- return int.from_bytes(bytes384, 'little')
-
-class MuHash3072:
- """Class representing the MuHash3072 computation of a set.
-
- See https://cseweb.ucsd.edu/~mihir/papers/inchash.pdf and https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2017-May/014337.html
- """
-
- MODULUS = 2**3072 - 1103717
-
- def __init__(self):
- """Initialize for an empty set."""
- self.numerator = 1
- self.denominator = 1
-
- def insert(self, data):
- """Insert a byte array data in the set."""
- data_hash = hashlib.sha256(data).digest()
- self.numerator = (self.numerator * data_to_num3072(data_hash)) % self.MODULUS
-
- def remove(self, data):
- """Remove a byte array from the set."""
- data_hash = hashlib.sha256(data).digest()
- self.denominator = (self.denominator * data_to_num3072(data_hash)) % self.MODULUS
-
- def digest(self):
- """Extract the final hash. Does not modify this object."""
- val = (self.numerator * pow(self.denominator, -1, self.MODULUS)) % self.MODULUS
- bytes384 = val.to_bytes(384, 'little')
- return hashlib.sha256(bytes384).digest()
-
-class TestFrameworkMuhash(unittest.TestCase):
- def test_muhash(self):
- muhash = MuHash3072()
- muhash.insert(b'\x00' * 32)
- muhash.insert((b'\x01' + b'\x00' * 31))
- muhash.remove((b'\x02' + b'\x00' * 31))
- finalized = muhash.digest()
- # This mirrors the result in the C++ MuHash3072 unit test
- self.assertEqual(finalized[::-1].hex(), "10d312b100cbd32ada024a6646e40d3482fcff103668d2625f10002a607d5863")
-
- def test_chacha20(self):
- def chacha_check(key, result):
- self.assertEqual(chacha20_32_to_384(key)[:64].hex(), result)
-
- # Test vectors from https://tools.ietf.org/html/draft-agl-tls-chacha20poly1305-04#section-7
- # Since the nonce is hardcoded to 0 in our function we only use those vectors.
- chacha_check([0]*32, "76b8e0ada0f13d90405d6ae55386bd28bdd219b8a08ded1aa836efcc8b770dc7da41597c5157488d7724e03fb8d84a376a43b8f41518a11cc387b669b2ee6586")
- chacha_check([0]*31 + [1], "4540f05a9f1fb296d7736e7b208e3c96eb4fe1834688d2604f450952ed432d41bbe2a0b6ea7566d2a5d1e7e20d42af2c53d792b1c43fea817e9ad275ae546963")
diff --git a/test/functional/test_framework/p2p.py b/test/functional/test_framework/p2p.py
index be4ed624fc..b1ed97b794 100755
--- a/test/functional/test_framework/p2p.py
+++ b/test/functional/test_framework/p2p.py
@@ -364,8 +364,8 @@ class P2PInterface(P2PConnection):
vt.addrFrom.port = 0
self.on_connection_send_msg = vt # Will be sent in connection_made callback
- def peer_connect(self, *args, services=P2P_SERVICES, send_version=True, **kwargs):
- create_conn = super().peer_connect(*args, **kwargs)
+ def peer_connect(self, *, services=P2P_SERVICES, send_version, **kwargs):
+ create_conn = super().peer_connect(**kwargs)
if send_version:
self.peer_connect_send_version(services)
@@ -456,7 +456,8 @@ class P2PInterface(P2PConnection):
self.send_message(msg_verack())
self.nServices = message.nServices
self.relay = message.relay
- self.send_message(msg_getaddr())
+ if self.p2p_connected_to_node:
+ self.send_message(msg_getaddr())
# Connection helper methods
diff --git a/test/functional/test_framework/script.py b/test/functional/test_framework/script.py
index 17a954cb22..78d8580794 100644
--- a/test/functional/test_framework/script.py
+++ b/test/functional/test_framework/script.py
@@ -10,7 +10,6 @@ This file is modified from python-bitcoinlib.
from collections import namedtuple
import struct
import unittest
-from typing import List, Dict
from .key import TaggedHash, tweak_add_pubkey, compute_xonly_pubkey
@@ -24,7 +23,7 @@ from .messages import (
uint256_from_str,
)
-from .ripemd160 import ripemd160
+from .crypto.ripemd160 import ripemd160
MAX_SCRIPT_ELEMENT_SIZE = 520
MAX_PUBKEYS_PER_MULTI_A = 999
@@ -110,8 +109,8 @@ class CScriptOp(int):
_opcode_instances.append(super().__new__(cls, n))
return _opcode_instances[n]
-OPCODE_NAMES: Dict[CScriptOp, str] = {}
-_opcode_instances: List[CScriptOp] = []
+OPCODE_NAMES: dict[CScriptOp, str] = {}
+_opcode_instances: list[CScriptOp] = []
# Populate opcode instance table
for n in range(0xff + 1):
diff --git a/test/functional/test_framework/test_framework.py b/test/functional/test_framework/test_framework.py
index 4e6d245b5f..0ee332b75b 100755
--- a/test/functional/test_framework/test_framework.py
+++ b/test/functional/test_framework/test_framework.py
@@ -19,7 +19,6 @@ import sys
import tempfile
import time
-from typing import List
from .address import create_deterministic_address_bcrt1_p2tr_op_true
from .authproxy import JSONRPCException
from . import coverage
@@ -30,6 +29,7 @@ from .util import (
PortSeed,
assert_equal,
check_json_precision,
+ find_vout_for_address,
get_datadir_path,
initialize_datadir,
p2p_port,
@@ -96,7 +96,7 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
"""Sets test framework defaults. Do not override this method. Instead, override the set_test_params() method"""
self.chain: str = 'regtest'
self.setup_clean_chain: bool = False
- self.nodes: List[TestNode] = []
+ self.nodes: list[TestNode] = []
self.extra_args = None
self.network_thread = None
self.rpc_timeout = 60 # Wait for up to 60 seconds for the RPC server to respond
@@ -507,8 +507,6 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
assert_equal(len(binary_cli), num_nodes)
for i in range(num_nodes):
args = list(extra_args[i])
- if self.options.v2transport and ("-v2transport=0" not in args):
- args.append("-v2transport=1")
test_node_i = TestNode(
i,
get_datadir_path(self.options.tmpdir, i),
@@ -527,6 +525,7 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
start_perf=self.options.perf,
use_valgrind=self.options.valgrind,
descriptors=self.options.descriptors,
+ v2transport=self.options.v2transport,
)
self.nodes.append(test_node_i)
if not test_node_i.version_is_at_least(170000):
@@ -601,7 +600,7 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
ip_port = "127.0.0.1:" + str(p2p_port(b))
if peer_advertises_v2 is None:
- peer_advertises_v2 = self.options.v2transport
+ peer_advertises_v2 = from_connection.use_v2transport
if peer_advertises_v2:
from_connection.addnode(node=ip_port, command="onetry", v2transport=True)
@@ -697,6 +696,22 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
sync_fun() if sync_fun else self.sync_all()
return blocks
+ def create_outpoints(self, node, *, outputs):
+ """Send funds to a given list of `{address: amount}` targets using the bitcoind
+ wallet and return the corresponding outpoints as a list of dictionaries
+ `[{"txid": txid, "vout": vout1}, {"txid": txid, "vout": vout2}, ...]`.
+ The result can be used to specify inputs for RPCs like `createrawtransaction`,
+ `createpsbt`, `lockunspent` etc."""
+ assert all(len(output.keys()) == 1 for output in outputs)
+ send_res = node.send(outputs)
+ assert send_res["complete"]
+ utxos = []
+ for output in outputs:
+ address = list(output.keys())[0]
+ vout = find_vout_for_address(node, send_res["txid"], address)
+ utxos.append({"txid": send_res["txid"], "vout": vout})
+ return utxos
+
def sync_blocks(self, nodes=None, wait=1, timeout=60):
"""
Wait until everybody has the same tip.
diff --git a/test/functional/test_framework/test_node.py b/test/functional/test_framework/test_node.py
index c77cfbdd91..b3f777b9df 100755
--- a/test/functional/test_framework/test_node.py
+++ b/test/functional/test_framework/test_node.py
@@ -67,7 +67,7 @@ class TestNode():
To make things easier for the test writer, any unrecognised messages will
be dispatched to the RPC connection."""
- def __init__(self, i, datadir_path, *, chain, rpchost, timewait, timeout_factor, bitcoind, bitcoin_cli, coverage_dir, cwd, extra_conf=None, extra_args=None, use_cli=False, start_perf=False, use_valgrind=False, version=None, descriptors=False):
+ def __init__(self, i, datadir_path, *, chain, rpchost, timewait, timeout_factor, bitcoind, bitcoin_cli, coverage_dir, cwd, extra_conf=None, extra_args=None, use_cli=False, start_perf=False, use_valgrind=False, version=None, descriptors=False, v2transport=False):
"""
Kwargs:
start_perf (bool): If True, begin profiling the node with `perf` as soon as
@@ -126,6 +126,12 @@ class TestNode():
if self.version_is_at_least(239000):
self.args.append("-loglevel=trace")
+ # Default behavior from global -v2transport flag is added to args to persist it over restarts.
+ # May be overwritten in individual tests, using extra_args.
+ self.default_to_v2 = v2transport
+ if self.default_to_v2:
+ self.args.append("-v2transport=1")
+
self.cli = TestNodeCLI(bitcoin_cli, self.datadir_path)
self.use_cli = use_cli
self.start_perf = start_perf
@@ -198,6 +204,8 @@ class TestNode():
if extra_args is None:
extra_args = self.extra_args
+ self.use_v2transport = "-v2transport=1" in extra_args or (self.default_to_v2 and "-v2transport=0" not in extra_args)
+
# Add a new stdout and stderr file each time bitcoind is started
if stderr is None:
stderr = tempfile.NamedTemporaryFile(dir=self.stderr_dir, delete=False)
@@ -252,7 +260,7 @@ class TestNode():
if self.version_is_at_least(190000):
# getmempoolinfo.loaded is available since commit
# bb8ae2c (version 0.19.0)
- wait_until_helper_internal(lambda: rpc.getmempoolinfo()['loaded'], timeout_factor=self.timeout_factor)
+ self.wait_until(lambda: rpc.getmempoolinfo()['loaded'])
# 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
@@ -406,7 +414,7 @@ class TestNode():
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_internal(lambda: self.is_node_stopped(expected_ret_code=expected_ret_code, **kwargs), timeout=timeout, timeout_factor=self.timeout_factor)
+ self.wait_until(lambda: self.is_node_stopped(expected_ret_code=expected_ret_code, **kwargs), timeout=timeout)
def replace_in_config(self, replacements):
"""
@@ -512,6 +520,23 @@ class TestNode():
str(expected_msgs), print_log))
@contextlib.contextmanager
+ def wait_for_new_peer(self, timeout=5):
+ """
+ Wait until the node is connected to at least one new peer. We detect this
+ by watching for an increased highest peer id, using the `getpeerinfo` RPC call.
+ Note that the simpler approach of only accounting for the number of peers
+ suffers from race conditions, as disconnects from unrelated previous peers
+ could happen anytime in-between.
+ """
+ def get_highest_peer_id():
+ peer_info = self.getpeerinfo()
+ return peer_info[-1]["id"] if peer_info else -1
+
+ initial_peer_id = get_highest_peer_id()
+ yield
+ self.wait_until(lambda: get_highest_peer_id() > initial_peer_id, timeout=timeout)
+
+ @contextlib.contextmanager
def profile_with_perf(self, profile_name: str):
"""
Context manager that allows easy profiling of node activity using `perf`.
@@ -634,7 +659,7 @@ class TestNode():
assert_msg += "with expected error " + expected_msg
self._raise_assertion_error(assert_msg)
- def add_p2p_connection(self, p2p_conn, *, wait_for_verack=True, **kwargs):
+ def add_p2p_connection(self, p2p_conn, *, wait_for_verack=True, send_version=True, **kwargs):
"""Add an inbound p2p connection to the node.
This method adds the p2p connection to the self.p2ps list and also
@@ -644,9 +669,12 @@ class TestNode():
if 'dstaddr' not in kwargs:
kwargs['dstaddr'] = '127.0.0.1'
- p2p_conn.peer_connect(**kwargs, net=self.chain, timeout_factor=self.timeout_factor)()
+ p2p_conn.p2p_connected_to_node = True
+ p2p_conn.peer_connect(**kwargs, send_version=send_version, net=self.chain, timeout_factor=self.timeout_factor)()
self.p2ps.append(p2p_conn)
p2p_conn.wait_until(lambda: p2p_conn.is_connected, check_connected=False)
+ if send_version:
+ p2p_conn.wait_until(lambda: not p2p_conn.on_connection_send_msg)
if wait_for_verack:
# Wait for the node to send us the version and verack
p2p_conn.wait_for_verack()
@@ -689,6 +717,7 @@ class TestNode():
self.log.debug("Connecting to %s:%d %s" % (address, port, connection_type))
self.addconnection('%s:%d' % (address, port), connection_type)
+ p2p_conn.p2p_connected_to_node = False
p2p_conn.peer_accept_connection(connect_cb=addconnection_callback, connect_id=p2p_idx + 1, net=self.chain, timeout_factor=self.timeout_factor, **kwargs)()
if connection_type == "feeler":
@@ -699,6 +728,7 @@ class TestNode():
p2p_conn.wait_for_connect()
self.p2ps.append(p2p_conn)
+ p2p_conn.wait_until(lambda: not p2p_conn.on_connection_send_msg)
if wait_for_verack:
p2p_conn.wait_for_verack()
p2p_conn.sync_with_ping()
@@ -716,7 +746,7 @@ class TestNode():
p.peer_disconnect()
del self.p2ps[:]
- wait_until_helper_internal(lambda: self.num_test_p2p_connections() == 0, timeout_factor=self.timeout_factor)
+ self.wait_until(lambda: self.num_test_p2p_connections() == 0)
def bumpmocktime(self, seconds):
"""Fast forward using setmocktime to self.mocktime + seconds. Requires setmocktime to have
@@ -725,6 +755,9 @@ class TestNode():
self.mocktime += seconds
self.setmocktime(self.mocktime)
+ def wait_until(self, test_function, timeout=60):
+ return wait_until_helper_internal(test_function, timeout=timeout, timeout_factor=self.timeout_factor)
+
class TestNodeCLIAttr:
def __init__(self, cli, command):
@@ -777,15 +810,15 @@ class TestNodeCLI():
results.append(dict(error=e))
return results
- def send_cli(self, command=None, *args, **kwargs):
+ def send_cli(self, clicommand=None, *args, **kwargs):
"""Run bitcoin-cli command. Deserializes returned string as python object."""
pos_args = [arg_to_cli(arg) for arg in args]
named_args = [str(key) + "=" + arg_to_cli(value) for (key, value) in kwargs.items()]
p_args = [self.binary, f"-datadir={self.datadir}"] + self.options
if named_args:
p_args += ["-named"]
- if command is not None:
- p_args += [command]
+ if clicommand is not None:
+ p_args += [clicommand]
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, text=True)
diff --git a/test/functional/test_framework/util.py b/test/functional/test_framework/util.py
index 61346e9d19..c65e3e38e6 100644
--- a/test/functional/test_framework/util.py
+++ b/test/functional/test_framework/util.py
@@ -13,14 +13,14 @@ import json
import logging
import os
import pathlib
-import random
import re
import sys
import time
from . import coverage
from .authproxy import AuthServiceProxy, JSONRPCException
-from typing import Callable, Optional, Tuple
+from collections.abc import Callable
+from typing import Optional
logger = logging.getLogger("TestFramework.utils")
@@ -287,12 +287,6 @@ def sha256sum_file(filename):
return h.digest()
-# TODO: Remove and use random.randbytes(n) instead, available in Python 3.9
-def random_bytes(n):
- """Return a random bytes object of length n."""
- return bytes(random.getrandbits(8) for i in range(n))
-
-
# RPC/P2P connection constants and functions
############################################
@@ -416,7 +410,7 @@ def get_datadir_path(dirname, n):
return pathlib.Path(dirname) / f"node{n}"
-def get_temp_default_datadir(temp_dir: pathlib.Path) -> Tuple[dict, pathlib.Path]:
+def get_temp_default_datadir(temp_dir: pathlib.Path) -> tuple[dict, pathlib.Path]:
"""Return os-specific environment variables that can be set to make the
GetDefaultDataDir() function return a datadir path under the provided
temp_dir, as well as the complete path it would return."""
@@ -490,18 +484,6 @@ def check_node_connections(*, node, num_in, num_out):
#############################
-def find_output(node, txid, amount, *, blockhash=None):
- """
- Return index to output of txid with value amount
- Raises exception if there is none.
- """
- txdata = node.getrawtransaction(txid, 1, blockhash)
- for i in range(len(txdata["vout"])):
- if txdata["vout"][i]["value"] == amount:
- return i
- raise RuntimeError("find_output txid %s : %s not found" % (txid, str(amount)))
-
-
# 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 035a482f4c..53c8e1b0cc 100644
--- a/test/functional/test_framework/wallet.py
+++ b/test/functional/test_framework/wallet.py
@@ -9,7 +9,6 @@ from decimal import Decimal
from enum import Enum
from typing import (
Any,
- List,
Optional,
)
from test_framework.address import (
@@ -284,7 +283,7 @@ class MiniWallet:
def create_self_transfer_multi(
self,
*,
- utxos_to_spend: Optional[List[dict]] = None,
+ utxos_to_spend: Optional[list[dict]] = None,
num_outputs=1,
amount_per_output=0,
locktime=0,
diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py
index fbf48a0e4d..2460b2e3e6 100755
--- a/test/functional/test_runner.py
+++ b/test/functional/test_runner.py
@@ -73,12 +73,15 @@ TEST_EXIT_SKIPPED = 77
# the output of `git grep unittest.TestCase ./test/functional/test_framework`
TEST_FRAMEWORK_MODULES = [
"address",
+ "crypto.bip324_cipher",
"blocktools",
- "ellswift",
+ "crypto.chacha20",
+ "crypto.ellswift",
"key",
"messages",
- "muhash",
- "ripemd160",
+ "crypto.muhash",
+ "crypto.poly1305",
+ "crypto.ripemd160",
"script",
"segwit_addr",
]
@@ -105,7 +108,6 @@ BASE_SCRIPTS = [
'feature_maxuploadtarget.py',
'mempool_updatefromblock.py',
'mempool_persist.py --descriptors',
- 'wallet_miniscript.py --descriptors',
# vv Tests less than 60s vv
'rpc_psbt.py --legacy-wallet',
'rpc_psbt.py --descriptors',
@@ -149,10 +151,12 @@ BASE_SCRIPTS = [
'p2p_sendheaders.py',
'wallet_listtransactions.py --legacy-wallet',
'wallet_listtransactions.py --descriptors',
+ 'wallet_miniscript.py --descriptors',
# vv Tests less than 30s vv
'p2p_invalid_messages.py',
'rpc_createmultisig.py',
'p2p_timeouts.py',
+ 'p2p_timeouts.py --v2transport',
'wallet_dump.py --legacy-wallet',
'rpc_signer.py',
'wallet_signer.py --descriptors',
@@ -203,6 +207,8 @@ BASE_SCRIPTS = [
'wallet_createwallet.py --descriptors',
'wallet_watchonly.py --legacy-wallet',
'wallet_watchonly.py --usecli --legacy-wallet',
+ 'wallet_reindex.py --legacy-wallet',
+ 'wallet_reindex.py --descriptors',
'wallet_reorgsrestore.py',
'wallet_conflicts.py --legacy-wallet',
'wallet_conflicts.py --descriptors',
@@ -240,6 +246,7 @@ BASE_SCRIPTS = [
'p2p_getdata.py',
'p2p_addrfetch.py',
'rpc_net.py',
+ 'rpc_net.py --v2transport',
'wallet_keypool.py --legacy-wallet',
'wallet_keypool.py --descriptors',
'wallet_descriptor.py --descriptors',
@@ -357,7 +364,6 @@ BASE_SCRIPTS = [
'rpc_scanblocks.py',
'p2p_sendtxrcncl.py',
'rpc_scantxoutset.py',
- 'feature_txindex_compatibility.py',
'feature_unsupported_utxo_db.py',
'feature_logging.py',
'feature_anchors.py',
@@ -366,6 +372,7 @@ BASE_SCRIPTS = [
'wallet_orphanedreward.py',
'wallet_timelock.py',
'p2p_node_network_limited.py',
+ 'p2p_node_network_limited.py --v2transport',
'p2p_permissions.py',
'feature_blocksdir.py',
'wallet_startup.py',
@@ -560,8 +567,7 @@ def run_tests(*, test_list, src_dir, build_dir, tmpdir, jobs=1, enable_coverage=
test_framework_tests.addTest(unittest.TestLoader().loadTestsFromName("test_framework.{}".format(module)))
result = unittest.TextTestRunner(verbosity=1, failfast=True).run(test_framework_tests)
if not result.wasSuccessful():
- logging.debug("Early exiting after failure in TestFramework unit tests")
- sys.exit(False)
+ sys.exit("Early exiting after failure in TestFramework unit tests")
flags = ['--cachedir={}'.format(cache_dir)] + args
diff --git a/test/functional/wallet_avoidreuse.py b/test/functional/wallet_avoidreuse.py
index c257bda452..9d3c55d6b6 100755
--- a/test/functional/wallet_avoidreuse.py
+++ b/test/functional/wallet_avoidreuse.py
@@ -257,7 +257,7 @@ class AvoidReuseTest(BitcoinTestFramework):
if not self.options.descriptors:
# For the second send, we transmute it to a related single-key address
- # to make sure it's also detected as re-use
+ # to make sure it's also detected as reuse
fund_spk = address_to_scriptpubkey(fundaddr).hex()
fund_decoded = self.nodes[0].decodescript(fund_spk)
if second_addr_type == "p2sh-segwit":
diff --git a/test/functional/wallet_basic.py b/test/functional/wallet_basic.py
index 01149a0977..f798eee365 100755
--- a/test/functional/wallet_basic.py
+++ b/test/functional/wallet_basic.py
@@ -18,7 +18,6 @@ from test_framework.util import (
assert_equal,
assert_fee_amount,
assert_raises_rpc_error,
- find_vout_for_address,
)
from test_framework.wallet_util import test_address
from test_framework.wallet import MiniWallet
@@ -471,10 +470,9 @@ class WalletTest(BitcoinTestFramework):
# Import address and private key to check correct behavior of spendable unspents
# 1. Send some coins to generate new UTXO
address_to_import = self.nodes[2].getnewaddress()
- txid = self.nodes[0].sendtoaddress(address_to_import, 1)
+ utxo = self.create_outpoints(self.nodes[0], outputs=[{address_to_import: 1}])[0]
self.sync_mempools(self.nodes[0:3])
- vout = find_vout_for_address(self.nodes[2], txid, address_to_import)
- self.nodes[2].lockunspent(False, [{"txid": txid, "vout": vout}])
+ self.nodes[2].lockunspent(False, [utxo])
self.generate(self.nodes[0], 1, sync_fun=lambda: self.sync_all(self.nodes[0:3]))
self.log.info("Test sendtoaddress with fee_rate param (explicit fee rate in sat/vB)")
@@ -641,7 +639,7 @@ class WalletTest(BitcoinTestFramework):
node0_balance = self.nodes[0].getbalance()
# With walletrejectlongchains we will not create the tx and store it in our wallet.
- assert_raises_rpc_error(-6, "Transaction has too long of a mempool chain", self.nodes[0].sendtoaddress, sending_addr, node0_balance - Decimal('0.01'))
+ assert_raises_rpc_error(-6, f"too many unconfirmed ancestors [limit: {chainlimit * 2}]", self.nodes[0].sendtoaddress, sending_addr, node0_balance - Decimal('0.01'))
# Verify nothing new in wallet
assert_equal(total_txs, len(self.nodes[0].listtransactions("*", 99999)))
diff --git a/test/functional/wallet_fast_rescan.py b/test/functional/wallet_fast_rescan.py
index 2f9c924e71..1315bccafd 100755
--- a/test/functional/wallet_fast_rescan.py
+++ b/test/functional/wallet_fast_rescan.py
@@ -4,8 +4,6 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test that fast rescan using block filters for descriptor wallets detects
top-ups correctly and finds the same transactions than the slow variant."""
-from typing import List
-
from test_framework.address import address_to_scriptpubkey
from test_framework.descriptors import descsum_create
from test_framework.test_framework import BitcoinTestFramework
@@ -32,7 +30,7 @@ class WalletFastRescanTest(BitcoinTestFramework):
self.skip_if_no_wallet()
self.skip_if_no_sqlite()
- def get_wallet_txids(self, node: TestNode, wallet_name: str) -> List[str]:
+ def get_wallet_txids(self, node: TestNode, wallet_name: str) -> list[str]:
w = node.get_wallet_rpc(wallet_name)
txs = w.listtransactions('*', 1000000)
return [tx['txid'] for tx in txs]
diff --git a/test/functional/wallet_fundrawtransaction.py b/test/functional/wallet_fundrawtransaction.py
index 77611649ac..a331ba997e 100755
--- a/test/functional/wallet_fundrawtransaction.py
+++ b/test/functional/wallet_fundrawtransaction.py
@@ -22,7 +22,6 @@ from test_framework.util import (
assert_greater_than_or_equal,
assert_raises_rpc_error,
count_bytes,
- find_vout_for_address,
get_fee,
)
from test_framework.wallet_util import generate_keypair, WalletUnlock
@@ -85,14 +84,13 @@ class RawTransactionsTest(BitcoinTestFramework):
Unlock all UTXOs except the watchonly one
"""
to_keep = []
- if self.watchonly_txid is not None and self.watchonly_vout is not None:
- to_keep.append({"txid": self.watchonly_txid, "vout": self.watchonly_vout})
+ if self.watchonly_utxo is not None:
+ to_keep.append(self.watchonly_utxo)
wallet.lockunspent(True)
wallet.lockunspent(False, to_keep)
def run_test(self):
- self.watchonly_txid = None
- self.watchonly_vout = None
+ self.watchonly_utxo = None
self.log.info("Connect nodes, set fees, generate blocks, and sync")
self.min_relay_tx_fee = self.nodes[0].getnetworkinfo()['relayfee']
# This test is not meant to test fee estimation and we'd like
@@ -163,11 +161,10 @@ class RawTransactionsTest(BitcoinTestFramework):
watchonly_pubkey = self.nodes[0].getaddressinfo(watchonly_address)["pubkey"]
self.watchonly_amount = Decimal(200)
wwatch.importpubkey(watchonly_pubkey, "", True)
- self.watchonly_txid = self.nodes[0].sendtoaddress(watchonly_address, self.watchonly_amount)
+ self.watchonly_utxo = self.create_outpoints(self.nodes[0], outputs=[{watchonly_address: self.watchonly_amount}])[0]
# Lock UTXO so nodes[0] doesn't accidentally spend it
- self.watchonly_vout = find_vout_for_address(self.nodes[0], self.watchonly_txid, watchonly_address)
- self.nodes[0].lockunspent(False, [{"txid": self.watchonly_txid, "vout": self.watchonly_vout}])
+ self.nodes[0].lockunspent(False, [self.watchonly_utxo])
self.nodes[0].sendtoaddress(self.nodes[3].get_wallet_rpc(self.default_wallet_name).getnewaddress(), self.watchonly_amount / 10)
@@ -738,7 +735,7 @@ class RawTransactionsTest(BitcoinTestFramework):
result = wwatch.fundrawtransaction(rawtx, True)
res_dec = self.nodes[0].decoderawtransaction(result["hex"])
assert_equal(len(res_dec["vin"]), 1)
- assert_equal(res_dec["vin"][0]["txid"], self.watchonly_txid)
+ assert_equal(res_dec["vin"][0]["txid"], self.watchonly_utxo['txid'])
assert "fee" in result.keys()
assert_greater_than(result["changepos"], -1)
@@ -758,7 +755,7 @@ class RawTransactionsTest(BitcoinTestFramework):
result = wwatch.fundrawtransaction(rawtx, includeWatching=True, changeAddress=w3.getrawchangeaddress(), subtractFeeFromOutputs=[0])
res_dec = self.nodes[0].decoderawtransaction(result["hex"])
assert_equal(len(res_dec["vin"]), 1)
- assert res_dec["vin"][0]["txid"] == self.watchonly_txid
+ assert res_dec["vin"][0]["txid"] == self.watchonly_utxo['txid']
assert_greater_than(result["fee"], 0)
assert_equal(result["changepos"], -1)
@@ -970,10 +967,9 @@ class RawTransactionsTest(BitcoinTestFramework):
self.log.info("Test fundrawtxn subtract fee from outputs with preset inputs that are sufficient")
addr = self.nodes[0].getnewaddress()
- txid = self.nodes[0].sendtoaddress(addr, 10)
- vout = find_vout_for_address(self.nodes[0], txid, addr)
+ utxo = self.create_outpoints(self.nodes[0], outputs=[{addr: 10}])[0]
- rawtx = self.nodes[0].createrawtransaction([{'txid': txid, 'vout': vout}], [{self.nodes[0].getnewaddress(): 5}])
+ rawtx = self.nodes[0].createrawtransaction([utxo], [{self.nodes[0].getnewaddress(): 5}])
fundedtx = self.nodes[0].fundrawtransaction(rawtx, subtractFeeFromOutputs=[0])
signedtx = self.nodes[0].signrawtransactionwithwallet(fundedtx['hex'])
self.nodes[0].sendrawtransaction(signedtx['hex'])
@@ -1264,14 +1260,12 @@ class RawTransactionsTest(BitcoinTestFramework):
addr = wallet.getnewaddress(address_type="bech32")
ext_addr = self.nodes[0].getnewaddress(address_type="bech32")
- txid = self.nodes[0].send([{addr: 5}, {ext_addr: 5}])["txid"]
- vout = find_vout_for_address(self.nodes[0], txid, addr)
- ext_vout = find_vout_for_address(self.nodes[0], txid, ext_addr)
+ utxo, ext_utxo = self.create_outpoints(self.nodes[0], outputs=[{addr: 5}, {ext_addr: 5}])
self.nodes[0].sendtoaddress(wallet.getnewaddress(address_type="bech32"), 5)
self.generate(self.nodes[0], 1)
- rawtx = wallet.createrawtransaction([{'txid': txid, 'vout': vout}], [{self.nodes[0].getnewaddress(address_type="bech32"): 8}])
+ rawtx = wallet.createrawtransaction([utxo], [{self.nodes[0].getnewaddress(address_type="bech32"): 8}])
fundedtx = wallet.fundrawtransaction(rawtx, fee_rate=10, change_type="bech32")
# with 71-byte signatures we should expect following tx size
# tx overhead (10) + 2 inputs (41 each) + 2 p2wpkh (31 each) + (segwit marker and flag (2) + 2 p2wpkh 71 byte sig witnesses (107 each)) / witness scaling factor (4)
@@ -1279,7 +1273,7 @@ class RawTransactionsTest(BitcoinTestFramework):
assert_equal(fundedtx['fee'] * COIN, tx_size * 10)
# Using the other output should have 72 byte sigs
- rawtx = wallet.createrawtransaction([{'txid': txid, 'vout': ext_vout}], [{self.nodes[0].getnewaddress(): 13}])
+ rawtx = wallet.createrawtransaction([ext_utxo], [{self.nodes[0].getnewaddress(): 13}])
ext_desc = self.nodes[0].getaddressinfo(ext_addr)["desc"]
fundedtx = wallet.fundrawtransaction(rawtx, fee_rate=10, change_type="bech32", solving_data={"descriptors": [ext_desc]})
# tx overhead (10) + 3 inputs (41 each) + 2 p2wpkh(31 each) + (segwit marker and flag (2) + 2 p2wpkh 71 bytes sig witnesses (107 each) + p2wpkh 72 byte sig witness (108)) / witness scaling factor (4)
@@ -1298,10 +1292,9 @@ class RawTransactionsTest(BitcoinTestFramework):
addr = wallet.getnewaddress()
inputs = []
for i in range(0, 2):
- txid = self.nodes[2].sendtoaddress(addr, 5)
- self.sync_mempools()
- vout = find_vout_for_address(wallet, txid, addr)
- inputs.append((txid, vout))
+ utxo = self.create_outpoints(self.nodes[2], outputs=[{addr: 5}])[0]
+ inputs.append((utxo['txid'], utxo['vout']))
+ self.sync_mempools()
# Unsafe inputs are ignored by default.
rawtx = wallet.createrawtransaction([], [{self.nodes[2].getnewaddress(): 7.5}])
diff --git a/test/functional/wallet_importdescriptors.py b/test/functional/wallet_importdescriptors.py
index ad5ae111aa..1f1f92589c 100755
--- a/test/functional/wallet_importdescriptors.py
+++ b/test/functional/wallet_importdescriptors.py
@@ -24,7 +24,6 @@ from test_framework.descriptors import descsum_create
from test_framework.util import (
assert_equal,
assert_raises_rpc_error,
- find_vout_for_address,
)
from test_framework.wallet_util import (
get_generate_key,
@@ -493,12 +492,10 @@ class ImportDescriptorsTest(BitcoinTestFramework):
assert_equal(wmulti_pub.getwalletinfo()['keypoolsize'], 999)
# generate some utxos for next tests
- txid = w0.sendtoaddress(addr, 10)
- vout = find_vout_for_address(self.nodes[0], txid, addr)
+ utxo = self.create_outpoints(w0, outputs=[{addr: 10}])[0]
addr2 = wmulti_pub.getnewaddress('', 'bech32')
- txid2 = w0.sendtoaddress(addr2, 10)
- vout2 = find_vout_for_address(self.nodes[0], txid2, addr2)
+ utxo2 = self.create_outpoints(w0, outputs=[{addr2: 10}])[0]
self.generate(self.nodes[0], 6)
assert_equal(wmulti_pub.getbalance(), wmulti_priv.getbalance())
@@ -554,7 +551,7 @@ class ImportDescriptorsTest(BitcoinTestFramework):
assert_equal(res[1]['success'], True)
assert_equal(res[1]['warnings'][0], 'Not all private keys provided. Some wallet functionality may return unexpected errors')
- rawtx = self.nodes[1].createrawtransaction([{'txid': txid, 'vout': vout}], {w0.getnewaddress(): 9.999})
+ rawtx = self.nodes[1].createrawtransaction([utxo], {w0.getnewaddress(): 9.999})
tx_signed_1 = wmulti_priv1.signrawtransactionwithwallet(rawtx)
assert_equal(tx_signed_1['complete'], False)
tx_signed_2 = wmulti_priv2.signrawtransactionwithwallet(tx_signed_1['hex'])
@@ -648,7 +645,7 @@ class ImportDescriptorsTest(BitcoinTestFramework):
}])
assert_equal(res[0]['success'], True)
- rawtx = self.nodes[1].createrawtransaction([{'txid': txid2, 'vout': vout2}], {w0.getnewaddress(): 9.999})
+ rawtx = self.nodes[1].createrawtransaction([utxo2], {w0.getnewaddress(): 9.999})
tx = wmulti_priv3.signrawtransactionwithwallet(rawtx)
assert_equal(tx['complete'], True)
self.nodes[1].sendrawtransaction(tx['hex'])
diff --git a/test/functional/wallet_listtransactions.py b/test/functional/wallet_listtransactions.py
index 18bb8a0cd8..064ce12108 100755
--- a/test/functional/wallet_listtransactions.py
+++ b/test/functional/wallet_listtransactions.py
@@ -111,6 +111,7 @@ class ListTransactionsTest(BitcoinTestFramework):
self.run_rbf_opt_in_test()
self.run_externally_generated_address_test()
+ self.run_coinjoin_test()
self.run_invalid_parameters_test()
self.test_op_return()
@@ -281,6 +282,34 @@ class ListTransactionsTest(BitcoinTestFramework):
assert_equal(['pizza2'], self.nodes[0].getaddressinfo(addr2)['labels'])
assert_equal(['pizza3'], self.nodes[0].getaddressinfo(addr3)['labels'])
+ def run_coinjoin_test(self):
+ self.log.info('Check "coin-join" transaction')
+ input_0 = next(i for i in self.nodes[0].listunspent(query_options={"minimumAmount": 0.2}, include_unsafe=False))
+ input_1 = next(i for i in self.nodes[1].listunspent(query_options={"minimumAmount": 0.2}, include_unsafe=False))
+ raw_hex = self.nodes[0].createrawtransaction(
+ inputs=[
+ {
+ "txid": input_0["txid"],
+ "vout": input_0["vout"],
+ },
+ {
+ "txid": input_1["txid"],
+ "vout": input_1["vout"],
+ },
+ ],
+ outputs={
+ self.nodes[0].getnewaddress(): 0.123,
+ self.nodes[1].getnewaddress(): 0.123,
+ },
+ )
+ raw_hex = self.nodes[0].signrawtransactionwithwallet(raw_hex)["hex"]
+ raw_hex = self.nodes[1].signrawtransactionwithwallet(raw_hex)["hex"]
+ txid_join = self.nodes[0].sendrawtransaction(hexstring=raw_hex, maxfeerate=0)
+ fee_join = self.nodes[0].getmempoolentry(txid_join)["fees"]["base"]
+ # Fee should be correct: assert_equal(fee_join, self.nodes[0].gettransaction(txid_join)['fee'])
+ # But it is not, see for example https://github.com/bitcoin/bitcoin/issues/14136:
+ assert fee_join != self.nodes[0].gettransaction(txid_join)["fee"]
+
def run_invalid_parameters_test(self):
self.log.info("Test listtransactions RPC parameter validity")
assert_raises_rpc_error(-8, 'Label argument must be a valid label name or "*".', self.nodes[0].listtransactions, label="")
diff --git a/test/functional/wallet_migration.py b/test/functional/wallet_migration.py
index aede9281d5..e2edaef4da 100755
--- a/test/functional/wallet_migration.py
+++ b/test/functional/wallet_migration.py
@@ -23,7 +23,6 @@ from test_framework.script_util import key_to_p2pkh_script, script_to_p2sh_scrip
from test_framework.util import (
assert_equal,
assert_raises_rpc_error,
- find_vout_for_address,
sha256sum_file,
)
from test_framework.wallet_util import (
@@ -310,14 +309,14 @@ class WalletMigrationTest(BitcoinTestFramework):
# Received watchonly tx that is then spent
import_sent_addr = default.getnewaddress()
imports0.importaddress(import_sent_addr)
- received_sent_watchonly_txid = default.sendtoaddress(import_sent_addr, 10)
- received_sent_watchonly_vout = find_vout_for_address(self.nodes[0], received_sent_watchonly_txid, import_sent_addr)
- send = default.sendall(recipients=[default.getnewaddress()], inputs=[{"txid": received_sent_watchonly_txid, "vout": received_sent_watchonly_vout}])
+ received_sent_watchonly_utxo = self.create_outpoints(node=default, outputs=[{import_sent_addr: 10}])[0]
+
+ send = default.sendall(recipients=[default.getnewaddress()], inputs=[received_sent_watchonly_utxo])
sent_watchonly_txid = send["txid"]
self.generate(self.nodes[0], 1)
received_watchonly_tx_info = imports0.gettransaction(received_watchonly_txid, True)
- received_sent_watchonly_tx_info = imports0.gettransaction(received_sent_watchonly_txid, True)
+ received_sent_watchonly_tx_info = imports0.gettransaction(received_sent_watchonly_utxo["txid"], True)
balances = imports0.getbalances()
spendable_bal = balances["mine"]["trusted"]
@@ -332,7 +331,7 @@ class WalletMigrationTest(BitcoinTestFramework):
assert_equal(imports0.getwalletinfo()["descriptors"], True)
self.assert_is_sqlite("imports0")
assert_raises_rpc_error(-5, "Invalid or non-wallet transaction id", imports0.gettransaction, received_watchonly_txid)
- assert_raises_rpc_error(-5, "Invalid or non-wallet transaction id", imports0.gettransaction, received_sent_watchonly_txid)
+ assert_raises_rpc_error(-5, "Invalid or non-wallet transaction id", imports0.gettransaction, received_sent_watchonly_utxo['txid'])
assert_raises_rpc_error(-5, "Invalid or non-wallet transaction id", imports0.gettransaction, sent_watchonly_txid)
assert_equal(len(imports0.listtransactions(include_watchonly=True)), 1)
imports0.gettransaction(received_txid)
@@ -347,7 +346,7 @@ class WalletMigrationTest(BitcoinTestFramework):
received_migrated_watchonly_tx_info = watchonly.gettransaction(received_watchonly_txid)
assert_equal(received_watchonly_tx_info["time"], received_migrated_watchonly_tx_info["time"])
assert_equal(received_watchonly_tx_info["timereceived"], received_migrated_watchonly_tx_info["timereceived"])
- received_sent_migrated_watchonly_tx_info = watchonly.gettransaction(received_sent_watchonly_txid)
+ received_sent_migrated_watchonly_tx_info = watchonly.gettransaction(received_sent_watchonly_utxo["txid"])
assert_equal(received_sent_watchonly_tx_info["time"], received_sent_migrated_watchonly_tx_info["time"])
assert_equal(received_sent_watchonly_tx_info["timereceived"], received_sent_migrated_watchonly_tx_info["timereceived"])
watchonly.gettransaction(sent_watchonly_txid)
diff --git a/test/functional/wallet_miniscript.py b/test/functional/wallet_miniscript.py
index d174b525b3..67e1283902 100755
--- a/test/functional/wallet_miniscript.py
+++ b/test/functional/wallet_miniscript.py
@@ -205,10 +205,10 @@ DESCS_PRIV = [
class WalletMiniscriptTest(BitcoinTestFramework):
def add_options(self, parser):
self.add_wallet_options(parser, legacy=False)
- self.rpc_timeout = 480
def set_test_params(self):
self.num_nodes = 1
+ self.rpc_timeout = 180
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
diff --git a/test/functional/wallet_reindex.py b/test/functional/wallet_reindex.py
new file mode 100755
index 0000000000..5388de4b71
--- /dev/null
+++ b/test/functional/wallet_reindex.py
@@ -0,0 +1,108 @@
+#!/usr/bin/env python3
+# Copyright (c) 2023-present The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or https://www.opensource.org/licenses/mit-license.php.
+
+"""Test wallet-reindex interaction"""
+
+import time
+
+from test_framework.blocktools import COINBASE_MATURITY
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import (
+ assert_equal,
+)
+BLOCK_TIME = 60 * 10
+
+class WalletReindexTest(BitcoinTestFramework):
+ def add_options(self, parser):
+ self.add_wallet_options(parser)
+
+ def set_test_params(self):
+ self.num_nodes = 1
+ self.setup_clean_chain = True
+
+ def skip_test_if_missing_module(self):
+ self.skip_if_no_wallet()
+
+ def advance_time(self, node, secs):
+ self.node_time += secs
+ node.setmocktime(self.node_time)
+
+ # Verify the wallet updates the birth time accordingly when it detects a transaction
+ # with a time older than the oldest descriptor timestamp.
+ # This could happen when the user blindly imports a descriptor with 'timestamp=now'.
+ def birthtime_test(self, node, miner_wallet):
+ self.log.info("Test birth time update during tx scanning")
+ # Fund address to test
+ wallet_addr = miner_wallet.getnewaddress()
+ tx_id = miner_wallet.sendtoaddress(wallet_addr, 2)
+
+ # Generate 50 blocks, one every 10 min to surpass the 2 hours rescan window the wallet has
+ for _ in range(50):
+ self.generate(node, 1)
+ self.advance_time(node, BLOCK_TIME)
+
+ # Now create a new wallet, and import the descriptor
+ node.createwallet(wallet_name='watch_only', disable_private_keys=True, load_on_startup=True)
+ wallet_watch_only = node.get_wallet_rpc('watch_only')
+ # Blank wallets don't have a birth time
+ assert 'birthtime' not in wallet_watch_only.getwalletinfo()
+
+ # For a descriptors wallet: Import address with timestamp=now.
+ # For legacy wallet: There is no way of importing a script/address with a custom time. The wallet always imports it with birthtime=1.
+ # In both cases, disable rescan to not detect the transaction.
+ wallet_watch_only.importaddress(wallet_addr, rescan=False)
+ assert_equal(len(wallet_watch_only.listtransactions()), 0)
+
+ # Depending on the wallet type, the birth time changes.
+ wallet_birthtime = wallet_watch_only.getwalletinfo()['birthtime']
+ if self.options.descriptors:
+ # As blocks were generated every 10 min, the chain MTP timestamp is node_time - 60 min.
+ assert_equal(self.node_time - BLOCK_TIME * 6, wallet_birthtime)
+ else:
+ # No way of importing scripts/addresses with a custom time on a legacy wallet.
+ # It's always set to the beginning of time.
+ assert_equal(wallet_birthtime, 1)
+
+ # Rescan the wallet to detect the missing transaction
+ wallet_watch_only.rescanblockchain()
+ assert_equal(wallet_watch_only.gettransaction(tx_id)['confirmations'], 50)
+ assert_equal(wallet_watch_only.getbalances()['mine' if self.options.descriptors else 'watchonly']['trusted'], 2)
+
+ # Reindex and wait for it to finish
+ with node.assert_debug_log(expected_msgs=["initload thread exit"]):
+ self.restart_node(0, extra_args=['-reindex=1', f'-mocktime={self.node_time}'])
+ node.syncwithvalidationinterfacequeue()
+
+ # Verify the transaction is still 'confirmed' after reindex
+ wallet_watch_only = node.get_wallet_rpc('watch_only')
+ tx_info = wallet_watch_only.gettransaction(tx_id)
+ assert_equal(tx_info['confirmations'], 50)
+
+ # Depending on the wallet type, the birth time changes.
+ if self.options.descriptors:
+ # For descriptors, verify the wallet updated the birth time to the transaction time
+ assert_equal(tx_info['time'], wallet_watch_only.getwalletinfo()['birthtime'])
+ else:
+ # For legacy, as the birth time was set to the beginning of time, verify it did not change
+ assert_equal(wallet_birthtime, 1)
+
+ wallet_watch_only.unloadwallet()
+
+ def run_test(self):
+ node = self.nodes[0]
+ self.node_time = int(time.time())
+ node.setmocktime(self.node_time)
+
+ # Fund miner
+ node.createwallet(wallet_name='miner', load_on_startup=True)
+ miner_wallet = node.get_wallet_rpc('miner')
+ self.generatetoaddress(node, COINBASE_MATURITY + 10, miner_wallet.getnewaddress())
+
+ # Tests
+ self.birthtime_test(node, miner_wallet)
+
+
+if __name__ == '__main__':
+ WalletReindexTest().main()
diff --git a/test/functional/wallet_signrawtransactionwithwallet.py b/test/functional/wallet_signrawtransactionwithwallet.py
index d560dfdc11..b0517f951d 100755
--- a/test/functional/wallet_signrawtransactionwithwallet.py
+++ b/test/functional/wallet_signrawtransactionwithwallet.py
@@ -14,7 +14,6 @@ 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.messages import (
CTxInWitness,
@@ -194,13 +193,12 @@ class SignRawTransactionWithWalletTest(BitcoinTestFramework):
address = script_to_p2wsh(script)
# Fund that address and make the spend
- txid = self.nodes[0].sendtoaddress(address, 1)
- vout = find_vout_for_address(self.nodes[0], txid, address)
+ utxo1 = self.create_outpoints(self.nodes[0], outputs=[{address: 1}])[0]
self.generate(self.nodes[0], 1)
- utxo = self.nodes[0].listunspent()[0]
- amt = Decimal(1) + utxo["amount"] - Decimal(0.00001)
+ utxo2 = self.nodes[0].listunspent()[0]
+ amt = Decimal(1) + utxo2["amount"] - Decimal(0.00001)
tx = self.nodes[0].createrawtransaction(
- [{"txid": txid, "vout": vout, "sequence": 1},{"txid": utxo["txid"], "vout": utxo["vout"]}],
+ [{**utxo1, "sequence": 1},{"txid": utxo2["txid"], "vout": utxo2["vout"]}],
[{self.nodes[0].getnewaddress(): amt}],
self.nodes[0].getblockcount()
)
@@ -229,13 +227,12 @@ class SignRawTransactionWithWalletTest(BitcoinTestFramework):
address = script_to_p2wsh(script)
# Fund that address and make the spend
- txid = self.nodes[0].sendtoaddress(address, 1)
- vout = find_vout_for_address(self.nodes[0], txid, address)
+ utxo1 = self.create_outpoints(self.nodes[0], outputs=[{address: 1}])[0]
self.generate(self.nodes[0], 1)
- utxo = self.nodes[0].listunspent()[0]
- amt = Decimal(1) + utxo["amount"] - Decimal(0.00001)
+ utxo2 = self.nodes[0].listunspent()[0]
+ amt = Decimal(1) + utxo2["amount"] - Decimal(0.00001)
tx = self.nodes[0].createrawtransaction(
- [{"txid": txid, "vout": vout},{"txid": utxo["txid"], "vout": utxo["vout"]}],
+ [utxo1, {"txid": utxo2["txid"], "vout": utxo2["vout"]}],
[{self.nodes[0].getnewaddress(): amt}],
self.nodes[0].getblockcount()
)
diff --git a/test/functional/wallet_txn_clone.py b/test/functional/wallet_txn_clone.py
index d8ef66d83a..1f3b6f2ce9 100755
--- a/test/functional/wallet_txn_clone.py
+++ b/test/functional/wallet_txn_clone.py
@@ -7,7 +7,6 @@
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
- find_vout_for_address
)
from test_framework.messages import (
COIN,
@@ -35,8 +34,8 @@ class TxnMallTest(BitcoinTestFramework):
super().setup_network()
self.disconnect_nodes(1, 2)
- def spend_txid(self, txid, vout, outputs):
- inputs = [{"txid": txid, "vout": vout}]
+ def spend_utxo(self, utxo, outputs):
+ inputs = [utxo]
tx = self.nodes[0].createrawtransaction(inputs, outputs)
tx = self.nodes[0].fundrawtransaction(tx)
tx = self.nodes[0].signrawtransactionwithwallet(tx['hex'])
@@ -56,13 +55,13 @@ class TxnMallTest(BitcoinTestFramework):
self.nodes[0].settxfee(.001)
node0_address1 = self.nodes[0].getnewaddress(address_type=output_type)
- node0_txid1 = self.nodes[0].sendtoaddress(node0_address1, 1219)
- node0_tx1 = self.nodes[0].gettransaction(node0_txid1)
- self.nodes[0].lockunspent(False, [{"txid":node0_txid1, "vout": find_vout_for_address(self.nodes[0], node0_txid1, node0_address1)}])
+ node0_utxo1 = self.create_outpoints(self.nodes[0], outputs=[{node0_address1: 1219}])[0]
+ node0_tx1 = self.nodes[0].gettransaction(node0_utxo1['txid'])
+ self.nodes[0].lockunspent(False, [node0_utxo1])
node0_address2 = self.nodes[0].getnewaddress(address_type=output_type)
- node0_txid2 = self.nodes[0].sendtoaddress(node0_address2, 29)
- node0_tx2 = self.nodes[0].gettransaction(node0_txid2)
+ node0_utxo2 = self.create_outpoints(self.nodes[0], outputs=[{node0_address2: 29}])[0]
+ node0_tx2 = self.nodes[0].gettransaction(node0_utxo2['txid'])
assert_equal(self.nodes[0].getbalance(),
starting_balance + node0_tx1["fee"] + node0_tx2["fee"])
@@ -71,8 +70,8 @@ class TxnMallTest(BitcoinTestFramework):
node1_address = self.nodes[1].getnewaddress()
# Send tx1, and another transaction tx2 that won't be cloned
- txid1 = self.spend_txid(node0_txid1, find_vout_for_address(self.nodes[0], node0_txid1, node0_address1), {node1_address: 40})
- txid2 = self.spend_txid(node0_txid2, find_vout_for_address(self.nodes[0], node0_txid2, node0_address2), {node1_address: 20})
+ txid1 = self.spend_utxo(node0_utxo1, {node1_address: 40})
+ txid2 = self.spend_utxo(node0_utxo2, {node1_address: 20})
# Construct a clone of tx1, to be malleated
rawtx1 = self.nodes[0].getrawtransaction(txid1, 1)
diff --git a/test/functional/wallet_txn_doublespend.py b/test/functional/wallet_txn_doublespend.py
index 38ebfe0d7a..3cd0cd3207 100755
--- a/test/functional/wallet_txn_doublespend.py
+++ b/test/functional/wallet_txn_doublespend.py
@@ -8,8 +8,6 @@ from decimal import Decimal
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
- find_output,
- find_vout_for_address
)
@@ -31,8 +29,8 @@ class TxnMallTest(BitcoinTestFramework):
super().setup_network()
self.disconnect_nodes(1, 2)
- def spend_txid(self, txid, vout, outputs):
- inputs = [{"txid": txid, "vout": vout}]
+ def spend_utxo(self, utxo, outputs):
+ inputs = [utxo]
tx = self.nodes[0].createrawtransaction(inputs, outputs)
tx = self.nodes[0].fundrawtransaction(tx)
tx = self.nodes[0].signrawtransactionwithwallet(tx['hex'])
@@ -54,13 +52,13 @@ class TxnMallTest(BitcoinTestFramework):
# Assign coins to foo and bar addresses:
node0_address_foo = self.nodes[0].getnewaddress()
- fund_foo_txid = self.nodes[0].sendtoaddress(node0_address_foo, 1219)
- fund_foo_tx = self.nodes[0].gettransaction(fund_foo_txid)
- self.nodes[0].lockunspent(False, [{"txid":fund_foo_txid, "vout": find_vout_for_address(self.nodes[0], fund_foo_txid, node0_address_foo)}])
+ fund_foo_utxo = self.create_outpoints(self.nodes[0], outputs=[{node0_address_foo: 1219}])[0]
+ fund_foo_tx = self.nodes[0].gettransaction(fund_foo_utxo['txid'])
+ self.nodes[0].lockunspent(False, [fund_foo_utxo])
node0_address_bar = self.nodes[0].getnewaddress()
- fund_bar_txid = self.nodes[0].sendtoaddress(node0_address_bar, 29)
- fund_bar_tx = self.nodes[0].gettransaction(fund_bar_txid)
+ fund_bar_utxo = self.create_outpoints(node=self.nodes[0], outputs=[{node0_address_bar: 29}])[0]
+ fund_bar_tx = self.nodes[0].gettransaction(fund_bar_utxo['txid'])
assert_equal(self.nodes[0].getbalance(),
starting_balance + fund_foo_tx["fee"] + fund_bar_tx["fee"])
@@ -71,13 +69,7 @@ class TxnMallTest(BitcoinTestFramework):
# First: use raw transaction API to send 1240 BTC to node1_address,
# but don't broadcast:
doublespend_fee = Decimal('-.02')
- rawtx_input_0 = {}
- rawtx_input_0["txid"] = fund_foo_txid
- rawtx_input_0["vout"] = find_output(self.nodes[0], fund_foo_txid, 1219)
- rawtx_input_1 = {}
- rawtx_input_1["txid"] = fund_bar_txid
- rawtx_input_1["vout"] = find_output(self.nodes[0], fund_bar_txid, 29)
- inputs = [rawtx_input_0, rawtx_input_1]
+ inputs = [fund_foo_utxo, fund_bar_utxo]
change_address = self.nodes[0].getnewaddress()
outputs = {}
outputs[node1_address] = 1240
@@ -87,8 +79,8 @@ class TxnMallTest(BitcoinTestFramework):
assert_equal(doublespend["complete"], True)
# Create two spends using 1 50 BTC coin each
- txid1 = self.spend_txid(fund_foo_txid, find_vout_for_address(self.nodes[0], fund_foo_txid, node0_address_foo), {node1_address: 40})
- txid2 = self.spend_txid(fund_bar_txid, find_vout_for_address(self.nodes[0], fund_bar_txid, node0_address_bar), {node1_address: 20})
+ txid1 = self.spend_utxo(fund_foo_utxo, {node1_address: 40})
+ txid2 = self.spend_utxo(fund_bar_utxo, {node1_address: 20})
# Have node0 mine a block:
if (self.options.mine_block):