aboutsummaryrefslogtreecommitdiff
path: root/test/functional
diff options
context:
space:
mode:
Diffstat (limited to 'test/functional')
-rwxr-xr-xtest/functional/feature_blockfilterindex_prune.py78
-rwxr-xr-xtest/functional/feature_coinstatsindex.py16
-rwxr-xr-xtest/functional/feature_config_args.py3
-rwxr-xr-xtest/functional/feature_csv_activation.py8
-rwxr-xr-xtest/functional/feature_index_prune.py156
-rwxr-xr-xtest/functional/feature_pruning.py5
-rwxr-xr-xtest/functional/feature_taproot.py110
-rwxr-xr-xtest/functional/feature_versionbits_warning.py3
-rwxr-xr-xtest/functional/interface_rest.py12
-rwxr-xr-xtest/functional/interface_usdt_coinselection.py208
-rwxr-xr-xtest/functional/p2p_addr_relay.py66
-rwxr-xr-xtest/functional/p2p_blockfilters.py6
-rwxr-xr-xtest/functional/rpc_misc.py2
-rwxr-xr-xtest/functional/rpc_psbt.py17
-rwxr-xr-xtest/functional/rpc_users.py3
-rwxr-xr-xtest/functional/test_framework/test_node.py1
-rw-r--r--test/functional/test_framework/wallet.py11
-rwxr-xr-xtest/functional/test_runner.py5
-rwxr-xr-xtest/functional/wallet_createwallet.py2
-rwxr-xr-xtest/functional/wallet_crosschain.py60
-rwxr-xr-xtest/functional/wallet_listreceivedby.py7
-rwxr-xr-xtest/functional/wallet_send.py17
-rwxr-xr-xtest/functional/wallet_taproot.py14
23 files changed, 594 insertions, 216 deletions
diff --git a/test/functional/feature_blockfilterindex_prune.py b/test/functional/feature_blockfilterindex_prune.py
deleted file mode 100755
index c983ceda6f..0000000000
--- a/test/functional/feature_blockfilterindex_prune.py
+++ /dev/null
@@ -1,78 +0,0 @@
-#!/usr/bin/env python3
-# Copyright (c) 2020-2021 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 blockfilterindex in conjunction with prune."""
-from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import (
- assert_equal,
- assert_greater_than,
- assert_raises_rpc_error,
-)
-
-
-class FeatureBlockfilterindexPruneTest(BitcoinTestFramework):
- def set_test_params(self):
- self.num_nodes = 1
- self.extra_args = [["-fastprune", "-prune=1", "-blockfilterindex=1"]]
-
- def sync_index(self, height):
- expected = {'basic block filter index': {'synced': True, 'best_block_height': height}}
- self.wait_until(lambda: self.nodes[0].getindexinfo() == expected)
-
- def run_test(self):
- self.log.info("check if we can access a blockfilter when pruning is enabled but no blocks are actually pruned")
- self.sync_index(height=200)
- assert_greater_than(len(self.nodes[0].getblockfilter(self.nodes[0].getbestblockhash())['filter']), 0)
- self.generate(self.nodes[0], 500)
- self.sync_index(height=700)
-
- self.log.info("prune some blocks")
- pruneheight = self.nodes[0].pruneblockchain(400)
- # the prune heights used here and below are magic numbers that are determined by the
- # thresholds at which block files wrap, so they depend on disk serialization and default block file size.
- assert_equal(pruneheight, 249)
-
- self.log.info("check if we can access the tips blockfilter when we have pruned some blocks")
- assert_greater_than(len(self.nodes[0].getblockfilter(self.nodes[0].getbestblockhash())['filter']), 0)
-
- self.log.info("check if we can access the blockfilter of a pruned block")
- assert_greater_than(len(self.nodes[0].getblockfilter(self.nodes[0].getblockhash(2))['filter']), 0)
-
- # mine and sync index up to a height that will later be the pruneheight
- self.generate(self.nodes[0], 51)
- self.sync_index(height=751)
-
- self.log.info("start node without blockfilterindex")
- self.restart_node(0, extra_args=["-fastprune", "-prune=1"])
-
- self.log.info("make sure accessing the blockfilters throws an error")
- assert_raises_rpc_error(-1, "Index is not enabled for filtertype basic", self.nodes[0].getblockfilter, self.nodes[0].getblockhash(2))
- self.generate(self.nodes[0], 749)
-
- self.log.info("prune exactly up to the blockfilterindexes best block while blockfilters are disabled")
- pruneheight_2 = self.nodes[0].pruneblockchain(1000)
- assert_equal(pruneheight_2, 751)
- self.restart_node(0, extra_args=["-fastprune", "-prune=1", "-blockfilterindex=1"])
- self.log.info("make sure that we can continue with the partially synced index after having pruned up to the index height")
- self.sync_index(height=1500)
-
- self.log.info("prune below the blockfilterindexes best block while blockfilters are disabled")
- self.restart_node(0, extra_args=["-fastprune", "-prune=1"])
- self.generate(self.nodes[0], 1000)
- pruneheight_3 = self.nodes[0].pruneblockchain(2000)
- assert_greater_than(pruneheight_3, pruneheight_2)
- self.stop_node(0)
-
- self.log.info("make sure we get an init error when starting the node again with block filters")
- self.nodes[0].assert_start_raises_init_error(
- extra_args=["-fastprune", "-prune=1", "-blockfilterindex=1"],
- expected_msg="Error: basic block filter index best block of the index goes beyond pruned data. Please disable the index or reindex (which will download the whole blockchain again)",
- )
-
- self.log.info("make sure the node starts again with the -reindex arg")
- self.start_node(0, extra_args=["-fastprune", "-prune=1", "-blockfilterindex", "-reindex"])
-
-
-if __name__ == '__main__':
- FeatureBlockfilterindexPruneTest().main()
diff --git a/test/functional/feature_coinstatsindex.py b/test/functional/feature_coinstatsindex.py
index f865661894..2e21638f80 100755
--- a/test/functional/feature_coinstatsindex.py
+++ b/test/functional/feature_coinstatsindex.py
@@ -223,6 +223,22 @@ class CoinStatsIndexTest(BitcoinTestFramework):
res10 = index_node.gettxoutsetinfo('muhash')
assert(res8['txouts'] < res10['txouts'])
+ self.log.info("Test that the index works with -reindex")
+
+ self.restart_node(1, extra_args=["-coinstatsindex", "-reindex"])
+ res11 = index_node.gettxoutsetinfo('muhash')
+ assert_equal(res11, res10)
+
+ self.log.info("Test that -reindex-chainstate is disallowed with coinstatsindex")
+
+ self.stop_node(1)
+ self.nodes[1].assert_start_raises_init_error(
+ expected_msg='Error: -reindex-chainstate option is not compatible with -coinstatsindex. '
+ 'Please temporarily disable coinstatsindex while using -reindex-chainstate, or replace -reindex-chainstate with -reindex to fully rebuild all indexes.',
+ extra_args=['-coinstatsindex', '-reindex-chainstate'],
+ )
+ self.restart_node(1, extra_args=["-coinstatsindex"])
+
def _test_use_index_option(self):
self.log.info("Test use_index option for nodes running the index")
diff --git a/test/functional/feature_config_args.py b/test/functional/feature_config_args.py
index eea5fa24ee..fe3196d5ee 100755
--- a/test/functional/feature_config_args.py
+++ b/test/functional/feature_config_args.py
@@ -247,7 +247,8 @@ class ConfArgsTest(BitcoinTestFramework):
conf_file = os.path.join(default_data_dir, "bitcoin.conf")
# datadir needs to be set before [chain] section
- conf_file_contents = open(conf_file, encoding='utf8').read()
+ with open(conf_file, encoding='utf8') as f:
+ conf_file_contents = f.read()
with open(conf_file, 'w', encoding='utf8') as f:
f.write(f"datadir={new_data_dir}\n")
f.write(conf_file_contents)
diff --git a/test/functional/feature_csv_activation.py b/test/functional/feature_csv_activation.py
index 6470c1c5eb..bff95c3b94 100755
--- a/test/functional/feature_csv_activation.py
+++ b/test/functional/feature_csv_activation.py
@@ -112,6 +112,7 @@ class BIP68_112_113Test(BitcoinTestFramework):
tx.nVersion = txversion
self.miniwallet.sign_tx(tx)
tx.vin[0].scriptSig = CScript([-1, OP_CHECKSEQUENCEVERIFY, OP_DROP] + list(CScript(tx.vin[0].scriptSig)))
+ tx.rehash()
return tx
def create_bip112emptystack(self, input, txversion):
@@ -119,6 +120,7 @@ class BIP68_112_113Test(BitcoinTestFramework):
tx.nVersion = txversion
self.miniwallet.sign_tx(tx)
tx.vin[0].scriptSig = CScript([OP_CHECKSEQUENCEVERIFY] + list(CScript(tx.vin[0].scriptSig)))
+ tx.rehash()
return tx
def send_generic_input_tx(self, coinbases):
@@ -136,7 +138,6 @@ class BIP68_112_113Test(BitcoinTestFramework):
tx.nVersion = txversion
tx.vin[0].nSequence = locktime + locktime_delta
self.miniwallet.sign_tx(tx)
- tx.rehash()
txs.append({'tx': tx, 'sdf': sdf, 'stf': stf})
return txs
@@ -339,20 +340,16 @@ class BIP68_112_113Test(BitcoinTestFramework):
# BIP 113 tests should now fail regardless of version number if nLockTime isn't satisfied by new rules
bip113tx_v1.nLockTime = self.last_block_time - 600 * 5 # = MTP of prior block (not <) but < time put on current block
self.miniwallet.sign_tx(bip113tx_v1)
- bip113tx_v1.rehash()
bip113tx_v2.nLockTime = self.last_block_time - 600 * 5 # = MTP of prior block (not <) but < time put on current block
self.miniwallet.sign_tx(bip113tx_v2)
- bip113tx_v2.rehash()
for bip113tx in [bip113tx_v1, bip113tx_v2]:
self.send_blocks([self.create_test_block([bip113tx])], success=False, reject_reason='bad-txns-nonfinal')
# BIP 113 tests should now pass if the locktime is < MTP
bip113tx_v1.nLockTime = self.last_block_time - 600 * 5 - 1 # < MTP of prior block
self.miniwallet.sign_tx(bip113tx_v1)
- bip113tx_v1.rehash()
bip113tx_v2.nLockTime = self.last_block_time - 600 * 5 - 1 # < MTP of prior block
self.miniwallet.sign_tx(bip113tx_v2)
- bip113tx_v2.rehash()
for bip113tx in [bip113tx_v1, bip113tx_v2]:
self.send_blocks([self.create_test_block([bip113tx])])
self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash())
@@ -477,7 +474,6 @@ class BIP68_112_113Test(BitcoinTestFramework):
for tx in [tx['tx'] for tx in bip112txs_vary_OP_CSV_v2 if not tx['sdf'] and tx['stf']]:
tx.vin[0].nSequence = BASE_RELATIVE_LOCKTIME | SEQ_TYPE_FLAG
self.miniwallet.sign_tx(tx)
- tx.rehash()
time_txs.append(tx)
self.send_blocks([self.create_test_block(time_txs)])
diff --git a/test/functional/feature_index_prune.py b/test/functional/feature_index_prune.py
new file mode 100755
index 0000000000..2bf57db923
--- /dev/null
+++ b/test/functional/feature_index_prune.py
@@ -0,0 +1,156 @@
+#!/usr/bin/env python3
+# Copyright (c) 2020-2021 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 indices in conjunction with prune."""
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import (
+ assert_equal,
+ assert_greater_than,
+ assert_raises_rpc_error,
+ p2p_port,
+)
+
+
+class FeatureIndexPruneTest(BitcoinTestFramework):
+ def set_test_params(self):
+ self.num_nodes = 4
+ self.extra_args = [
+ ["-fastprune", "-prune=1", "-blockfilterindex=1"],
+ ["-fastprune", "-prune=1", "-coinstatsindex=1"],
+ ["-fastprune", "-prune=1", "-blockfilterindex=1", "-coinstatsindex=1"],
+ []
+ ]
+
+ def sync_index(self, height):
+ expected_filter = {
+ 'basic block filter index': {'synced': True, 'best_block_height': height},
+ }
+ self.wait_until(lambda: self.nodes[0].getindexinfo() == expected_filter)
+
+ expected_stats = {
+ 'coinstatsindex': {'synced': True, 'best_block_height': height}
+ }
+ self.wait_until(lambda: self.nodes[1].getindexinfo() == expected_stats)
+
+ expected = {**expected_filter, **expected_stats}
+ self.wait_until(lambda: self.nodes[2].getindexinfo() == expected)
+
+ def reconnect_nodes(self):
+ self.connect_nodes(0,1)
+ self.connect_nodes(0,2)
+ self.connect_nodes(0,3)
+
+ def mine_batches(self, blocks):
+ n = blocks // 250
+ for _ in range(n):
+ self.generate(self.nodes[0], 250)
+ self.generate(self.nodes[0], blocks % 250)
+ self.sync_blocks()
+
+ def restart_without_indices(self):
+ for i in range(3):
+ self.restart_node(i, extra_args=["-fastprune", "-prune=1"])
+ self.reconnect_nodes()
+
+ def run_test(self):
+ filter_nodes = [self.nodes[0], self.nodes[2]]
+ stats_nodes = [self.nodes[1], self.nodes[2]]
+
+ self.log.info("check if we can access blockfilters and coinstats when pruning is enabled but no blocks are actually pruned")
+ self.sync_index(height=200)
+ tip = self.nodes[0].getbestblockhash()
+ for node in filter_nodes:
+ assert_greater_than(len(node.getblockfilter(tip)['filter']), 0)
+ for node in stats_nodes:
+ assert(node.gettxoutsetinfo(hash_type="muhash", hash_or_height=tip)['muhash'])
+
+ self.mine_batches(500)
+ self.sync_index(height=700)
+
+ self.log.info("prune some blocks")
+ for node in self.nodes[:2]:
+ with node.assert_debug_log(['limited pruning to height 689']):
+ pruneheight_new = node.pruneblockchain(400)
+ # the prune heights used here and below are magic numbers that are determined by the
+ # thresholds at which block files wrap, so they depend on disk serialization and default block file size.
+ assert_equal(pruneheight_new, 249)
+
+ self.log.info("check if we can access the tips blockfilter and coinstats when we have pruned some blocks")
+ tip = self.nodes[0].getbestblockhash()
+ for node in filter_nodes:
+ assert_greater_than(len(node.getblockfilter(tip)['filter']), 0)
+ for node in stats_nodes:
+ assert(node.gettxoutsetinfo(hash_type="muhash", hash_or_height=tip)['muhash'])
+
+ self.log.info("check if we can access the blockfilter and coinstats of a pruned block")
+ height_hash = self.nodes[0].getblockhash(2)
+ for node in filter_nodes:
+ assert_greater_than(len(node.getblockfilter(height_hash)['filter']), 0)
+ for node in stats_nodes:
+ assert(node.gettxoutsetinfo(hash_type="muhash", hash_or_height=height_hash)['muhash'])
+
+ # mine and sync index up to a height that will later be the pruneheight
+ self.generate(self.nodes[0], 51)
+ self.sync_index(height=751)
+
+ self.restart_without_indices()
+
+ self.log.info("make sure trying to access the indices throws errors")
+ for node in filter_nodes:
+ msg = "Index is not enabled for filtertype basic"
+ assert_raises_rpc_error(-1, msg, node.getblockfilter, height_hash)
+ for node in stats_nodes:
+ msg = "Querying specific block heights requires coinstatsindex"
+ assert_raises_rpc_error(-8, msg, node.gettxoutsetinfo, "muhash", height_hash)
+
+ self.mine_batches(749)
+
+ self.log.info("prune exactly up to the indices best blocks while the indices are disabled")
+ for i in range(3):
+ pruneheight_2 = self.nodes[i].pruneblockchain(1000)
+ assert_equal(pruneheight_2, 751)
+ # Restart the nodes again with the indices activated
+ self.restart_node(i, extra_args=self.extra_args[i])
+
+ self.log.info("make sure that we can continue with the partially synced indices after having pruned up to the index height")
+ self.sync_index(height=1500)
+
+ self.log.info("prune further than the indices best blocks while the indices are disabled")
+ self.restart_without_indices()
+ self.mine_batches(1000)
+
+ for i in range(3):
+ pruneheight_3 = self.nodes[i].pruneblockchain(2000)
+ assert_greater_than(pruneheight_3, pruneheight_2)
+ self.stop_node(i)
+
+ self.log.info("make sure we get an init error when starting the nodes again with the indices")
+ filter_msg = "Error: basic block filter index best block of the index goes beyond pruned data. Please disable the index or reindex (which will download the whole blockchain again)"
+ stats_msg = "Error: coinstatsindex best block of the index goes beyond pruned data. Please disable the index or reindex (which will download the whole blockchain again)"
+ for i, msg in enumerate([filter_msg, stats_msg, filter_msg]):
+ self.nodes[i].assert_start_raises_init_error(extra_args=self.extra_args[i], expected_msg=msg)
+
+ self.log.info("make sure the nodes start again with the indices and an additional -reindex arg")
+ ip_port = "127.0.0.1:" + str(p2p_port(3))
+ for i in range(3):
+ # The nodes need to be reconnected to the non-pruning node upon restart, otherwise they will be stuck
+ restart_args = self.extra_args[i]+["-reindex", f"-connect={ip_port}"]
+ self.restart_node(i, extra_args=restart_args)
+
+ self.sync_blocks(timeout=300)
+
+ for node in self.nodes[:2]:
+ with node.assert_debug_log(['limited pruning to height 2489']):
+ pruneheight_new = node.pruneblockchain(2500)
+ assert_equal(pruneheight_new, 2006)
+
+ self.log.info("ensure that prune locks don't prevent indices from failing in a reorg scenario")
+ with self.nodes[0].assert_debug_log(['basic block filter index prune lock moved back to 2480']):
+ self.nodes[3].invalidateblock(self.nodes[0].getblockhash(2480))
+ self.generate(self.nodes[3], 30)
+ self.sync_blocks()
+
+
+if __name__ == '__main__':
+ FeatureIndexPruneTest().main()
diff --git a/test/functional/feature_pruning.py b/test/functional/feature_pruning.py
index bf19384279..77524e85a3 100755
--- a/test/functional/feature_pruning.py
+++ b/test/functional/feature_pruning.py
@@ -125,6 +125,7 @@ class PruneTest(BitcoinTestFramework):
self.sync_blocks(self.nodes[0:5])
def test_invalid_command_line_options(self):
+ self.stop_node(0)
self.nodes[0].assert_start_raises_init_error(
expected_msg='Error: Prune cannot be configured with a negative value.',
extra_args=['-prune=-1'],
@@ -138,10 +139,6 @@ class PruneTest(BitcoinTestFramework):
extra_args=['-prune=550', '-txindex'],
)
self.nodes[0].assert_start_raises_init_error(
- expected_msg='Error: Prune mode is incompatible with -coinstatsindex.',
- extra_args=['-prune=550', '-coinstatsindex'],
- )
- self.nodes[0].assert_start_raises_init_error(
expected_msg='Error: Prune mode is incompatible with -reindex-chainstate. Use full -reindex instead.',
extra_args=['-prune=550', '-reindex-chainstate'],
)
diff --git a/test/functional/feature_taproot.py b/test/functional/feature_taproot.py
index c3925dbb00..0e44038196 100755
--- a/test/functional/feature_taproot.py
+++ b/test/functional/feature_taproot.py
@@ -10,7 +10,6 @@ from test_framework.blocktools import (
create_block,
add_witness_commitment,
MAX_BLOCK_SIGOPS_WEIGHT,
- NORMAL_GBT_REQUEST_PARAMS,
WITNESS_SCALE_FACTOR,
)
from test_framework.messages import (
@@ -96,10 +95,9 @@ from test_framework.util import assert_raises_rpc_error, assert_equal
from test_framework.key import generate_privkey, compute_xonly_pubkey, sign_schnorr, tweak_add_privkey, ECKey
from test_framework.address import (
hash160,
- program_to_witness
+ program_to_witness,
)
from collections import OrderedDict, namedtuple
-from enum import Enum
from io import BytesIO
import json
import hashlib
@@ -458,7 +456,7 @@ def spend(tx, idx, utxos, **kwargs):
# Each spender is a tuple of:
# - A scriptPubKey which is to be spent from (CScript)
# - A comment describing the test (string)
-# - Whether the spending (on itself) is expected to be standard (Enum.Standard)
+# - Whether the spending (on itself) is expected to be standard (bool)
# - A tx-signing lambda returning (scriptsig, witness_stack), taking as inputs:
# - A transaction to sign (CTransaction)
# - An input position (int)
@@ -470,14 +468,9 @@ def spend(tx, idx, utxos, **kwargs):
# - Whether this test demands being placed in a txin with no corresponding txout (for testing SIGHASH_SINGLE behavior)
Spender = namedtuple("Spender", "script,comment,is_standard,sat_function,err_msg,sigops_weight,no_fail,need_vin_vout_mismatch")
-# The full node versions that treat the tx standard.
-# ALL means any version
-# V23 means the major version 23.0 and any later version
-# NONE means no version
-Standard = Enum('Standard', 'ALL V23 NONE')
-def make_spender(comment, *, tap=None, witv0=False, script=None, pkh=None, p2sh=False, spk_mutate_pre_p2sh=None, failure=None, standard=Standard.ALL, err_msg=None, sigops_weight=0, need_vin_vout_mismatch=False, **kwargs):
+def make_spender(comment, *, tap=None, witv0=False, script=None, pkh=None, p2sh=False, spk_mutate_pre_p2sh=None, failure=None, standard=True, err_msg=None, sigops_weight=0, need_vin_vout_mismatch=False, **kwargs):
"""Helper for constructing Spender objects using the context signing framework.
* tap: a TaprootInfo object (see taproot_construct), for Taproot spends (cannot be combined with pkh, witv0, or script)
@@ -487,18 +480,13 @@ def make_spender(comment, *, tap=None, witv0=False, script=None, pkh=None, p2sh=
* p2sh: whether the output is P2SH wrapper (this is supported even for Taproot, where it makes the output unencumbered)
* spk_mutate_pre_psh: a callable to be applied to the script (before potentially P2SH-wrapping it)
* failure: a dict of entries to override in the context when intentionally failing to spend (if None, no_fail will be set)
- * standard: whether the (valid version of) spending is expected to be standard (True is mapped to Standard.ALL, False is mapped to Standard.NONE)
+ * standard: whether the (valid version of) spending is expected to be standard
* err_msg: a string with an expected error message for failure (or None, if not cared about)
* sigops_weight: the pre-taproot sigops weight consumed by a successful spend
* need_vin_vout_mismatch: whether this test requires being tested in a transaction input that has no corresponding
transaction output.
"""
- if standard == True:
- standard = Standard.ALL
- elif standard == False:
- standard = Standard.NONE
-
conf = dict()
# Compute scriptPubKey and set useful defaults based on the inputs.
@@ -1168,24 +1156,20 @@ def spenders_taproot_active():
return spenders
-def spenders_taproot_inactive():
- """Spenders for testing that pre-activation Taproot rules don't apply."""
+
+def spenders_taproot_nonstandard():
+ """Spenders for testing that post-activation Taproot rules may be nonstandard."""
spenders = []
sec = generate_privkey()
pub, _ = compute_xonly_pubkey(sec)
scripts = [
- ("pk", CScript([pub, OP_CHECKSIG])),
("future_leaf", CScript([pub, OP_CHECKSIG]), 0xc2),
("op_success", CScript([pub, OP_CHECKSIG, OP_0, OP_IF, CScriptOp(0x50), OP_ENDIF])),
]
tap = taproot_construct(pub, scripts)
- # Test that valid spending is standard.
- add_spender(spenders, "inactive/keypath_valid", key=sec, tap=tap, standard=Standard.V23)
- add_spender(spenders, "inactive/scriptpath_valid", key=sec, tap=tap, leaf="pk", standard=Standard.V23, inputs=[getter("sign")])
-
# Test that features like annex, leaf versions, or OP_SUCCESS are valid but non-standard
add_spender(spenders, "inactive/scriptpath_valid_unkleaf", key=sec, tap=tap, leaf="future_leaf", standard=False, inputs=[getter("sign")])
add_spender(spenders, "inactive/scriptpath_invalid_unkleaf", key=sec, tap=tap, leaf="future_leaf", standard=False, inputs=[getter("sign")], sighash=bitflipper(default_sighash))
@@ -1214,7 +1198,7 @@ def dump_json_test(tx, input_utxos, idx, success, failure):
# The "final" field indicates that a spend should be always valid, even with more validation flags enabled
# than the listed ones. Use standardness as a proxy for this (which gives a conservative underestimate).
- if spender.is_standard == Standard.ALL:
+ if spender.is_standard:
fields.append(("final", True))
def dump_witness(wit):
@@ -1241,31 +1225,14 @@ class TaprootTest(BitcoinTestFramework):
def add_options(self, parser):
parser.add_argument("--dumptests", dest="dump_tests", default=False, action="store_true",
help="Dump generated test cases to directory set by TEST_DUMP_DIR environment variable")
- parser.add_argument("--previous_release", dest="previous_release", default=False, action="store_true",
- help="Use a previous release as taproot-inactive node")
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
- if self.options.previous_release:
- self.skip_if_no_previous_releases()
def set_test_params(self):
- self.num_nodes = 2
+ self.num_nodes = 1
self.setup_clean_chain = True
- # Node 0 has Taproot inactive, Node 1 active.
- self.extra_args = [["-par=1"], ["-par=1"]]
- if self.options.previous_release:
- self.wallet_names = [None, self.default_wallet_name]
- else:
- self.extra_args[0].append("-vbparams=taproot:1:1")
-
- def setup_nodes(self):
- self.add_nodes(self.num_nodes, self.extra_args, versions=[
- 200100 if self.options.previous_release else None,
- None,
- ])
- self.start_nodes()
- self.import_deterministic_coinbase_privkeys()
+ self.extra_args = [["-par=1"]]
def block_submit(self, node, txs, msg, err_msg, cb_pubkey=None, fees=0, sigops_weight=0, witness=False, accept=False):
@@ -1479,11 +1446,10 @@ class TaprootTest(BitcoinTestFramework):
for i in range(len(input_utxos)):
tx.vin[i].scriptSig = input_data[i][i != fail_input][0]
tx.wit.vtxinwit[i].scriptWitness.stack = input_data[i][i != fail_input][1]
- taproot_spend_policy = Standard.V23 if node.version is None else Standard.ALL
# Submit to mempool to check standardness
is_standard_tx = (
fail_input is None # Must be valid to be standard
- and (all(utxo.spender.is_standard == Standard.ALL or utxo.spender.is_standard == taproot_spend_policy for utxo in input_utxos)) # All inputs must be standard
+ and (all(utxo.spender.is_standard for utxo in input_utxos)) # All inputs must be standard
and tx.nVersion >= 1 # The tx version must be standard
and tx.nVersion <= 2)
tx.rehash()
@@ -1510,7 +1476,7 @@ class TaprootTest(BitcoinTestFramework):
self.log.info("Unit test scenario...")
# Deterministically mine coins to OP_TRUE in block 1
- assert self.nodes[1].getblockcount() == 0
+ assert_equal(self.nodes[0].getblockcount(), 0)
coinbase = CTransaction()
coinbase.nVersion = 1
coinbase.vin = [CTxIn(COutPoint(0, 0xffffffff), CScript([OP_1, OP_1]), SEQUENCE_FINAL)]
@@ -1519,12 +1485,12 @@ class TaprootTest(BitcoinTestFramework):
coinbase.rehash()
assert coinbase.hash == "f60c73405d499a956d3162e3483c395526ef78286458a4cb17b125aa92e49b20"
# Mine it
- block = create_block(hashprev=int(self.nodes[1].getbestblockhash(), 16), coinbase=coinbase)
+ block = create_block(hashprev=int(self.nodes[0].getbestblockhash(), 16), coinbase=coinbase)
block.rehash()
block.solve()
- self.nodes[1].submitblock(block.serialize().hex())
- assert self.nodes[1].getblockcount() == 1
- self.generate(self.nodes[1], COINBASE_MATURITY)
+ self.nodes[0].submitblock(block.serialize().hex())
+ assert_equal(self.nodes[0].getblockcount(), 1)
+ self.generate(self.nodes[0], COINBASE_MATURITY)
SEED = 317
VALID_LEAF_VERS = list(range(0xc0, 0x100, 2)) + [0x66, 0x7e, 0x80, 0x84, 0x96, 0x98, 0xba, 0xbc, 0xbe]
@@ -1613,8 +1579,8 @@ class TaprootTest(BitcoinTestFramework):
spend_info[spk]['prevout'] = COutPoint(tx.sha256, i & 1)
spend_info[spk]['utxo'] = CTxOut(val, spk)
# Mine those transactions
- self.init_blockinfo(self.nodes[1])
- self.block_submit(self.nodes[1], txn, "Crediting txn", None, sigops_weight=10, accept=True)
+ self.init_blockinfo(self.nodes[0])
+ self.block_submit(self.nodes[0], txn, "Crediting txn", None, sigops_weight=10, accept=True)
# scriptPubKey computation
tests = {"version": 1}
@@ -1726,53 +1692,21 @@ class TaprootTest(BitcoinTestFramework):
keypath_tests.append(tx_test)
assert_equal(hashlib.sha256(tx.serialize()).hexdigest(), "24bab662cb55a7f3bae29b559f651674c62bcc1cd442d44715c0133939107b38")
# Mine the spending transaction
- self.block_submit(self.nodes[1], [tx], "Spending txn", None, sigops_weight=10000, accept=True, witness=True)
+ self.block_submit(self.nodes[0], [tx], "Spending txn", None, sigops_weight=10000, accept=True, witness=True)
if GEN_TEST_VECTORS:
print(json.dumps(tests, indent=4, sort_keys=False))
-
def run_test(self):
self.gen_test_vectors()
- # Post-taproot activation tests go first (pre-taproot tests' blocks are invalid post-taproot).
self.log.info("Post-activation tests...")
- self.test_spenders(self.nodes[1], spenders_taproot_active(), input_counts=[1, 2, 2, 2, 2, 3])
-
- # Re-connect nodes in case they have been disconnected
- self.disconnect_nodes(0, 1)
- self.connect_nodes(0, 1)
-
- # Transfer value of the largest 500 coins to pre-taproot node.
- addr = self.nodes[0].getnewaddress()
-
- unsp = self.nodes[1].listunspent()
- unsp = sorted(unsp, key=lambda i: i['amount'], reverse=True)
- unsp = unsp[:500]
-
- rawtx = self.nodes[1].createrawtransaction(
- inputs=[{
- 'txid': i['txid'],
- 'vout': i['vout']
- } for i in unsp],
- outputs={addr: sum(i['amount'] for i in unsp)}
- )
- rawtx = self.nodes[1].signrawtransactionwithwallet(rawtx)['hex']
-
- # Mine a block with the transaction
- block = create_block(tmpl=self.nodes[1].getblocktemplate(NORMAL_GBT_REQUEST_PARAMS), txlist=[rawtx])
- add_witness_commitment(block)
- block.solve()
- assert_equal(None, self.nodes[1].submitblock(block.serialize().hex()))
- self.sync_blocks()
-
- # Pre-taproot activation tests.
- self.log.info("Pre-activation tests...")
+ self.test_spenders(self.nodes[0], spenders_taproot_active(), input_counts=[1, 2, 2, 2, 2, 3])
# Run each test twice; once in isolation, and once combined with others. Testing in isolation
# means that the standardness is verified in every test (as combined transactions are only standard
# when all their inputs are standard).
- self.test_spenders(self.nodes[0], spenders_taproot_inactive(), input_counts=[1])
- self.test_spenders(self.nodes[0], spenders_taproot_inactive(), input_counts=[2, 3])
+ self.test_spenders(self.nodes[0], spenders_taproot_nonstandard(), input_counts=[1])
+ self.test_spenders(self.nodes[0], spenders_taproot_nonstandard(), input_counts=[2, 3])
if __name__ == '__main__':
diff --git a/test/functional/feature_versionbits_warning.py b/test/functional/feature_versionbits_warning.py
index e83dd7f446..1572463308 100755
--- a/test/functional/feature_versionbits_warning.py
+++ b/test/functional/feature_versionbits_warning.py
@@ -58,7 +58,8 @@ class VersionBitsWarningTest(BitcoinTestFramework):
def versionbits_in_alert_file(self):
"""Test that the versionbits warning has been written to the alert file."""
- alert_text = open(self.alert_filename, 'r', encoding='utf8').read()
+ with open(self.alert_filename, 'r', encoding='utf8') as f:
+ alert_text = f.read()
return VB_PATTERN.search(alert_text) is not None
def run_test(self):
diff --git a/test/functional/interface_rest.py b/test/functional/interface_rest.py
index 30c9e0c9cd..2c158e37e7 100755
--- a/test/functional/interface_rest.py
+++ b/test/functional/interface_rest.py
@@ -219,7 +219,7 @@ class RESTTest (BitcoinTestFramework):
self.generate(self.nodes[0], 1) # generate block to not affect upcoming tests
- self.log.info("Test the /block, /blockhashbyheight and /headers URIs")
+ self.log.info("Test the /block, /blockhashbyheight, /headers, and /blockfilterheaders URIs")
bb_hash = self.nodes[0].getbestblockhash()
# Check result if block does not exists
@@ -300,6 +300,12 @@ class RESTTest (BitcoinTestFramework):
assert_equal(first_filter_header, rpc_blockfilter['header'])
assert_equal(json_obj['filter'], rpc_blockfilter['filter'])
+ # Test blockfilterheaders with an invalid hash and filtertype
+ resp = self.test_rest_request(f"/blockfilterheaders/{INVALID_PARAM}/{bb_hash}", ret_type=RetType.OBJ, status=400)
+ assert_equal(resp.read().decode('utf-8').rstrip(), f"Unknown filtertype {INVALID_PARAM}")
+ resp = self.test_rest_request(f"/blockfilterheaders/basic/{INVALID_PARAM}", ret_type=RetType.OBJ, status=400)
+ assert_equal(resp.read().decode('utf-8').rstrip(), f"Invalid hash: {INVALID_PARAM}")
+
# Test number parsing
for num in ['5a', '-5', '0', '2001', '99999999999999999999999999999999999']:
assert_equal(
@@ -326,6 +332,10 @@ class RESTTest (BitcoinTestFramework):
# Check that there are our submitted transactions in the TX memory pool
json_obj = self.test_rest_request("/mempool/contents")
+ raw_mempool_verbose = self.nodes[0].getrawmempool(verbose=True)
+
+ assert_equal(json_obj, raw_mempool_verbose)
+
for i, tx in enumerate(txs):
assert tx in json_obj
assert_equal(json_obj[tx]['spentby'], txs[i + 1:i + 2])
diff --git a/test/functional/interface_usdt_coinselection.py b/test/functional/interface_usdt_coinselection.py
new file mode 100755
index 0000000000..ef32feda99
--- /dev/null
+++ b/test/functional/interface_usdt_coinselection.py
@@ -0,0 +1,208 @@
+#!/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.
+
+""" Tests the coin_selection:* tracepoint API interface.
+ See https://github.com/bitcoin/bitcoin/blob/master/doc/tracing.md#context-coin_selection
+"""
+
+# Test will be skipped if we don't have bcc installed
+try:
+ from bcc import BPF, USDT # type: ignore[import]
+except ImportError:
+ pass
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import (
+ assert_equal,
+ assert_greater_than,
+ assert_raises_rpc_error,
+)
+
+coinselection_tracepoints_program = """
+#include <uapi/linux/ptrace.h>
+
+#define WALLET_NAME_LENGTH 16
+#define ALGO_NAME_LENGTH 16
+
+struct event_data
+{
+ u8 type;
+ char wallet_name[WALLET_NAME_LENGTH];
+
+ // selected coins event
+ char algo[ALGO_NAME_LENGTH];
+ s64 target;
+ s64 waste;
+ s64 selected_value;
+
+ // create tx event
+ bool success;
+ s64 fee;
+ s32 change_pos;
+
+ // aps create tx event
+ bool use_aps;
+};
+
+BPF_QUEUE(coin_selection_events, struct event_data, 1024);
+
+int trace_selected_coins(struct pt_regs *ctx) {
+ struct event_data data;
+ __builtin_memset(&data, 0, sizeof(data));
+ data.type = 1;
+ bpf_usdt_readarg_p(1, ctx, &data.wallet_name, WALLET_NAME_LENGTH);
+ bpf_usdt_readarg_p(2, ctx, &data.algo, ALGO_NAME_LENGTH);
+ bpf_usdt_readarg(3, ctx, &data.target);
+ bpf_usdt_readarg(4, ctx, &data.waste);
+ bpf_usdt_readarg(5, ctx, &data.selected_value);
+ coin_selection_events.push(&data, 0);
+ return 0;
+}
+
+int trace_normal_create_tx(struct pt_regs *ctx) {
+ struct event_data data;
+ __builtin_memset(&data, 0, sizeof(data));
+ data.type = 2;
+ bpf_usdt_readarg_p(1, ctx, &data.wallet_name, WALLET_NAME_LENGTH);
+ bpf_usdt_readarg(2, ctx, &data.success);
+ bpf_usdt_readarg(3, ctx, &data.fee);
+ bpf_usdt_readarg(4, ctx, &data.change_pos);
+ coin_selection_events.push(&data, 0);
+ return 0;
+}
+
+int trace_attempt_aps(struct pt_regs *ctx) {
+ struct event_data data;
+ __builtin_memset(&data, 0, sizeof(data));
+ data.type = 3;
+ bpf_usdt_readarg_p(1, ctx, &data.wallet_name, WALLET_NAME_LENGTH);
+ coin_selection_events.push(&data, 0);
+ return 0;
+}
+
+int trace_aps_create_tx(struct pt_regs *ctx) {
+ struct event_data data;
+ __builtin_memset(&data, 0, sizeof(data));
+ data.type = 4;
+ bpf_usdt_readarg_p(1, ctx, &data.wallet_name, WALLET_NAME_LENGTH);
+ bpf_usdt_readarg(2, ctx, &data.use_aps);
+ bpf_usdt_readarg(3, ctx, &data.success);
+ bpf_usdt_readarg(4, ctx, &data.fee);
+ bpf_usdt_readarg(5, ctx, &data.change_pos);
+ coin_selection_events.push(&data, 0);
+ return 0;
+}
+"""
+
+
+class CoinSelectionTracepointTest(BitcoinTestFramework):
+ def set_test_params(self):
+ self.num_nodes = 1
+ self.setup_clean_chain = True
+
+ def skip_test_if_missing_module(self):
+ self.skip_if_platform_not_linux()
+ self.skip_if_no_bitcoind_tracepoints()
+ self.skip_if_no_python_bcc()
+ self.skip_if_no_bpf_permissions()
+ self.skip_if_no_wallet()
+
+ def get_tracepoints(self, expected_types):
+ events = []
+ try:
+ for i in range(0, len(expected_types) + 1):
+ event = self.bpf["coin_selection_events"].pop()
+ assert_equal(event.wallet_name.decode(), self.default_wallet_name)
+ assert_equal(event.type, expected_types[i])
+ events.append(event)
+ else:
+ # If the loop exits successfully instead of throwing a KeyError, then we have had
+ # more events than expected. There should be no more than len(expected_types) events.
+ assert False
+ except KeyError:
+ assert_equal(len(events), len(expected_types))
+ return events
+
+
+ def determine_selection_from_usdt(self, events):
+ success = None
+ use_aps = None
+ algo = None
+ waste = None
+ change_pos = None
+
+ is_aps = False
+ sc_events = []
+ for event in events:
+ if event.type == 1:
+ if not is_aps:
+ algo = event.algo.decode()
+ waste = event.waste
+ sc_events.append(event)
+ elif event.type == 2:
+ success = event.success
+ if not is_aps:
+ change_pos = event.change_pos
+ elif event.type == 3:
+ is_aps = True
+ elif event.type == 4:
+ assert is_aps
+ if event.use_aps:
+ use_aps = True
+ assert_equal(len(sc_events), 2)
+ algo = sc_events[1].algo.decode()
+ waste = sc_events[1].waste
+ change_pos = event.change_pos
+ return success, use_aps, algo, waste, change_pos
+
+ def run_test(self):
+ self.log.info("hook into the coin_selection tracepoints")
+ ctx = USDT(pid=self.nodes[0].process.pid)
+ ctx.enable_probe(probe="coin_selection:selected_coins", fn_name="trace_selected_coins")
+ ctx.enable_probe(probe="coin_selection:normal_create_tx_internal", fn_name="trace_normal_create_tx")
+ ctx.enable_probe(probe="coin_selection:attempting_aps_create_tx", fn_name="trace_attempt_aps")
+ ctx.enable_probe(probe="coin_selection:aps_create_tx_internal", fn_name="trace_aps_create_tx")
+ self.bpf = BPF(text=coinselection_tracepoints_program, usdt_contexts=[ctx], debug=0)
+
+ self.log.info("Prepare wallets")
+ self.generate(self.nodes[0], 101)
+ wallet = self.nodes[0].get_wallet_rpc(self.default_wallet_name)
+
+ self.log.info("Sending a transaction should result in all tracepoints")
+ # We should have 5 tracepoints in the order:
+ # 1. selected_coins (type 1)
+ # 2. normal_create_tx_internal (type 2)
+ # 3. attempting_aps_create_tx (type 3)
+ # 4. selected_coins (type 1)
+ # 5. aps_create_tx_internal (type 4)
+ wallet.sendtoaddress(wallet.getnewaddress(), 10)
+ events = self.get_tracepoints([1, 2, 3, 1, 4])
+ success, use_aps, algo, waste, change_pos = self.determine_selection_from_usdt(events)
+ assert_equal(success, True)
+ assert_greater_than(change_pos, -1)
+
+ self.log.info("Failing to fund results in 1 tracepoint")
+ # We should have 1 tracepoints in the order
+ # 1. normal_create_tx_internal (type 2)
+ assert_raises_rpc_error(-6, "Insufficient funds", wallet.sendtoaddress, wallet.getnewaddress(), 102 * 50)
+ events = self.get_tracepoints([2])
+ success, use_aps, algo, waste, change_pos = self.determine_selection_from_usdt(events)
+ assert_equal(success, False)
+
+ self.log.info("Explicitly enabling APS results in 2 tracepoints")
+ # We should have 2 tracepoints in the order
+ # 1. selected_coins (type 1)
+ # 2. normal_create_tx_internal (type 2)
+ wallet.setwalletflag("avoid_reuse")
+ wallet.sendtoaddress(address=wallet.getnewaddress(), amount=10, avoid_reuse=True)
+ events = self.get_tracepoints([1, 2])
+ success, use_aps, algo, waste, change_pos = self.determine_selection_from_usdt(events)
+ assert_equal(success, True)
+ assert_equal(use_aps, None)
+
+ self.bpf.cleanup()
+
+
+if __name__ == '__main__':
+ CoinSelectionTracepointTest().main()
diff --git a/test/functional/p2p_addr_relay.py b/test/functional/p2p_addr_relay.py
index 3218a9b14a..e2e9b6dcb2 100755
--- a/test/functional/p2p_addr_relay.py
+++ b/test/functional/p2p_addr_relay.py
@@ -21,8 +21,19 @@ from test_framework.p2p import (
P2P_SERVICES,
)
from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import assert_equal, assert_greater_than
+from test_framework.util import (
+ assert_equal,
+ assert_greater_than,
+ assert_greater_than_or_equal
+)
+
+ONE_MINUTE = 60
+TEN_MINUTES = 10 * ONE_MINUTE
+ONE_HOUR = 60 * ONE_MINUTE
+TWO_HOURS = 2 * ONE_HOUR
+ONE_DAY = 24 * ONE_HOUR
+ADDR_DESTINATIONS_THRESHOLD = 4
class AddrReceiver(P2PInterface):
num_ipv4_received = 0
@@ -85,6 +96,9 @@ class AddrTest(BitcoinTestFramework):
self.relay_tests()
self.inbound_blackhole_tests()
+ self.destination_rotates_once_in_24_hours_test()
+ self.destination_rotates_more_than_once_over_several_days_test()
+
# This test populates the addrman, which can impact the node's behavior
# in subsequent tests
self.getaddr_tests()
@@ -362,6 +376,56 @@ class AddrTest(BitcoinTestFramework):
self.nodes[0].disconnect_p2ps()
+ def get_nodes_that_received_addr(self, peer, receiver_peer, addr_receivers,
+ time_interval_1, time_interval_2):
+
+ # Clean addr response related to the initial getaddr. There is no way to avoid initial
+ # getaddr because the peer won't self-announce then.
+ for addr_receiver in addr_receivers:
+ addr_receiver.num_ipv4_received = 0
+
+ for _ in range(10):
+ self.mocktime += time_interval_1
+ self.msg.addrs[0].time = self.mocktime + TEN_MINUTES
+ self.nodes[0].setmocktime(self.mocktime)
+ with self.nodes[0].assert_debug_log(['received: addr (31 bytes) peer=0']):
+ peer.send_and_ping(self.msg)
+ self.mocktime += time_interval_2
+ self.nodes[0].setmocktime(self.mocktime)
+ receiver_peer.sync_with_ping()
+ return [node for node in addr_receivers if node.addr_received()]
+
+ def destination_rotates_once_in_24_hours_test(self):
+ self.restart_node(0, [])
+
+ self.log.info('Test within 24 hours an addr relay destination is rotated at most once')
+ self.mocktime = int(time.time())
+ self.msg = self.setup_addr_msg(1)
+ self.addr_receivers = []
+ peer = self.nodes[0].add_p2p_connection(P2PInterface())
+ receiver_peer = self.nodes[0].add_p2p_connection(AddrReceiver())
+ addr_receivers = [self.nodes[0].add_p2p_connection(AddrReceiver()) for _ in range(20)]
+ nodes_received_addr = self.get_nodes_that_received_addr(peer, receiver_peer, addr_receivers, 0, TWO_HOURS) # 10 intervals of 2 hours
+ # Per RelayAddress, we would announce these addrs to 2 destinations per day.
+ # Since it's at most one rotation, at most 4 nodes can receive ADDR.
+ assert_greater_than_or_equal(ADDR_DESTINATIONS_THRESHOLD, len(nodes_received_addr))
+ self.nodes[0].disconnect_p2ps()
+
+ def destination_rotates_more_than_once_over_several_days_test(self):
+ self.restart_node(0, [])
+
+ self.log.info('Test after several days an addr relay destination is rotated more than once')
+ self.msg = self.setup_addr_msg(1)
+ peer = self.nodes[0].add_p2p_connection(P2PInterface())
+ receiver_peer = self.nodes[0].add_p2p_connection(AddrReceiver())
+ addr_receivers = [self.nodes[0].add_p2p_connection(AddrReceiver()) for _ in range(20)]
+ # 10 intervals of 1 day (+ 1 hour, which should be enough to cover 30-min Poisson in most cases)
+ nodes_received_addr = self.get_nodes_that_received_addr(peer, receiver_peer, addr_receivers, ONE_DAY, ONE_HOUR)
+ # Now that there should have been more than one rotation, more than
+ # ADDR_DESTINATIONS_THRESHOLD nodes should have received ADDR.
+ assert_greater_than(len(nodes_received_addr), ADDR_DESTINATIONS_THRESHOLD)
+ self.nodes[0].disconnect_p2ps()
+
if __name__ == '__main__':
AddrTest().main()
diff --git a/test/functional/p2p_blockfilters.py b/test/functional/p2p_blockfilters.py
index e45cef65bd..2ffc825acd 100755
--- a/test/functional/p2p_blockfilters.py
+++ b/test/functional/p2p_blockfilters.py
@@ -250,6 +250,12 @@ class CompactFiltersTest(BitcoinTestFramework):
msg = "Error: Cannot set -peerblockfilters without -blockfilterindex."
self.nodes[0].assert_start_raises_init_error(expected_msg=msg)
+ self.log.info("Test -blockfilterindex with -reindex-chainstate raises an error")
+ self.nodes[0].assert_start_raises_init_error(
+ expected_msg='Error: -reindex-chainstate option is not compatible with -blockfilterindex. '
+ 'Please temporarily disable blockfilterindex while using -reindex-chainstate, or replace -reindex-chainstate with -reindex to fully rebuild all indexes.',
+ extra_args=['-blockfilterindex', '-reindex-chainstate'],
+ )
def compute_last_header(prev_header, hashes):
"""Compute the last filter header from a starting header and a sequence of filter hashes."""
diff --git a/test/functional/rpc_misc.py b/test/functional/rpc_misc.py
index f64aae7223..f6ee6a5215 100755
--- a/test/functional/rpc_misc.py
+++ b/test/functional/rpc_misc.py
@@ -27,7 +27,7 @@ class RpcMiscTest(BitcoinTestFramework):
self.log.info("test CHECK_NONFATAL")
assert_raises_rpc_error(
-1,
- 'Internal bug detected: \'request.params[9].get_str() != "trigger_internal_bug"\'',
+ 'Internal bug detected: "request.params[9].get_str() != "trigger_internal_bug""',
lambda: node.echo(arg9='trigger_internal_bug'),
)
diff --git a/test/functional/rpc_psbt.py b/test/functional/rpc_psbt.py
index b037807b53..444e56610e 100755
--- a/test/functional/rpc_psbt.py
+++ b/test/functional/rpc_psbt.py
@@ -10,6 +10,10 @@ from itertools import product
from test_framework.descriptors import descsum_create
from test_framework.key import ECKey
+from test_framework.messages import (
+ ser_compact_size,
+ WITNESS_SCALE_FACTOR,
+)
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_approx,
@@ -615,8 +619,8 @@ class PSBTTest(BitcoinTestFramework):
self.nodes[1].createwallet("extfund")
wallet = self.nodes[1].get_wallet_rpc("extfund")
- # Make a weird but signable script. sh(pkh()) descriptor accomplishes this
- desc = descsum_create("sh(pkh({}))".format(privkey))
+ # Make a weird but signable script. sh(wsh(pkh())) descriptor accomplishes this
+ desc = descsum_create("sh(wsh(pkh({})))".format(privkey))
if self.options.descriptors:
res = self.nodes[0].importdescriptors([{"desc": desc, "timestamp": "now"}])
else:
@@ -634,7 +638,7 @@ class PSBTTest(BitcoinTestFramework):
assert_raises_rpc_error(-4, "Insufficient funds", wallet.walletcreatefundedpsbt, [ext_utxo], {self.nodes[0].getnewaddress(): 15})
# But funding should work when the solving data is provided
- psbt = wallet.walletcreatefundedpsbt([ext_utxo], {self.nodes[0].getnewaddress(): 15}, 0, {"add_inputs": True, "solving_data": {"pubkeys": [addr_info['pubkey']], "scripts": [addr_info["embedded"]["scriptPubKey"]]}})
+ psbt = wallet.walletcreatefundedpsbt([ext_utxo], {self.nodes[0].getnewaddress(): 15}, 0, {"add_inputs": True, "solving_data": {"pubkeys": [addr_info['pubkey']], "scripts": [addr_info["embedded"]["scriptPubKey"], addr_info["embedded"]["embedded"]["scriptPubKey"]]}})
signed = wallet.walletprocesspsbt(psbt['psbt'])
assert not signed['complete']
signed = self.nodes[0].walletprocesspsbt(signed['psbt'])
@@ -655,10 +659,11 @@ class PSBTTest(BitcoinTestFramework):
break
psbt_in = dec["inputs"][input_idx]
# Calculate the input weight
- # (prevout + sequence + length of scriptSig + 2 bytes buffer) * 4 + len of scriptwitness
+ # (prevout + sequence + length of scriptSig + scriptsig + 1 byte buffer) * WITNESS_SCALE_FACTOR + num scriptWitness stack items + (length of stack item + stack item) * N stack items + 1 byte buffer
len_scriptsig = len(psbt_in["final_scriptSig"]["hex"]) // 2 if "final_scriptSig" in psbt_in else 0
- len_scriptwitness = len(psbt_in["final_scriptwitness"]["hex"]) // 2 if "final_scriptwitness" in psbt_in else 0
- input_weight = ((41 + len_scriptsig + 2) * 4) + len_scriptwitness
+ len_scriptsig += len(ser_compact_size(len_scriptsig)) + 1
+ len_scriptwitness = (sum([(len(x) // 2) + len(ser_compact_size(len(x) // 2)) for x in psbt_in["final_scriptwitness"]]) + len(psbt_in["final_scriptwitness"]) + 1) if "final_scriptwitness" in psbt_in else 0
+ input_weight = ((40 + len_scriptsig) * WITNESS_SCALE_FACTOR) + len_scriptwitness
low_input_weight = input_weight // 2
high_input_weight = input_weight * 2
diff --git a/test/functional/rpc_users.py b/test/functional/rpc_users.py
index 7cedb4336b..1a35a57802 100755
--- a/test/functional/rpc_users.py
+++ b/test/functional/rpc_users.py
@@ -107,6 +107,9 @@ class HTTPBasicsTest(BitcoinTestFramework):
self.stop_node(0)
self.nodes[0].assert_start_raises_init_error(expected_msg=init_error, extra_args=['-rpcauth=foo'])
self.nodes[0].assert_start_raises_init_error(expected_msg=init_error, extra_args=['-rpcauth=foo:bar'])
+ self.nodes[0].assert_start_raises_init_error(expected_msg=init_error, extra_args=['-rpcauth=foo:bar:baz'])
+ self.nodes[0].assert_start_raises_init_error(expected_msg=init_error, extra_args=['-rpcauth=foo$bar:baz'])
+ self.nodes[0].assert_start_raises_init_error(expected_msg=init_error, extra_args=['-rpcauth=foo$bar$baz'])
self.log.info('Check that failure to write cookie file will abort the node gracefully')
cookie_file = os.path.join(get_datadir_path(self.options.tmpdir, 0), self.chain, '.cookie.tmp')
diff --git a/test/functional/test_framework/test_node.py b/test/functional/test_framework/test_node.py
index e56d4aa492..7d2db391b6 100755
--- a/test/functional/test_framework/test_node.py
+++ b/test/functional/test_framework/test_node.py
@@ -545,6 +545,7 @@ class TestNode():
Will throw if bitcoind starts without an error.
Will throw if an expected_msg is provided and it does not match bitcoind's stdout."""
+ assert not self.running
with tempfile.NamedTemporaryFile(dir=self.stderr_dir, delete=False) as log_stderr, \
tempfile.NamedTemporaryFile(dir=self.stdout_dir, delete=False) as log_stdout:
try:
diff --git a/test/functional/test_framework/wallet.py b/test/functional/test_framework/wallet.py
index e86f365f11..6901bcfe66 100644
--- a/test/functional/test_framework/wallet.py
+++ b/test/functional/test_framework/wallet.py
@@ -127,6 +127,7 @@ class MiniWallet:
if not fixed_length:
break
tx.vin[0].scriptSig = CScript([der_sig + bytes(bytearray([SIGHASH_ALL]))])
+ tx.rehash()
def generate(self, num_blocks, **kwargs):
"""Generate blocks with coinbase outputs to the internal address, and append the outputs to the internal list"""
@@ -233,7 +234,8 @@ class MiniWallet:
return tx
def create_self_transfer(self, *, fee_rate=Decimal("0.003"), from_node=None, utxo_to_spend=None, mempool_valid=True, locktime=0, sequence=0):
- """Create and return a tx with the specified fee_rate. Fee may be exact or at most one satoshi higher than needed."""
+ """Create and return a tx with the specified fee_rate. Fee may be exact or at most one satoshi higher than needed.
+ Checking mempool validity via the testmempoolaccept RPC can be skipped by setting mempool_valid to False."""
from_node = from_node or self._test_node
utxo_to_spend = utxo_to_spend or self.get_utxo()
if self._priv_key is None:
@@ -260,12 +262,13 @@ class MiniWallet:
tx.wit.vtxinwit[0].scriptWitness.stack = [CScript([OP_TRUE]), bytes([LEAF_VERSION_TAPSCRIPT]) + self._internal_key]
tx_hex = tx.serialize().hex()
- tx_info = from_node.testmempoolaccept([tx_hex])[0]
- assert_equal(mempool_valid, tx_info['allowed'])
if mempool_valid:
+ tx_info = from_node.testmempoolaccept([tx_hex])[0]
+ assert_equal(tx_info['allowed'], True)
assert_equal(tx_info['vsize'], vsize)
assert_equal(tx_info['fees']['base'], utxo_to_spend['value'] - Decimal(send_value) / COIN)
- return {'txid': tx_info['txid'], 'wtxid': tx_info['wtxid'], 'hex': tx_hex, 'tx': tx}
+
+ return {'txid': tx.rehash(), 'wtxid': tx.getwtxid(), 'hex': tx_hex, 'tx': tx}
def sendrawtransaction(self, *, from_node, tx_hex, **kwargs):
txid = from_node.sendrawtransaction(hexstring=tx_hex, **kwargs)
diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py
index a3c938ae26..7d6397d193 100755
--- a/test/functional/test_runner.py
+++ b/test/functional/test_runner.py
@@ -82,6 +82,7 @@ EXTENDED_SCRIPTS = [
# Longest test should go first, to favor running tests in parallel
'feature_pruning.py',
'feature_dbcrash.py',
+ 'feature_index_prune.py',
]
BASE_SCRIPTS = [
@@ -111,7 +112,6 @@ BASE_SCRIPTS = [
'p2p_tx_download.py',
'mempool_updatefromblock.py',
'wallet_dump.py --legacy-wallet',
- 'feature_taproot.py --previous_release',
'feature_taproot.py',
'rpc_signer.py',
'wallet_signer.py --descriptors',
@@ -170,6 +170,7 @@ BASE_SCRIPTS = [
'wallet_reorgsrestore.py',
'interface_http.py',
'interface_rpc.py',
+ 'interface_usdt_coinselection.py',
'interface_usdt_net.py',
'interface_usdt_utxocache.py',
'interface_usdt_validation.py',
@@ -254,6 +255,7 @@ BASE_SCRIPTS = [
'rpc_bind.py --ipv4',
'rpc_bind.py --ipv6',
'rpc_bind.py --nonloopback',
+ 'wallet_crosschain.py',
'mining_basic.py',
'feature_signet.py',
'wallet_bumpfee.py --legacy-wallet',
@@ -332,7 +334,6 @@ BASE_SCRIPTS = [
'feature_help.py',
'feature_shutdown.py',
'p2p_ibd_txrelay.py',
- 'feature_blockfilterindex_prune.py'
# Don't append tests at the end to avoid merge conflicts
# Put them in a random line within the section that fits their approximate run-time
]
diff --git a/test/functional/wallet_createwallet.py b/test/functional/wallet_createwallet.py
index dcf2e98638..12480d4d1e 100755
--- a/test/functional/wallet_createwallet.py
+++ b/test/functional/wallet_createwallet.py
@@ -29,7 +29,7 @@ class CreateWalletTest(BitcoinTestFramework):
self.log.info("Run createwallet with invalid parameters.")
# Run createwallet with invalid parameters. This must not prevent a new wallet with the same name from being created with the correct parameters.
assert_raises_rpc_error(-4, "Passphrase provided but private keys are disabled. A passphrase is only used to encrypt private keys, so cannot be used for wallets with private keys disabled.",
- self.nodes[0].createwallet, wallet_name='w0', descriptors=True, disable_private_keys=True, passphrase="passphrase")
+ self.nodes[0].createwallet, wallet_name='w0', disable_private_keys=True, passphrase="passphrase")
self.nodes[0].createwallet(wallet_name='w0')
w0 = node.get_wallet_rpc('w0')
diff --git a/test/functional/wallet_crosschain.py b/test/functional/wallet_crosschain.py
new file mode 100755
index 0000000000..b6d0c87985
--- /dev/null
+++ b/test/functional/wallet_crosschain.py
@@ -0,0 +1,60 @@
+#!/usr/bin/env python3
+# Copyright (c) 2020 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+import os
+
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import assert_raises_rpc_error
+
+class WalletCrossChain(BitcoinTestFramework):
+ def set_test_params(self):
+ self.num_nodes = 2
+ self.setup_clean_chain = True
+
+ def skip_test_if_missing_module(self):
+ self.skip_if_no_wallet()
+
+ def setup_network(self):
+ self.add_nodes(self.num_nodes)
+
+ # Switch node 1 to testnet before starting it.
+ self.nodes[1].chain = 'testnet3'
+ self.nodes[1].extra_args = ['-maxconnections=0'] # disable testnet sync
+ with open(self.nodes[1].bitcoinconf, 'r', encoding='utf8') as conf:
+ conf_data = conf.read()
+ with open (self.nodes[1].bitcoinconf, 'w', encoding='utf8') as conf:
+ conf.write(conf_data.replace('regtest=', 'testnet=').replace('[regtest]', '[test]'))
+
+ self.start_nodes()
+
+ def run_test(self):
+ self.log.info("Creating wallets")
+
+ node0_wallet = os.path.join(self.nodes[0].datadir, 'node0_wallet')
+ self.nodes[0].createwallet(node0_wallet)
+ self.nodes[0].unloadwallet(node0_wallet)
+ node1_wallet = os.path.join(self.nodes[1].datadir, 'node1_wallet')
+ self.nodes[1].createwallet(node1_wallet)
+ self.nodes[1].unloadwallet(node1_wallet)
+
+ self.log.info("Loading wallets into nodes with a different genesis blocks")
+
+ if self.options.descriptors:
+ assert_raises_rpc_error(-18, 'Wallet file verification failed.', self.nodes[0].loadwallet, node1_wallet)
+ assert_raises_rpc_error(-18, 'Wallet file verification failed.', self.nodes[1].loadwallet, node0_wallet)
+ else:
+ assert_raises_rpc_error(-4, 'Wallet files should not be reused across chains.', self.nodes[0].loadwallet, node1_wallet)
+ assert_raises_rpc_error(-4, 'Wallet files should not be reused across chains.', self.nodes[1].loadwallet, node0_wallet)
+
+ if not self.options.descriptors:
+ self.log.info("Override cross-chain wallet load protection")
+ self.stop_nodes()
+ self.start_nodes([['-walletcrosschain']] * self.num_nodes)
+ self.nodes[0].loadwallet(node1_wallet)
+ self.nodes[1].loadwallet(node0_wallet)
+
+
+if __name__ == '__main__':
+ WalletCrossChain().main()
diff --git a/test/functional/wallet_listreceivedby.py b/test/functional/wallet_listreceivedby.py
index 48b92796fc..a7f4f9ffaf 100755
--- a/test/functional/wallet_listreceivedby.py
+++ b/test/functional/wallet_listreceivedby.py
@@ -26,9 +26,6 @@ class ReceivedByTest(BitcoinTestFramework):
self.skip_if_no_cli()
def run_test(self):
- # Generate block to get out of IBD
- self.generate(self.nodes[0], 1)
-
# save the number of coinbase reward addresses so far
num_cb_reward_addresses = len(self.nodes[1].listreceivedbyaddress(minconf=0, include_empty=True, include_watchonly=True))
@@ -172,7 +169,7 @@ class ReceivedByTest(BitcoinTestFramework):
address = self.nodes[0].getnewaddress(label)
reward = Decimal("25")
- self.generatetoaddress(self.nodes[0], 1, address, sync_fun=self.no_op)
+ self.generatetoaddress(self.nodes[0], 1, address)
hash = self.nodes[0].getbestblockhash()
self.log.info("getreceivedbyaddress returns nothing with defaults")
@@ -212,7 +209,7 @@ class ReceivedByTest(BitcoinTestFramework):
{"label": label, "amount": reward})
self.log.info("Generate 100 more blocks")
- self.generate(self.nodes[0], COINBASE_MATURITY, sync_fun=self.no_op)
+ self.generate(self.nodes[0], COINBASE_MATURITY)
self.log.info("getreceivedbyaddress returns reward with defaults")
balance = self.nodes[0].getreceivedbyaddress(address)
diff --git a/test/functional/wallet_send.py b/test/functional/wallet_send.py
index 86e36be8f7..07baa0595e 100755
--- a/test/functional/wallet_send.py
+++ b/test/functional/wallet_send.py
@@ -10,6 +10,10 @@ from itertools import product
from test_framework.authproxy import JSONRPCException
from test_framework.descriptors import descsum_create
from test_framework.key import ECKey
+from test_framework.messages import (
+ ser_compact_size,
+ WITNESS_SCALE_FACTOR,
+)
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
@@ -488,8 +492,8 @@ class WalletSendTest(BitcoinTestFramework):
self.nodes[1].createwallet("extfund")
ext_fund = self.nodes[1].get_wallet_rpc("extfund")
- # Make a weird but signable script. sh(pkh()) descriptor accomplishes this
- desc = descsum_create("sh(pkh({}))".format(privkey))
+ # Make a weird but signable script. sh(wsh(pkh())) descriptor accomplishes this
+ desc = descsum_create("sh(wsh(pkh({})))".format(privkey))
if self.options.descriptors:
res = ext_fund.importdescriptors([{"desc": desc, "timestamp": "now"}])
else:
@@ -507,7 +511,7 @@ class WalletSendTest(BitcoinTestFramework):
self.test_send(from_wallet=ext_wallet, to_wallet=self.nodes[0], amount=15, inputs=[ext_utxo], add_inputs=True, psbt=True, include_watching=True, expect_error=(-4, "Insufficient funds"))
# But funding should work when the solving data is provided
- res = self.test_send(from_wallet=ext_wallet, to_wallet=self.nodes[0], amount=15, inputs=[ext_utxo], add_inputs=True, psbt=True, include_watching=True, solving_data={"pubkeys": [addr_info['pubkey']], "scripts": [addr_info["embedded"]["scriptPubKey"]]})
+ res = self.test_send(from_wallet=ext_wallet, to_wallet=self.nodes[0], amount=15, inputs=[ext_utxo], add_inputs=True, psbt=True, include_watching=True, solving_data={"pubkeys": [addr_info['pubkey']], "scripts": [addr_info["embedded"]["scriptPubKey"], addr_info["embedded"]["embedded"]["scriptPubKey"]]})
signed = ext_wallet.walletprocesspsbt(res["psbt"])
signed = ext_fund.walletprocesspsbt(res["psbt"])
assert signed["complete"]
@@ -526,10 +530,11 @@ class WalletSendTest(BitcoinTestFramework):
break
psbt_in = dec["inputs"][input_idx]
# Calculate the input weight
- # (prevout + sequence + length of scriptSig + 2 bytes buffer) * 4 + len of scriptwitness
+ # (prevout + sequence + length of scriptSig + scriptsig + 1 byte buffer) * WITNESS_SCALE_FACTOR + num scriptWitness stack items + (length of stack item + stack item) * N stack items + 1 byte buffer
len_scriptsig = len(psbt_in["final_scriptSig"]["hex"]) // 2 if "final_scriptSig" in psbt_in else 0
- len_scriptwitness = len(psbt_in["final_scriptwitness"]["hex"]) // 2 if "final_scriptwitness" in psbt_in else 0
- input_weight = ((41 + len_scriptsig + 2) * 4) + len_scriptwitness
+ len_scriptsig += len(ser_compact_size(len_scriptsig)) + 1
+ len_scriptwitness = (sum([(len(x) // 2) + len(ser_compact_size(len(x) // 2)) for x in psbt_in["final_scriptwitness"]]) + len(psbt_in["final_scriptwitness"]) + 1) if "final_scriptwitness" in psbt_in else 0
+ input_weight = ((40 + len_scriptsig) * WITNESS_SCALE_FACTOR) + len_scriptwitness
# Input weight error conditions
assert_raises_rpc_error(
diff --git a/test/functional/wallet_taproot.py b/test/functional/wallet_taproot.py
index d3731b135a..41bb86f962 100755
--- a/test/functional/wallet_taproot.py
+++ b/test/functional/wallet_taproot.py
@@ -192,9 +192,9 @@ class WalletTaprootTest(BitcoinTestFramework):
"""Test generation and spending of P2TR address outputs."""
def set_test_params(self):
- self.num_nodes = 3
+ self.num_nodes = 2
self.setup_clean_chain = True
- self.extra_args = [['-keypool=100'], ['-keypool=100'], ["-vbparams=taproot:1:1"]]
+ self.extra_args = [['-keypool=100'], ['-keypool=100']]
self.supports_cli = False
def skip_test_if_missing_module(self):
@@ -243,15 +243,11 @@ class WalletTaprootTest(BitcoinTestFramework):
assert_equal(len(rederive), 1)
assert_equal(rederive[0], addr_g)
- # tr descriptors can be imported regardless of Taproot status
+ # tr descriptors can be imported
result = self.privs_tr_enabled.importdescriptors([{"desc": desc, "timestamp": "now"}])
assert(result[0]["success"])
result = self.pubs_tr_enabled.importdescriptors([{"desc": desc_pub, "timestamp": "now"}])
assert(result[0]["success"])
- result = self.privs_tr_disabled.importdescriptors([{"desc": desc, "timestamp": "now"}])
- assert result[0]["success"]
- result = self.pubs_tr_disabled.importdescriptors([{"desc": desc_pub, "timestamp": "now"}])
- assert result[0]["success"]
def do_test_sendtoaddress(self, comment, pattern, privmap, treefn, keys_pay, keys_change):
self.log.info("Testing %s through sendtoaddress" % comment)
@@ -328,12 +324,8 @@ class WalletTaprootTest(BitcoinTestFramework):
self.log.info("Creating wallets...")
self.nodes[0].createwallet(wallet_name="privs_tr_enabled", descriptors=True, blank=True)
self.privs_tr_enabled = self.nodes[0].get_wallet_rpc("privs_tr_enabled")
- self.nodes[2].createwallet(wallet_name="privs_tr_disabled", descriptors=True, blank=True)
- self.privs_tr_disabled=self.nodes[2].get_wallet_rpc("privs_tr_disabled")
self.nodes[0].createwallet(wallet_name="pubs_tr_enabled", descriptors=True, blank=True, disable_private_keys=True)
self.pubs_tr_enabled = self.nodes[0].get_wallet_rpc("pubs_tr_enabled")
- self.nodes[2].createwallet(wallet_name="pubs_tr_disabled", descriptors=True, blank=True, disable_private_keys=True)
- self.pubs_tr_disabled=self.nodes[2].get_wallet_rpc("pubs_tr_disabled")
self.nodes[0].createwallet(wallet_name="boring")
self.nodes[0].createwallet(wallet_name="addr_gen", descriptors=True, disable_private_keys=True, blank=True)
self.nodes[0].createwallet(wallet_name="rpc_online", descriptors=True, blank=True)