aboutsummaryrefslogtreecommitdiff
path: root/test/functional
diff options
context:
space:
mode:
Diffstat (limited to 'test/functional')
-rwxr-xr-xtest/functional/feature_backwards_compatibility.py4
-rwxr-xr-xtest/functional/feature_blockfilterindex_prune.py78
-rwxr-xr-xtest/functional/feature_coinstatsindex.py2
-rwxr-xr-xtest/functional/feature_config_args.py3
-rwxr-xr-xtest/functional/feature_csv_activation.py8
-rwxr-xr-xtest/functional/feature_index_prune.py155
-rwxr-xr-xtest/functional/feature_proxy.py6
-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.py11
-rwxr-xr-xtest/functional/interface_usdt_coinselection.py208
-rwxr-xr-xtest/functional/p2p_addr_relay.py66
-rwxr-xr-xtest/functional/p2p_compactblocks.py159
-rwxr-xr-xtest/functional/p2p_compactblocks_blocksonly.py6
-rwxr-xr-xtest/functional/p2p_message_capture.py6
-rwxr-xr-xtest/functional/p2p_unrequested_blocks.py15
-rwxr-xr-xtest/functional/rpc_dumptxoutset.py6
-rwxr-xr-xtest/functional/rpc_net.py4
-rwxr-xr-xtest/functional/rpc_users.py3
-rwxr-xr-xtest/functional/test_framework/messages.py6
-rwxr-xr-xtest/functional/test_framework/p2p.py6
-rwxr-xr-xtest/functional/test_framework/test_node.py1
-rw-r--r--test/functional/test_framework/util.py1
-rw-r--r--test/functional/test_framework/wallet.py11
-rwxr-xr-xtest/functional/test_runner.py15
-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_taproot.py4
30 files changed, 644 insertions, 327 deletions
diff --git a/test/functional/feature_backwards_compatibility.py b/test/functional/feature_backwards_compatibility.py
index a7fb3184a6..59a12193fd 100755
--- a/test/functional/feature_backwards_compatibility.py
+++ b/test/functional/feature_backwards_compatibility.py
@@ -34,11 +34,12 @@ from test_framework.util import (
class BackwardsCompatibilityTest(BitcoinTestFramework):
def set_test_params(self):
self.setup_clean_chain = True
- self.num_nodes = 9
+ self.num_nodes = 10
# Add new version after each release:
self.extra_args = [
["-addresstype=bech32", "-whitelist=noban@127.0.0.1"], # Pre-release: use to mine blocks. noban for immediate tx relay
["-nowallet", "-walletrbf=1", "-addresstype=bech32", "-whitelist=noban@127.0.0.1"], # Pre-release: use to receive coins, swap wallets, etc
+ ["-nowallet", "-walletrbf=1", "-addresstype=bech32", "-whitelist=noban@127.0.0.1"], # v23.0
["-nowallet", "-walletrbf=1", "-addresstype=bech32", "-whitelist=noban@127.0.0.1"], # v22.0
["-nowallet", "-walletrbf=1", "-addresstype=bech32", "-whitelist=noban@127.0.0.1"], # v0.21.0
["-nowallet", "-walletrbf=1", "-addresstype=bech32", "-whitelist=noban@127.0.0.1"], # v0.20.1
@@ -57,6 +58,7 @@ class BackwardsCompatibilityTest(BitcoinTestFramework):
self.add_nodes(self.num_nodes, extra_args=self.extra_args, versions=[
None,
None,
+ 230000,
220000,
210000,
200100,
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 251aa2114b..2e21638f80 100755
--- a/test/functional/feature_coinstatsindex.py
+++ b/test/functional/feature_coinstatsindex.py
@@ -231,11 +231,13 @@ class CoinStatsIndexTest(BitcoinTestFramework):
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 682ea62438..6c51a5ac31 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..3ee6a8036c
--- /dev/null
+++ b/test/functional/feature_index_prune.py
@@ -0,0 +1,155 @@
+#!/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,
+)
+
+
+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")
+ for i in range(3):
+ restart_args = self.extra_args[i]+["-reindex"]
+ self.restart_node(i, extra_args=restart_args)
+ # The nodes need to be reconnected to the non-pruning node upon restart, otherwise they will be stuck
+ self.connect_nodes(i, 3)
+
+ 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_proxy.py b/test/functional/feature_proxy.py
index 8541c3ed88..50e0e2c4cc 100755
--- a/test/functional/feature_proxy.py
+++ b/test/functional/feature_proxy.py
@@ -36,6 +36,7 @@ addnode connect to a CJDNS address
- Test passing invalid -i2psam
- Test passing -onlynet=onion without -proxy or -onion
- Test passing -onlynet=onion with -onion=0 and with -noonion
+- Test passing unknown -onlynet
"""
import socket
@@ -349,6 +350,11 @@ class ProxyTest(BitcoinTestFramework):
self.nodes[1].extra_args = ["-onlynet=onion", arg]
self.nodes[1].assert_start_raises_init_error(expected_msg=msg)
+ self.log.info("Test passing unknown network to -onlynet raises expected init error")
+ self.nodes[1].extra_args = ["-onlynet=abc"]
+ msg = "Error: Unknown network specified in -onlynet: 'abc'"
+ self.nodes[1].assert_start_raises_init_error(expected_msg=msg)
+
if __name__ == '__main__':
ProxyTest().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 95dc40cb52..f36bbda3af 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(
@@ -324,6 +330,9 @@ class RESTTest (BitcoinTestFramework):
# the size of the memory pool should be greater than 3x ~100 bytes
assert_greater_than(json_obj['bytes'], 300)
+ mempool_info = self.nodes[0].getmempoolinfo()
+ assert_equal(json_obj, mempool_info)
+
# 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)
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_compactblocks.py b/test/functional/p2p_compactblocks.py
index 364e806e18..b9ac3c32c5 100755
--- a/test/functional/p2p_compactblocks.py
+++ b/test/functional/p2p_compactblocks.py
@@ -2,11 +2,7 @@
# Copyright (c) 2016-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 compact blocks (BIP 152).
-
-Version 1 compact blocks are pre-segwit (txids)
-Version 2 compact blocks are post-segwit (wtxids)
-"""
+"""Test compact blocks (BIP 152)."""
import random
from test_framework.blocktools import (
@@ -31,7 +27,6 @@ from test_framework.messages import (
MSG_BLOCK,
MSG_CMPCT_BLOCK,
MSG_WITNESS_FLAG,
- NODE_NETWORK,
P2PHeaderAndShortIDs,
PrefilledTransaction,
calculate_shortid,
@@ -70,7 +65,7 @@ from test_framework.wallet import MiniWallet
# TestP2PConn: A peer we use to send messages to bitcoind, and store responses.
class TestP2PConn(P2PInterface):
- def __init__(self, cmpct_version):
+ def __init__(self):
super().__init__()
self.last_sendcmpct = []
self.block_announced = False
@@ -78,7 +73,6 @@ class TestP2PConn(P2PInterface):
# This is for synchronizing the p2p message traffic,
# so we can eg wait until a particular block is announced.
self.announced_blockhashes = set()
- self.cmpct_version = cmpct_version
def on_sendcmpct(self, message):
self.last_sendcmpct.append(message)
@@ -152,10 +146,8 @@ class CompactBlocksTest(BitcoinTestFramework):
]]
self.utxos = []
- def build_block_on_tip(self, node, segwit=False):
+ def build_block_on_tip(self, node):
block = create_block(tmpl=node.getblocktemplate(NORMAL_GBT_REQUEST_PARAMS))
- if segwit:
- add_witness_commitment(block)
block.solve()
return block
@@ -185,15 +177,13 @@ class CompactBlocksTest(BitcoinTestFramework):
# Test "sendcmpct" (between peers preferring the same version):
# - No compact block announcements unless sendcmpct is sent.
- # - If sendcmpct is sent with version > preferred_version, the message is ignored.
+ # - If sendcmpct is sent with version = 1, the message is ignored.
+ # - If sendcmpct is sent with version > 2, the message is ignored.
# - If sendcmpct is sent with boolean 0, then block announcements are not
# made with compact blocks.
# - If sendcmpct is then sent with boolean 1, then new block announcements
# are made with compact blocks.
- # If old_node is passed in, request compact blocks with version=preferred-1
- # and verify that it receives block announcements via compact block.
- def test_sendcmpct(self, test_node, old_node=None):
- preferred_version = test_node.cmpct_version
+ def test_sendcmpct(self, test_node):
node = self.nodes[0]
# Make sure we get a SENDCMPCT message from our peer
@@ -201,10 +191,8 @@ class CompactBlocksTest(BitcoinTestFramework):
return (len(test_node.last_sendcmpct) > 0)
test_node.wait_until(received_sendcmpct, timeout=30)
with p2p_lock:
- # Check that the first version received is the preferred one
- assert_equal(test_node.last_sendcmpct[0].version, preferred_version)
- # And that we receive versions down to 1.
- assert_equal(test_node.last_sendcmpct[-1].version, 1)
+ # Check that version 2 is received.
+ assert_equal(test_node.last_sendcmpct[0].version, 2)
test_node.last_sendcmpct = []
tip = int(node.getbestblockhash(), 16)
@@ -232,22 +220,29 @@ class CompactBlocksTest(BitcoinTestFramework):
# Before each test, sync the headers chain.
test_node.request_headers_and_sync(locator=[tip])
+ # Now try a SENDCMPCT message with too-low version
+ test_node.send_and_ping(msg_sendcmpct(announce=True, version=1))
+ check_announcement_of_new_block(node, test_node, lambda p: "cmpctblock" not in p.last_message)
+
+ # Headers sync before next test.
+ test_node.request_headers_and_sync(locator=[tip])
+
# Now try a SENDCMPCT message with too-high version
- test_node.send_and_ping(msg_sendcmpct(announce=True, version=preferred_version+1))
+ test_node.send_and_ping(msg_sendcmpct(announce=True, version=3))
check_announcement_of_new_block(node, test_node, lambda p: "cmpctblock" not in p.last_message)
# Headers sync before next test.
test_node.request_headers_and_sync(locator=[tip])
# Now try a SENDCMPCT message with valid version, but announce=False
- test_node.send_and_ping(msg_sendcmpct(announce=False, version=preferred_version))
+ test_node.send_and_ping(msg_sendcmpct(announce=False, version=2))
check_announcement_of_new_block(node, test_node, lambda p: "cmpctblock" not in p.last_message)
# Headers sync before next test.
test_node.request_headers_and_sync(locator=[tip])
# Finally, try a SENDCMPCT message with announce=True
- test_node.send_and_ping(msg_sendcmpct(announce=True, version=preferred_version))
+ test_node.send_and_ping(msg_sendcmpct(announce=True, version=2))
check_announcement_of_new_block(node, test_node, lambda p: "cmpctblock" in p.last_message)
# Try one more time (no headers sync should be needed!)
@@ -257,22 +252,14 @@ class CompactBlocksTest(BitcoinTestFramework):
test_node.send_and_ping(msg_sendheaders())
check_announcement_of_new_block(node, test_node, lambda p: "cmpctblock" in p.last_message)
- # Try one more time, after sending a version-1, announce=false message.
- test_node.send_and_ping(msg_sendcmpct(announce=False, version=preferred_version-1))
+ # Try one more time, after sending a version=1, announce=false message.
+ test_node.send_and_ping(msg_sendcmpct(announce=False, version=1))
check_announcement_of_new_block(node, test_node, lambda p: "cmpctblock" in p.last_message)
# Now turn off announcements
- test_node.send_and_ping(msg_sendcmpct(announce=False, version=preferred_version))
+ test_node.send_and_ping(msg_sendcmpct(announce=False, version=2))
check_announcement_of_new_block(node, test_node, lambda p: "cmpctblock" not in p.last_message and "headers" in p.last_message)
- if old_node is not None:
- # Verify that a peer using an older protocol version can receive
- # announcements from this node.
- old_node.send_and_ping(msg_sendcmpct(announce=True, version=preferred_version-1))
- # Header sync
- old_node.request_headers_and_sync(locator=[tip])
- check_announcement_of_new_block(node, old_node, lambda p: "cmpctblock" in p.last_message)
-
# This test actually causes bitcoind to (reasonably!) disconnect us, so do this last.
def test_invalid_cmpctblock_message(self):
self.generate(self.nodes[0], COINBASE_MATURITY + 1)
@@ -289,8 +276,7 @@ class CompactBlocksTest(BitcoinTestFramework):
# Compare the generated shortids to what we expect based on BIP 152, given
# bitcoind's choice of nonce.
- def test_compactblock_construction(self, test_node, use_witness_address=True):
- version = test_node.cmpct_version
+ def test_compactblock_construction(self, test_node):
node = self.nodes[0]
# Generate a bunch of transactions.
self.generate(node, COINBASE_MATURITY + 1)
@@ -303,8 +289,7 @@ class CompactBlocksTest(BitcoinTestFramework):
if not tx.wit.is_null():
segwit_tx_generated = True
- if use_witness_address:
- assert segwit_tx_generated # check that our test is not broken
+ assert segwit_tx_generated # check that our test is not broken
# Wait until we've seen the block announcement for the resulting tip
tip = int(node.getbestblockhash(), 16)
@@ -331,7 +316,7 @@ class CompactBlocksTest(BitcoinTestFramework):
with p2p_lock:
# Convert the on-the-wire representation to absolute indexes
header_and_shortids = HeaderAndShortIDs(test_node.last_message["cmpctblock"].header_and_shortids)
- self.check_compactblock_construction_from_block(version, header_and_shortids, block_hash, block)
+ self.check_compactblock_construction_from_block(header_and_shortids, block_hash, block)
# Now fetch the compact block using a normal non-announce getdata
test_node.clear_block_announcement()
@@ -345,9 +330,9 @@ class CompactBlocksTest(BitcoinTestFramework):
with p2p_lock:
# Convert the on-the-wire representation to absolute indexes
header_and_shortids = HeaderAndShortIDs(test_node.last_message["cmpctblock"].header_and_shortids)
- self.check_compactblock_construction_from_block(version, header_and_shortids, block_hash, block)
+ self.check_compactblock_construction_from_block(header_and_shortids, block_hash, block)
- def check_compactblock_construction_from_block(self, version, header_and_shortids, block_hash, block):
+ def check_compactblock_construction_from_block(self, header_and_shortids, block_hash, block):
# Check that we got the right block!
header_and_shortids.header.calc_sha256()
assert_equal(header_and_shortids.header.sha256, block_hash)
@@ -364,11 +349,7 @@ class CompactBlocksTest(BitcoinTestFramework):
# And this checks the witness
wtxid = entry.tx.calc_sha256(True)
- if version == 2:
- assert_equal(wtxid, block.vtx[entry.index].calc_sha256(True))
- else:
- # Shouldn't have received a witness
- assert entry.tx.wit.is_null()
+ assert_equal(wtxid, block.vtx[entry.index].calc_sha256(True))
# Check that the cmpctblock message announced all the transactions.
assert_equal(len(header_and_shortids.prefilled_txn) + len(header_and_shortids.shortids), len(block.vtx))
@@ -384,9 +365,7 @@ class CompactBlocksTest(BitcoinTestFramework):
# Already checked prefilled transactions above
header_and_shortids.prefilled_txn.pop(0)
else:
- tx_hash = block.vtx[index].sha256
- if version == 2:
- tx_hash = block.vtx[index].calc_sha256(True)
+ tx_hash = block.vtx[index].calc_sha256(True)
shortid = calculate_shortid(k0, k1, tx_hash)
assert_equal(shortid, header_and_shortids.shortids[0])
header_and_shortids.shortids.pop(0)
@@ -395,16 +374,12 @@ class CompactBlocksTest(BitcoinTestFramework):
# Test that bitcoind requests compact blocks when we announce new blocks
# via header or inv, and that responding to getblocktxn causes the block
# to be successfully reconstructed.
- # Post-segwit: upgraded nodes would only make this request of cb-version-2,
- # NODE_WITNESS peers. Unupgraded nodes would still make this request of
- # any cb-version-1-supporting peer.
- def test_compactblock_requests(self, test_node, segwit=True):
- version = test_node.cmpct_version
+ def test_compactblock_requests(self, test_node):
node = self.nodes[0]
# Try announcing a block with an inv or header, expect a compactblock
# request
for announce in ["inv", "header"]:
- block = self.build_block_on_tip(node, segwit=segwit)
+ block = self.build_block_on_tip(node)
if announce == "inv":
test_node.send_message(msg_inv([CInv(MSG_BLOCK, block.sha256)]))
@@ -420,9 +395,7 @@ class CompactBlocksTest(BitcoinTestFramework):
comp_block.header = CBlockHeader(block)
comp_block.nonce = 0
[k0, k1] = comp_block.get_siphash_keys()
- coinbase_hash = block.vtx[0].sha256
- if version == 2:
- coinbase_hash = block.vtx[0].calc_sha256(True)
+ coinbase_hash = block.vtx[0].calc_sha256(True)
comp_block.shortids = [calculate_shortid(k0, k1, coinbase_hash)]
test_node.send_and_ping(msg_cmpctblock(comp_block.to_p2p()))
assert_equal(int(node.getbestblockhash(), 16), block.hashPrevBlock)
@@ -433,10 +406,7 @@ class CompactBlocksTest(BitcoinTestFramework):
assert_equal(absolute_indexes, [0]) # should be a coinbase request
# Send the coinbase, and verify that the tip advances.
- if version == 2:
- msg = msg_blocktxn()
- else:
- msg = msg_no_witness_blocktxn()
+ msg = msg_blocktxn()
msg.block_transactions.blockhash = block.sha256
msg.block_transactions.transactions = [block.vtx[0]]
test_node.send_and_ping(msg)
@@ -462,9 +432,7 @@ class CompactBlocksTest(BitcoinTestFramework):
# node needs, and that responding to them causes the block to be
# reconstructed.
def test_getblocktxn_requests(self, test_node):
- version = test_node.cmpct_version
node = self.nodes[0]
- with_witness = (version == 2)
def test_getblocktxn_response(compact_block, peer, expected_result):
msg = msg_cmpctblock(compact_block.to_p2p())
@@ -485,13 +453,12 @@ class CompactBlocksTest(BitcoinTestFramework):
block = self.build_block_with_transactions(node, utxo, 5)
self.utxos.append([block.vtx[-1].sha256, 0, block.vtx[-1].vout[0].nValue])
comp_block = HeaderAndShortIDs()
- comp_block.initialize_from_block(block, use_witness=with_witness)
+ comp_block.initialize_from_block(block, use_witness=True)
test_getblocktxn_response(comp_block, test_node, [1, 2, 3, 4, 5])
msg_bt = msg_no_witness_blocktxn()
- if with_witness:
- msg_bt = msg_blocktxn() # serialize with witnesses
+ msg_bt = msg_blocktxn() # serialize with witnesses
msg_bt.block_transactions = BlockTransactions(block.sha256, block.vtx[1:])
test_tip_after_message(node, test_node, msg_bt, block.sha256)
@@ -500,7 +467,7 @@ class CompactBlocksTest(BitcoinTestFramework):
self.utxos.append([block.vtx[-1].sha256, 0, block.vtx[-1].vout[0].nValue])
# Now try interspersing the prefilled transactions
- comp_block.initialize_from_block(block, prefill_list=[0, 1, 5], use_witness=with_witness)
+ comp_block.initialize_from_block(block, prefill_list=[0, 1, 5], use_witness=True)
test_getblocktxn_response(comp_block, test_node, [2, 3, 4])
msg_bt.block_transactions = BlockTransactions(block.sha256, block.vtx[2:5])
test_tip_after_message(node, test_node, msg_bt, block.sha256)
@@ -514,7 +481,7 @@ class CompactBlocksTest(BitcoinTestFramework):
# Prefill 4 out of the 6 transactions, and verify that only the one
# that was not in the mempool is requested.
- comp_block.initialize_from_block(block, prefill_list=[0, 2, 3, 4], use_witness=with_witness)
+ comp_block.initialize_from_block(block, prefill_list=[0, 2, 3, 4], use_witness=True)
test_getblocktxn_response(comp_block, test_node, [5])
msg_bt.block_transactions = BlockTransactions(block.sha256, [block.vtx[5]])
@@ -538,7 +505,7 @@ class CompactBlocksTest(BitcoinTestFramework):
test_node.last_message.pop("getblocktxn", None)
# Send compact block
- comp_block.initialize_from_block(block, prefill_list=[0], use_witness=with_witness)
+ comp_block.initialize_from_block(block, prefill_list=[0], use_witness=True)
test_tip_after_message(node, test_node, msg_cmpctblock(comp_block.to_p2p()), block.sha256)
with p2p_lock:
# Shouldn't have gotten a request for any transaction
@@ -547,7 +514,6 @@ class CompactBlocksTest(BitcoinTestFramework):
# Incorrectly responding to a getblocktxn shouldn't cause the block to be
# permanently failed.
def test_incorrect_blocktxn_response(self, test_node):
- version = test_node.cmpct_version
node = self.nodes[0]
utxo = self.utxos.pop(0)
@@ -564,7 +530,7 @@ class CompactBlocksTest(BitcoinTestFramework):
# Send compact block
comp_block = HeaderAndShortIDs()
- comp_block.initialize_from_block(block, prefill_list=[0], use_witness=(version == 2))
+ comp_block.initialize_from_block(block, prefill_list=[0], use_witness=True)
test_node.send_and_ping(msg_cmpctblock(comp_block.to_p2p()))
absolute_indexes = []
with p2p_lock:
@@ -580,9 +546,7 @@ class CompactBlocksTest(BitcoinTestFramework):
# different peer provide the block further down, so that we're still
# verifying that the block isn't marked bad permanently. This is good
# enough for now.
- msg = msg_no_witness_blocktxn()
- if version == 2:
- msg = msg_blocktxn()
+ msg = msg_blocktxn()
msg.block_transactions = BlockTransactions(block.sha256, [block.vtx[5]] + block.vtx[7:])
test_node.send_and_ping(msg)
@@ -595,14 +559,10 @@ class CompactBlocksTest(BitcoinTestFramework):
test_node.last_message["getdata"].inv[0].type == MSG_BLOCK | MSG_WITNESS_FLAG
# Deliver the block
- if version == 2:
- test_node.send_and_ping(msg_block(block))
- else:
- test_node.send_and_ping(msg_no_witness_block(block))
+ test_node.send_and_ping(msg_block(block))
assert_equal(int(node.getbestblockhash(), 16), block.sha256)
def test_getblocktxn_handler(self, test_node):
- version = test_node.cmpct_version
node = self.nodes[0]
# bitcoind will not send blocktxn responses for blocks whose height is
# more than 10 blocks deep.
@@ -628,12 +588,8 @@ class CompactBlocksTest(BitcoinTestFramework):
tx = test_node.last_message["blocktxn"].block_transactions.transactions.pop(0)
tx.calc_sha256()
assert_equal(tx.sha256, block.vtx[index].sha256)
- if version == 1:
- # Witnesses should have been stripped
- assert tx.wit.is_null()
- else:
- # Check that the witness matches
- assert_equal(tx.calc_sha256(True), block.vtx[index].calc_sha256(True))
+ # Check that the witness matches
+ assert_equal(tx.calc_sha256(True), block.vtx[index].calc_sha256(True))
test_node.last_message.pop("blocktxn", None)
current_height -= 1
@@ -727,7 +683,7 @@ class CompactBlocksTest(BitcoinTestFramework):
# Test that we don't get disconnected if we relay a compact block with valid header,
# but invalid transactions.
- def test_invalid_tx_in_compactblock(self, test_node, use_segwit=True):
+ def test_invalid_tx_in_compactblock(self, test_node):
node = self.nodes[0]
assert len(self.utxos)
utxo = self.utxos[0]
@@ -735,17 +691,15 @@ class CompactBlocksTest(BitcoinTestFramework):
block = self.build_block_with_transactions(node, utxo, 5)
del block.vtx[3]
block.hashMerkleRoot = block.calc_merkle_root()
- if use_segwit:
- # If we're testing with segwit, also drop the coinbase witness,
- # but include the witness commitment.
- add_witness_commitment(block)
- block.vtx[0].wit.vtxinwit = []
+ # Drop the coinbase witness but include the witness commitment.
+ add_witness_commitment(block)
+ block.vtx[0].wit.vtxinwit = []
block.solve()
# Now send the compact block with all transactions prefilled, and
# verify that we don't get disconnected.
comp_block = HeaderAndShortIDs()
- comp_block.initialize_from_block(block, prefill_list=[0, 1, 2, 3, 4], use_witness=use_segwit)
+ comp_block.initialize_from_block(block, prefill_list=[0, 1, 2, 3, 4], use_witness=True)
msg = msg_cmpctblock(comp_block.to_p2p())
test_node.send_and_ping(msg)
@@ -759,7 +713,7 @@ class CompactBlocksTest(BitcoinTestFramework):
node = self.nodes[0]
tip = node.getbestblockhash()
peer.get_headers(locator=[int(tip, 16)], hashstop=0)
- peer.send_and_ping(msg_sendcmpct(announce=True, version=peer.cmpct_version))
+ peer.send_and_ping(msg_sendcmpct(announce=True, version=2))
def test_compactblock_reconstruction_multiple_peers(self, stalling_peer, delivery_peer):
node = self.nodes[0]
@@ -813,7 +767,7 @@ class CompactBlocksTest(BitcoinTestFramework):
def test_highbandwidth_mode_states_via_getpeerinfo(self):
# create new p2p connection for a fresh state w/o any prior sendcmpct messages sent
- hb_test_node = self.nodes[0].add_p2p_connection(TestP2PConn(cmpct_version=2))
+ hb_test_node = self.nodes[0].add_p2p_connection(TestP2PConn())
# assert the RPC getpeerinfo boolean fields `bip152_hb_{to, from}`
# match the given parameters for the last peer of a given node
@@ -843,9 +797,8 @@ class CompactBlocksTest(BitcoinTestFramework):
self.wallet = MiniWallet(self.nodes[0])
# Setup the p2p connections
- self.segwit_node = self.nodes[0].add_p2p_connection(TestP2PConn(cmpct_version=2))
- self.old_node = self.nodes[0].add_p2p_connection(TestP2PConn(cmpct_version=1), services=NODE_NETWORK)
- self.additional_segwit_node = self.nodes[0].add_p2p_connection(TestP2PConn(cmpct_version=2))
+ self.segwit_node = self.nodes[0].add_p2p_connection(TestP2PConn())
+ self.additional_segwit_node = self.nodes[0].add_p2p_connection(TestP2PConn())
# We will need UTXOs to construct transactions in later tests.
self.make_utxos()
@@ -853,11 +806,10 @@ class CompactBlocksTest(BitcoinTestFramework):
assert softfork_active(self.nodes[0], "segwit")
self.log.info("Testing SENDCMPCT p2p message... ")
- self.test_sendcmpct(self.segwit_node, old_node=self.old_node)
+ self.test_sendcmpct(self.segwit_node)
self.test_sendcmpct(self.additional_segwit_node)
self.log.info("Testing compactblock construction...")
- self.test_compactblock_construction(self.old_node)
self.test_compactblock_construction(self.segwit_node)
self.log.info("Testing compactblock requests (segwit node)... ")
@@ -868,11 +820,9 @@ class CompactBlocksTest(BitcoinTestFramework):
self.log.info("Testing getblocktxn handler (segwit node should return witnesses)...")
self.test_getblocktxn_handler(self.segwit_node)
- self.test_getblocktxn_handler(self.old_node)
self.log.info("Testing compactblock requests/announcements not at chain tip...")
self.test_compactblocks_not_at_tip(self.segwit_node)
- self.test_compactblocks_not_at_tip(self.old_node)
self.log.info("Testing handling of incorrect blocktxn responses...")
self.test_incorrect_blocktxn_response(self.segwit_node)
@@ -885,13 +835,12 @@ class CompactBlocksTest(BitcoinTestFramework):
# (Post-segwit activation, blocks won't propagate from node0 to node1
# automatically, so don't bother testing a block announced to node0.)
self.log.info("Testing end-to-end block relay...")
- self.request_cb_announcements(self.old_node)
self.request_cb_announcements(self.segwit_node)
- self.test_end_to_end_block_relay([self.segwit_node, self.old_node])
+ self.request_cb_announcements(self.additional_segwit_node)
+ self.test_end_to_end_block_relay([self.segwit_node, self.additional_segwit_node])
self.log.info("Testing handling of invalid compact blocks...")
self.test_invalid_tx_in_compactblock(self.segwit_node)
- self.test_invalid_tx_in_compactblock(self.old_node)
self.log.info("Testing invalid index in cmpctblock message...")
self.test_invalid_cmpctblock_message()
diff --git a/test/functional/p2p_compactblocks_blocksonly.py b/test/functional/p2p_compactblocks_blocksonly.py
index 6367eb26a3..3d0c421a93 100755
--- a/test/functional/p2p_compactblocks_blocksonly.py
+++ b/test/functional/p2p_compactblocks_blocksonly.py
@@ -48,7 +48,7 @@ class P2PCompactBlocksBlocksOnly(BitcoinTestFramework):
p2p_conn_high_bw = self.nodes[1].add_p2p_connection(P2PInterface())
p2p_conn_low_bw = self.nodes[3].add_p2p_connection(P2PInterface())
for conn in [p2p_conn_blocksonly, p2p_conn_high_bw, p2p_conn_low_bw]:
- assert_equal(conn.message_count['sendcmpct'], 2)
+ assert_equal(conn.message_count['sendcmpct'], 1)
conn.send_and_ping(msg_sendcmpct(announce=False, version=2))
# Nodes:
@@ -74,14 +74,14 @@ class P2PCompactBlocksBlocksOnly(BitcoinTestFramework):
# receiving a new valid block at the tip.
p2p_conn_blocksonly.send_and_ping(msg_block(block0))
assert_equal(int(self.nodes[0].getbestblockhash(), 16), block0.sha256)
- assert_equal(p2p_conn_blocksonly.message_count['sendcmpct'], 2)
+ assert_equal(p2p_conn_blocksonly.message_count['sendcmpct'], 1)
assert_equal(p2p_conn_blocksonly.last_message['sendcmpct'].announce, False)
# A normal node participating in transaction relay should request BIP152
# high bandwidth mode upon receiving a new valid block at the tip.
p2p_conn_high_bw.send_and_ping(msg_block(block0))
assert_equal(int(self.nodes[1].getbestblockhash(), 16), block0.sha256)
- p2p_conn_high_bw.wait_until(lambda: p2p_conn_high_bw.message_count['sendcmpct'] == 3)
+ p2p_conn_high_bw.wait_until(lambda: p2p_conn_high_bw.message_count['sendcmpct'] == 2)
assert_equal(p2p_conn_high_bw.last_message['sendcmpct'].announce, True)
# Don't send a block from the p2p_conn_low_bw so the low bandwidth node
diff --git a/test/functional/p2p_message_capture.py b/test/functional/p2p_message_capture.py
index 0a7ae44de4..87c77f4540 100755
--- a/test/functional/p2p_message_capture.py
+++ b/test/functional/p2p_message_capture.py
@@ -43,12 +43,8 @@ def mini_parser(dat_file):
break
tmp_header = BytesIO(tmp_header_raw)
tmp_header.read(TIME_SIZE) # skip the timestamp field
- raw_msgtype = tmp_header.read(MSGTYPE_SIZE)
- msgtype: bytes = raw_msgtype.split(b'\x00', 1)[0]
- remainder = raw_msgtype.split(b'\x00', 1)[1]
- assert(len(msgtype) > 0)
+ msgtype = tmp_header.read(MSGTYPE_SIZE).rstrip(b'\x00')
assert(msgtype in MESSAGEMAP)
- assert(len(remainder) == 0 or not remainder.decode().isprintable())
length: int = int.from_bytes(tmp_header.read(LENGTH_SIZE), "little")
data = f_in.read(length)
assert_equal(len(data), length)
diff --git a/test/functional/p2p_unrequested_blocks.py b/test/functional/p2p_unrequested_blocks.py
index 9c4e1dd1b1..76d9b045ce 100755
--- a/test/functional/p2p_unrequested_blocks.py
+++ b/test/functional/p2p_unrequested_blocks.py
@@ -257,16 +257,11 @@ class AcceptBlockTest(BitcoinTestFramework):
test_node.send_message(msg_block(block_291))
# At this point we've sent an obviously-bogus block, wait for full processing
- # without assuming whether we will be disconnected or not
- try:
- # Only wait a short while so the test doesn't take forever if we do get
- # disconnected
- test_node.sync_with_ping(timeout=1)
- except AssertionError:
- test_node.wait_for_disconnect()
-
- self.nodes[0].disconnect_p2ps()
- test_node = self.nodes[0].add_p2p_connection(P2PInterface())
+ # and assume disconnection
+ test_node.wait_for_disconnect()
+
+ self.nodes[0].disconnect_p2ps()
+ test_node = self.nodes[0].add_p2p_connection(P2PInterface())
# We should have failed reorg and switched back to 290 (but have block 291)
assert_equal(self.nodes[0].getblockcount(), 290)
diff --git a/test/functional/rpc_dumptxoutset.py b/test/functional/rpc_dumptxoutset.py
index 4ca84748b2..672c9a53dc 100755
--- a/test/functional/rpc_dumptxoutset.py
+++ b/test/functional/rpc_dumptxoutset.py
@@ -49,9 +49,13 @@ class DumptxoutsetTest(BitcoinTestFramework):
out['txoutset_hash'], '1f7e3befd45dc13ae198dfbb22869a9c5c4196f8e9ef9735831af1288033f890')
assert_equal(out['nchaintx'], 101)
- # Specifying a path to an existing file will fail.
+ # Specifying a path to an existing or invalid file will fail.
assert_raises_rpc_error(
-8, '{} already exists'.format(FILENAME), node.dumptxoutset, FILENAME)
+ invalid_path = str(Path(node.datadir) / "invalid" / "path")
+ assert_raises_rpc_error(
+ -8, "Couldn't open file {}.incomplete for writing".format(invalid_path), node.dumptxoutset, invalid_path)
+
if __name__ == '__main__':
DumptxoutsetTest().main()
diff --git a/test/functional/rpc_net.py b/test/functional/rpc_net.py
index 81a3cfee97..ad8ba06824 100755
--- a/test/functional/rpc_net.py
+++ b/test/functional/rpc_net.py
@@ -257,6 +257,10 @@ class NetTest(BitcoinTestFramework):
assert_equal(node.addpeeraddress(address="", port=8333), {"success": False})
assert_equal(node.getnodeaddresses(count=0), [])
+ self.log.debug("Test that adding an address with invalid port fails")
+ assert_raises_rpc_error(-1, "JSON integer out of range", self.nodes[0].addpeeraddress, address="1.2.3.4", port=-1)
+ assert_raises_rpc_error(-1, "JSON integer out of range", self.nodes[0].addpeeraddress,address="1.2.3.4", port=65536)
+
self.log.debug("Test that adding a valid address to the tried table succeeds")
assert_equal(node.addpeeraddress(address="1.2.3.4", tried=True, port=8333), {"success": True})
with node.assert_debug_log(expected_msgs=["CheckAddrman: new 0, tried 1, total 1 started"]):
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/messages.py b/test/functional/test_framework/messages.py
index f57b6e7494..aae44c0ac0 100755
--- a/test/functional/test_framework/messages.py
+++ b/test/functional/test_framework/messages.py
@@ -1672,7 +1672,7 @@ class msg_getcfilters:
__slots__ = ("filter_type", "start_height", "stop_hash")
msgtype = b"getcfilters"
- def __init__(self, filter_type, start_height, stop_hash):
+ def __init__(self, filter_type=None, start_height=None, stop_hash=None):
self.filter_type = filter_type
self.start_height = start_height
self.stop_hash = stop_hash
@@ -1722,7 +1722,7 @@ class msg_getcfheaders:
__slots__ = ("filter_type", "start_height", "stop_hash")
msgtype = b"getcfheaders"
- def __init__(self, filter_type, start_height, stop_hash):
+ def __init__(self, filter_type=None, start_height=None, stop_hash=None):
self.filter_type = filter_type
self.start_height = start_height
self.stop_hash = stop_hash
@@ -1775,7 +1775,7 @@ class msg_getcfcheckpt:
__slots__ = ("filter_type", "stop_hash")
msgtype = b"getcfcheckpt"
- def __init__(self, filter_type, stop_hash):
+ def __init__(self, filter_type=None, stop_hash=None):
self.filter_type = filter_type
self.stop_hash = stop_hash
diff --git a/test/functional/test_framework/p2p.py b/test/functional/test_framework/p2p.py
index 251d3d5eae..fc72a9ab73 100755
--- a/test/functional/test_framework/p2p.py
+++ b/test/functional/test_framework/p2p.py
@@ -47,6 +47,9 @@ from test_framework.messages import (
msg_getaddr,
msg_getblocks,
msg_getblocktxn,
+ msg_getcfcheckpt,
+ msg_getcfheaders,
+ msg_getcfilters,
msg_getdata,
msg_getheaders,
msg_headers,
@@ -108,6 +111,9 @@ MESSAGEMAP = {
b"getaddr": msg_getaddr,
b"getblocks": msg_getblocks,
b"getblocktxn": msg_getblocktxn,
+ b"getcfcheckpt": msg_getcfcheckpt,
+ b"getcfheaders": msg_getcfheaders,
+ b"getcfilters": msg_getcfilters,
b"getdata": msg_getdata,
b"getheaders": msg_getheaders,
b"headers": msg_headers,
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/util.py b/test/functional/test_framework/util.py
index 8651bcf636..b043d1a70d 100644
--- a/test/functional/test_framework/util.py
+++ b/test/functional/test_framework/util.py
@@ -378,6 +378,7 @@ def write_config(config_path, *, n, chain, extra_config="", disable_autoconnect=
f.write("[{}]\n".format(chain_name_conf_section))
f.write("port=" + str(p2p_port(n)) + "\n")
f.write("rpcport=" + str(rpc_port(n)) + "\n")
+ f.write("rpcdoccheck=1\n")
f.write("fallbackfee=0.0002\n")
f.write("server=1\n")
f.write("keypool=1\n")
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..8416a5881d 100755
--- a/test/functional/test_runner.py
+++ b/test/functional/test_runner.py
@@ -28,7 +28,7 @@ import logging
import unittest
# Formatting. Default colors to empty strings.
-BOLD, GREEN, RED, GREY = ("", ""), ("", ""), ("", ""), ("", "")
+DEFAULT, BOLD, GREEN, RED = ("", ""), ("", ""), ("", ""), ("", "")
try:
# Make sure python thinks it can write unicode to its stdout
"\u2713".encode("utf_8").decode(sys.stdout.encoding)
@@ -59,10 +59,10 @@ if os.name != 'nt' or sys.getwindowsversion() >= (10, 0, 14393): #type:ignore
kernel32.SetConsoleMode(stderr, stderr_mode.value | ENABLE_VIRTUAL_TERMINAL_PROCESSING)
# primitive formatting on supported
# terminal via ANSI escape sequences:
+ DEFAULT = ('\033[0m', '\033[0m')
BOLD = ('\033[0m', '\033[1m')
GREEN = ('\033[0m', '\033[0;32m')
RED = ('\033[0m', '\033[0;31m')
- GREY = ('\033[0m', '\033[1;30m')
TEST_EXIT_PASSED = 0
TEST_EXIT_SKIPPED = 77
@@ -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
]
@@ -371,11 +372,11 @@ def main():
args, unknown_args = parser.parse_known_args()
if not args.ansi:
- global BOLD, GREEN, RED, GREY
+ global DEFAULT, BOLD, GREEN, RED
+ DEFAULT = ("", "")
BOLD = ("", "")
GREEN = ("", "")
RED = ("", "")
- GREY = ("", "")
# args to be passed on always start with two dashes; tests are the remaining unknown args
tests = [arg for arg in unknown_args if arg[:2] != "--"]
@@ -719,7 +720,7 @@ class TestResult():
color = RED
glyph = CROSS
elif self.status == "Skipped":
- color = GREY
+ color = DEFAULT
glyph = CIRCLE
return color[1] + "%s | %s%s | %s s\n" % (self.name.ljust(self.padding), glyph, self.status.ljust(7), self.time) + color[0]
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_taproot.py b/test/functional/wallet_taproot.py
index 41bb86f962..a4d836c8fe 100755
--- a/test/functional/wallet_taproot.py
+++ b/test/functional/wallet_taproot.py
@@ -441,11 +441,11 @@ class WalletTaprootTest(BitcoinTestFramework):
self.log.info("Sending everything back...")
- txid = self.rpc_online.sendtoaddress(address=self.boring.getnewaddress(), amount=self.rpc_online.getbalance(), subtractfeefromamount=True)
+ txid = self.rpc_online.sendall(recipients=[self.boring.getnewaddress()])["txid"]
self.generatetoaddress(self.nodes[0], 1, self.boring.getnewaddress(), sync_fun=self.no_op)
assert(self.rpc_online.gettransaction(txid)["confirmations"] > 0)
- psbt = self.psbt_online.walletcreatefundedpsbt([], [{self.boring.getnewaddress(): self.psbt_online.getbalance()}], None, {"subtractFeeFromOutputs": [0]})['psbt']
+ psbt = self.psbt_online.sendall(recipients=[self.boring.getnewaddress()], options={"psbt": True})["psbt"]
res = self.psbt_offline.walletprocesspsbt(psbt)
assert(res['complete'])
rawtx = self.nodes[0].finalizepsbt(res['psbt'])['hex']