aboutsummaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rw-r--r--test/README.md6
-rw-r--r--test/functional/data/invalid_txs.py48
-rwxr-xr-xtest/functional/feature_anchors.py32
-rwxr-xr-xtest/functional/feature_assumevalid.py9
-rwxr-xr-xtest/functional/feature_backwards_compatibility.py10
-rwxr-xr-xtest/functional/feature_bind_extra.py95
-rwxr-xr-xtest/functional/feature_bip68_sequence.py53
-rwxr-xr-xtest/functional/feature_block.py30
-rwxr-xr-xtest/functional/feature_cltv.py33
-rwxr-xr-xtest/functional/feature_coinstatsindex.py9
-rwxr-xr-xtest/functional/feature_csv_activation.py9
-rwxr-xr-xtest/functional/feature_dbcrash.py3
-rwxr-xr-xtest/functional/feature_dersig.py26
-rwxr-xr-xtest/functional/feature_fee_estimation.py31
-rwxr-xr-xtest/functional/feature_loadblock.py3
-rwxr-xr-xtest/functional/feature_nulldummy.py17
-rwxr-xr-xtest/functional/feature_presegwit_node_upgrade.py52
-rwxr-xr-xtest/functional/feature_proxy.py4
-rwxr-xr-xtest/functional/feature_pruning.py10
-rwxr-xr-xtest/functional/feature_rbf.py237
-rwxr-xr-xtest/functional/feature_segwit.py88
-rwxr-xr-xtest/functional/feature_taproot.py49
-rwxr-xr-xtest/functional/feature_utxo_set_hash.py6
-rwxr-xr-xtest/functional/feature_versionbits_warning.py4
-rwxr-xr-xtest/functional/interface_bitcoin_cli.py153
-rwxr-xr-xtest/functional/interface_zmq.py21
-rwxr-xr-xtest/functional/mempool_accept.py76
-rwxr-xr-xtest/functional/mempool_accept_wtxid.py131
-rwxr-xr-xtest/functional/mempool_compatibility.py8
-rwxr-xr-xtest/functional/mempool_expiry.py3
-rwxr-xr-xtest/functional/mempool_package_onemore.py46
-rwxr-xr-xtest/functional/mempool_packages.py33
-rwxr-xr-xtest/functional/mempool_reorg.py109
-rwxr-xr-xtest/functional/mempool_resurrect.py3
-rwxr-xr-xtest/functional/mempool_unbroadcast.py6
-rwxr-xr-xtest/functional/mining_basic.py21
-rwxr-xr-xtest/functional/mining_getblocktemplate_longpoll.py3
-rwxr-xr-xtest/functional/p2p_addr_relay.py150
-rwxr-xr-xtest/functional/p2p_addrfetch.py62
-rwxr-xr-xtest/functional/p2p_addrv2_relay.py28
-rwxr-xr-xtest/functional/p2p_blockfilters.py98
-rwxr-xr-xtest/functional/p2p_blocksonly.py3
-rwxr-xr-xtest/functional/p2p_compactblocks.py78
-rwxr-xr-xtest/functional/p2p_compactblocks_hb.py97
-rwxr-xr-xtest/functional/p2p_dos_header_tree.py6
-rwxr-xr-xtest/functional/p2p_eviction.py16
-rwxr-xr-xtest/functional/p2p_feefilter.py3
-rwxr-xr-xtest/functional/p2p_i2p_ports.py43
-rwxr-xr-xtest/functional/p2p_invalid_block.py19
-rwxr-xr-xtest/functional/p2p_invalid_messages.py1
-rwxr-xr-xtest/functional/p2p_leak.py6
-rwxr-xr-xtest/functional/p2p_leak_tx.py3
-rwxr-xr-xtest/functional/p2p_permissions.py6
-rwxr-xr-xtest/functional/p2p_segwit.py195
-rwxr-xr-xtest/functional/p2p_tx_download.py5
-rwxr-xr-xtest/functional/rpc_addresses_deprecation.py9
-rwxr-xr-xtest/functional/rpc_blockchain.py7
-rwxr-xr-xtest/functional/rpc_createmultisig.py10
-rwxr-xr-xtest/functional/rpc_decodescript.py14
-rwxr-xr-xtest/functional/rpc_dumptxoutset.py4
-rwxr-xr-xtest/functional/rpc_fundrawtransaction.py2
-rwxr-xr-xtest/functional/rpc_getblockstats.py4
-rwxr-xr-xtest/functional/rpc_misc.py16
-rwxr-xr-xtest/functional/rpc_net.py28
-rwxr-xr-xtest/functional/rpc_packages.py351
-rwxr-xr-xtest/functional/rpc_rawtransaction.py35
-rwxr-xr-xtest/functional/rpc_setban.py31
-rwxr-xr-xtest/functional/rpc_signrawtransaction.py64
-rwxr-xr-xtest/functional/rpc_txoutproof.py19
-rw-r--r--test/functional/test_framework/blocktools.py31
-rwxr-xr-xtest/functional/test_framework/messages.py48
-rw-r--r--test/functional/test_framework/netutil.py2
-rwxr-xr-xtest/functional/test_framework/test_framework.py4
-rwxr-xr-xtest/functional/test_framework/test_node.py9
-rw-r--r--test/functional/test_framework/util.py60
-rw-r--r--test/functional/test_framework/wallet.py71
-rwxr-xr-xtest/functional/test_framework/wallet_util.py38
-rwxr-xr-xtest/functional/test_runner.py37
-rwxr-xr-xtest/functional/wallet_abandonconflict.py3
-rwxr-xr-xtest/functional/wallet_address_types.py15
-rwxr-xr-xtest/functional/wallet_avoidreuse.py46
-rwxr-xr-xtest/functional/wallet_backup.py5
-rwxr-xr-xtest/functional/wallet_balance.py3
-rwxr-xr-xtest/functional/wallet_basic.py8
-rwxr-xr-xtest/functional/wallet_bumpfee.py21
-rwxr-xr-xtest/functional/wallet_descriptor.py3
-rwxr-xr-xtest/functional/wallet_dump.py9
-rwxr-xr-xtest/functional/wallet_fallbackfee.py4
-rwxr-xr-xtest/functional/wallet_groups.py11
-rwxr-xr-xtest/functional/wallet_hd.py3
-rwxr-xr-xtest/functional/wallet_importdescriptors.py162
-rwxr-xr-xtest/functional/wallet_importmulti.py30
-rwxr-xr-xtest/functional/wallet_importprunedfunds.py5
-rwxr-xr-xtest/functional/wallet_keypool.py2
-rwxr-xr-xtest/functional/wallet_keypool_topup.py3
-rwxr-xr-xtest/functional/wallet_labels.py59
-rwxr-xr-xtest/functional/wallet_listdescriptors.py11
-rwxr-xr-xtest/functional/wallet_listsinceblock.py3
-rwxr-xr-xtest/functional/wallet_listtransactions.py48
-rwxr-xr-xtest/functional/wallet_multiwallet.py3
-rwxr-xr-xtest/functional/wallet_orphanedreward.py62
-rwxr-xr-xtest/functional/wallet_resendwallettransactions.py8
-rwxr-xr-xtest/functional/wallet_taproot.py427
-rwxr-xr-xtest/functional/wallet_txn_clone.py9
-rwxr-xr-xtest/functional/wallet_upgradewallet.py22
-rwxr-xr-xtest/functional/wallet_watchonly.py3
-rwxr-xr-xtest/get_previous_releases.py76
-rwxr-xr-xtest/lint/lint-circular-dependencies.sh5
-rwxr-xr-xtest/lint/lint-python.sh2
-rw-r--r--test/lint/lint-spelling.ignore-words.txt2
-rwxr-xr-xtest/lint/lint-spelling.sh2
-rw-r--r--test/sanitizer_suppressions/ubsan1
-rw-r--r--test/util/data/bitcoin-util-test.json30
113 files changed, 3233 insertions, 1063 deletions
diff --git a/test/README.md b/test/README.md
index ab34ce42dc..51e61562a4 100644
--- a/test/README.md
+++ b/test/README.md
@@ -84,6 +84,12 @@ Run all possible tests with
test/functional/test_runner.py --extended
```
+In order to run backwards compatibility tests, download the previous node binaries:
+
+```
+test/get_previous_releases.py -b v0.20.1 v0.19.1 v0.18.1 v0.17.2 v0.16.3 v0.15.2
+```
+
By default, up to 4 tests will be run in parallel by test_runner. To specify
how many jobs to run, append `--jobs=n`
diff --git a/test/functional/data/invalid_txs.py b/test/functional/data/invalid_txs.py
index fab921ef19..cde0399d8b 100644
--- a/test/functional/data/invalid_txs.py
+++ b/test/functional/data/invalid_txs.py
@@ -29,27 +29,32 @@ from test_framework.messages import (
CTxOut,
MAX_MONEY,
)
-from test_framework import script as sc
from test_framework.blocktools import create_tx_with_script, MAX_BLOCK_SIGOPS
from test_framework.script import (
CScript,
+ OP_0,
+ OP_2DIV,
+ OP_2MUL,
+ OP_AND,
OP_CAT,
- OP_SUBSTR,
- OP_LEFT,
- OP_RIGHT,
+ OP_CHECKSIG,
+ OP_DIV,
OP_INVERT,
- OP_AND,
+ OP_LEFT,
+ OP_LSHIFT,
+ OP_MOD,
+ OP_MUL,
OP_OR,
+ OP_RIGHT,
+ OP_RSHIFT,
+ OP_SUBSTR,
+ OP_TRUE,
OP_XOR,
- OP_2MUL,
- OP_2DIV,
- OP_MUL,
- OP_DIV,
- OP_MOD,
- OP_LSHIFT,
- OP_RSHIFT
)
-basic_p2sh = sc.CScript([sc.OP_HASH160, sc.hash160(sc.CScript([sc.OP_0])), sc.OP_EQUAL])
+from test_framework.script_util import (
+ script_to_p2sh_script,
+)
+basic_p2sh = script_to_p2sh_script(CScript([OP_0]))
class BadTxTemplate:
@@ -116,7 +121,7 @@ class SizeTooSmall(BadTxTemplate):
def get_tx(self):
tx = CTransaction()
tx.vin.append(self.valid_txin)
- tx.vout.append(CTxOut(0, sc.CScript([sc.OP_TRUE])))
+ tx.vout.append(CTxOut(0, CScript([OP_TRUE])))
tx.calc_sha256()
return tx
@@ -151,6 +156,19 @@ class DuplicateInput(BadTxTemplate):
return tx
+class PrevoutNullInput(BadTxTemplate):
+ reject_reason = 'bad-txns-prevout-null'
+ expect_disconnect = True
+
+ def get_tx(self):
+ tx = CTransaction()
+ tx.vin.append(self.valid_txin)
+ tx.vin.append(CTxIn(COutPoint(hash=0, n=0xffffffff)))
+ tx.vout.append(CTxOut(1, basic_p2sh))
+ tx.calc_sha256()
+ return tx
+
+
class NonexistentInput(BadTxTemplate):
reject_reason = None # Added as an orphan tx.
expect_disconnect = False
@@ -217,7 +235,7 @@ class TooManySigops(BadTxTemplate):
expect_disconnect = False
def get_tx(self):
- lotsa_checksigs = sc.CScript([sc.OP_CHECKSIG] * (MAX_BLOCK_SIGOPS))
+ lotsa_checksigs = CScript([OP_CHECKSIG] * (MAX_BLOCK_SIGOPS))
return create_tx_with_script(
self.spend_tx, 0,
script_pub_key=lotsa_checksigs,
diff --git a/test/functional/feature_anchors.py b/test/functional/feature_anchors.py
index a60a723b3e..24bb02bc90 100755
--- a/test/functional/feature_anchors.py
+++ b/test/functional/feature_anchors.py
@@ -2,7 +2,7 @@
# 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.
-"""Test Anchors functionality"""
+"""Test block-relay-only anchors functionality"""
import os
@@ -10,6 +10,9 @@ from test_framework.p2p import P2PInterface
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_equal
+INBOUND_CONNECTIONS = 5
+BLOCK_RELAY_CONNECTIONS = 2
+
def check_node_connections(*, node, num_in, num_out):
info = node.getnetworkinfo()
@@ -25,19 +28,26 @@ class AnchorsTest(BitcoinTestFramework):
self.setup_nodes()
def run_test(self):
- self.log.info("Add 2 block-relay-only connections to node 0")
- for i in range(2):
+ node_anchors_path = os.path.join(
+ self.nodes[0].datadir, "regtest", "anchors.dat"
+ )
+
+ self.log.info("When node starts, check if anchors.dat doesn't exist")
+ assert not os.path.exists(node_anchors_path)
+
+ self.log.info(f"Add {BLOCK_RELAY_CONNECTIONS} block-relay-only connections to node")
+ for i in range(BLOCK_RELAY_CONNECTIONS):
self.log.debug(f"block-relay-only: {i}")
self.nodes[0].add_outbound_p2p_connection(
P2PInterface(), p2p_idx=i, connection_type="block-relay-only"
)
- self.log.info("Add 5 inbound connections to node 0")
- for i in range(5):
+ self.log.info(f"Add {INBOUND_CONNECTIONS} inbound connections to node")
+ for i in range(INBOUND_CONNECTIONS):
self.log.debug(f"inbound: {i}")
self.nodes[0].add_p2p_connection(P2PInterface())
- self.log.info("Check node 0 connections")
+ self.log.info("Check node connections")
check_node_connections(node=self.nodes[0], num_in=5, num_out=2)
# 127.0.0.1
@@ -57,14 +67,10 @@ class AnchorsTest(BitcoinTestFramework):
self.log.info("Stop node 0")
self.stop_node(0)
- node0_anchors_path = os.path.join(
- self.nodes[0].datadir, "regtest", "anchors.dat"
- )
-
# It should contain only the block-relay-only addresses
self.log.info("Check the addresses in anchors.dat")
- with open(node0_anchors_path, "rb") as file_handler:
+ with open(node_anchors_path, "rb") as file_handler:
anchors = file_handler.read().hex()
for port in block_relay_nodes_port:
@@ -74,11 +80,11 @@ class AnchorsTest(BitcoinTestFramework):
ip_port = ip + port
assert ip_port not in anchors
- self.log.info("Start node 0")
+ self.log.info("Start node")
self.start_node(0)
self.log.info("When node starts, check if anchors.dat doesn't exist anymore")
- assert not os.path.exists(node0_anchors_path)
+ assert not os.path.exists(node_anchors_path)
if __name__ == "__main__":
diff --git a/test/functional/feature_assumevalid.py b/test/functional/feature_assumevalid.py
index 1a148f04f4..a4480307a7 100755
--- a/test/functional/feature_assumevalid.py
+++ b/test/functional/feature_assumevalid.py
@@ -31,6 +31,7 @@ Start three nodes:
"""
from test_framework.blocktools import (
+ COINBASE_MATURITY,
create_block,
create_coinbase,
)
@@ -161,8 +162,8 @@ class AssumeValidTest(BitcoinTestFramework):
# Send blocks to node0. Block 102 will be rejected.
self.send_blocks_until_disconnected(p2p0)
- self.wait_until(lambda: self.nodes[0].getblockcount() >= 101)
- assert_equal(self.nodes[0].getblockcount(), 101)
+ self.wait_until(lambda: self.nodes[0].getblockcount() >= COINBASE_MATURITY + 1)
+ assert_equal(self.nodes[0].getblockcount(), COINBASE_MATURITY + 1)
# Send all blocks to node1. All blocks will be accepted.
for i in range(2202):
@@ -173,8 +174,8 @@ class AssumeValidTest(BitcoinTestFramework):
# Send blocks to node2. Block 102 will be rejected.
self.send_blocks_until_disconnected(p2p2)
- self.wait_until(lambda: self.nodes[2].getblockcount() >= 101)
- assert_equal(self.nodes[2].getblockcount(), 101)
+ self.wait_until(lambda: self.nodes[2].getblockcount() >= COINBASE_MATURITY + 1)
+ assert_equal(self.nodes[2].getblockcount(), COINBASE_MATURITY + 1)
if __name__ == '__main__':
diff --git a/test/functional/feature_backwards_compatibility.py b/test/functional/feature_backwards_compatibility.py
index e6a53b52db..e0ba835f99 100755
--- a/test/functional/feature_backwards_compatibility.py
+++ b/test/functional/feature_backwards_compatibility.py
@@ -4,9 +4,8 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Backwards compatibility functional test
-Test various backwards compatibility scenarios. Download the previous node binaries:
-
-test/get_previous_releases.py -b v0.19.1 v0.18.1 v0.17.2 v0.16.3 v0.15.2
+Test various backwards compatibility scenarios. Requires previous releases binaries,
+see test/README.md.
v0.15.2 is not required by this test, but it is used in wallet_upgradewallet.py.
Due to a hardfork in regtest, it can't be used to sync nodes.
@@ -22,6 +21,7 @@ needs an older patch version.
import os
import shutil
+from test_framework.blocktools import COINBASE_MATURITY
from test_framework.test_framework import BitcoinTestFramework
from test_framework.descriptors import descsum_create
@@ -64,13 +64,13 @@ class BackwardsCompatibilityTest(BitcoinTestFramework):
self.import_deterministic_coinbase_privkeys()
def run_test(self):
- self.nodes[0].generatetoaddress(101, self.nodes[0].getnewaddress())
+ self.nodes[0].generatetoaddress(COINBASE_MATURITY + 1, self.nodes[0].getnewaddress())
self.sync_blocks()
# Sanity check the test framework:
res = self.nodes[self.num_nodes - 1].getblockchaininfo()
- assert_equal(res['blocks'], 101)
+ assert_equal(res['blocks'], COINBASE_MATURITY + 1)
node_master = self.nodes[self.num_nodes - 5]
node_v19 = self.nodes[self.num_nodes - 4]
diff --git a/test/functional/feature_bind_extra.py b/test/functional/feature_bind_extra.py
new file mode 100755
index 0000000000..6802da8d48
--- /dev/null
+++ b/test/functional/feature_bind_extra.py
@@ -0,0 +1,95 @@
+#!/usr/bin/env python3
+# Copyright (c) 2014-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 starting bitcoind with -bind and/or -bind=...=onion and confirm
+that bind happens on the expected ports.
+"""
+
+import sys
+
+from test_framework.netutil import (
+ addr_to_hex,
+ get_bind_addrs,
+)
+from test_framework.test_framework import (
+ BitcoinTestFramework,
+ SkipTest,
+)
+from test_framework.util import (
+ PORT_MIN,
+ PORT_RANGE,
+ assert_equal,
+ rpc_port,
+)
+
+class BindExtraTest(BitcoinTestFramework):
+ def set_test_params(self):
+ self.setup_clean_chain = True
+ # Avoid any -bind= on the command line. Force the framework to avoid
+ # adding -bind=127.0.0.1.
+ self.bind_to_localhost_only = False
+ self.num_nodes = 2
+
+ def setup_network(self):
+ # Override setup_network() because we want to put the result of
+ # p2p_port() in self.extra_args[], before the nodes are started.
+ # p2p_port() is not usable in set_test_params() because PortSeed.n is
+ # not set at that time.
+
+ # Due to OS-specific network stats queries, we only run on Linux.
+ self.log.info("Checking for Linux")
+ if not sys.platform.startswith('linux'):
+ raise SkipTest("This test can only be run on Linux.")
+
+ loopback_ipv4 = addr_to_hex("127.0.0.1")
+
+ # Start custom ports after p2p and rpc ports.
+ port = PORT_MIN + 2 * PORT_RANGE
+
+ # Array of tuples [command line arguments, expected bind addresses].
+ self.expected = []
+
+ # Node0, no normal -bind=... with -bind=...=onion, thus only the tor target.
+ self.expected.append(
+ [
+ [f"-bind=127.0.0.1:{port}=onion"],
+ [(loopback_ipv4, port)]
+ ],
+ )
+ port += 1
+
+ # Node1, both -bind=... and -bind=...=onion.
+ self.expected.append(
+ [
+ [f"-bind=127.0.0.1:{port}", f"-bind=127.0.0.1:{port + 1}=onion"],
+ [(loopback_ipv4, port), (loopback_ipv4, port + 1)]
+ ],
+ )
+ port += 2
+
+ self.extra_args = list(map(lambda e: e[0], self.expected))
+ self.add_nodes(self.num_nodes, self.extra_args)
+ # Don't start the nodes, as some of them would collide trying to bind on the same port.
+
+ def run_test(self):
+ for i in range(len(self.expected)):
+ self.log.info(f"Starting node {i} with {self.expected[i][0]}")
+ self.start_node(i)
+ pid = self.nodes[i].process.pid
+ binds = set(get_bind_addrs(pid))
+ # Remove IPv6 addresses because on some CI environments "::1" is not configured
+ # on the system (so our test_ipv6_local() would return False), but it is
+ # possible to bind on "::". This makes it unpredictable whether to expect
+ # that bitcoind has bound on "::1" (for RPC) and "::" (for P2P).
+ ipv6_addr_len_bytes = 32
+ binds = set(filter(lambda e: len(e[0]) != ipv6_addr_len_bytes, binds))
+ # Remove RPC ports. They are not relevant for this test.
+ binds = set(filter(lambda e: e[1] != rpc_port(i), binds))
+ assert_equal(binds, set(self.expected[i][1]))
+ self.stop_node(i)
+ self.log.info(f"Stopped node {i}")
+
+if __name__ == '__main__':
+ BindExtraTest().main()
diff --git a/test/functional/feature_bip68_sequence.py b/test/functional/feature_bip68_sequence.py
index 6c5857c5ce..e44ce9b57d 100755
--- a/test/functional/feature_bip68_sequence.py
+++ b/test/functional/feature_bip68_sequence.py
@@ -6,8 +6,19 @@
import time
-from test_framework.blocktools import create_block, NORMAL_GBT_REQUEST_PARAMS, add_witness_commitment
-from test_framework.messages import COIN, COutPoint, CTransaction, CTxIn, CTxOut, FromHex, ToHex
+from test_framework.blocktools import (
+ NORMAL_GBT_REQUEST_PARAMS,
+ add_witness_commitment,
+ create_block,
+)
+from test_framework.messages import (
+ COIN,
+ COutPoint,
+ CTransaction,
+ CTxIn,
+ CTxOut,
+ tx_from_hex,
+)
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
@@ -89,7 +100,7 @@ class BIP68Test(BitcoinTestFramework):
tx1.vin = [CTxIn(COutPoint(int(utxo["txid"], 16), utxo["vout"]), nSequence=sequence_value)]
tx1.vout = [CTxOut(value, DUMMY_P2WPKH_SCRIPT)]
- tx1_signed = self.nodes[0].signrawtransactionwithwallet(ToHex(tx1))["hex"]
+ tx1_signed = self.nodes[0].signrawtransactionwithwallet(tx1.serialize().hex())["hex"]
tx1_id = self.nodes[0].sendrawtransaction(tx1_signed)
tx1_id = int(tx1_id, 16)
@@ -102,13 +113,13 @@ class BIP68Test(BitcoinTestFramework):
tx2.vout = [CTxOut(int(value - self.relayfee * COIN), DUMMY_P2WPKH_SCRIPT)]
tx2.rehash()
- assert_raises_rpc_error(-26, NOT_FINAL_ERROR, self.nodes[0].sendrawtransaction, ToHex(tx2))
+ assert_raises_rpc_error(-26, NOT_FINAL_ERROR, self.nodes[0].sendrawtransaction, tx2.serialize().hex())
# Setting the version back down to 1 should disable the sequence lock,
# so this should be accepted.
tx2.nVersion = 1
- self.nodes[0].sendrawtransaction(ToHex(tx2))
+ self.nodes[0].sendrawtransaction(tx2.serialize().hex())
# Calculate the median time past of a prior block ("confirmations" before
# the current tip).
@@ -193,9 +204,9 @@ class BIP68Test(BitcoinTestFramework):
tx.vin.append(CTxIn(COutPoint(int(utxos[j]["txid"], 16), utxos[j]["vout"]), nSequence=sequence_value))
value += utxos[j]["amount"]*COIN
# Overestimate the size of the tx - signatures should be less than 120 bytes, and leave 50 for the output
- tx_size = len(ToHex(tx))//2 + 120*num_inputs + 50
+ tx_size = len(tx.serialize().hex())//2 + 120*num_inputs + 50
tx.vout.append(CTxOut(int(value-self.relayfee*tx_size*COIN/1000), DUMMY_P2WPKH_SCRIPT))
- rawtx = self.nodes[0].signrawtransactionwithwallet(ToHex(tx))["hex"]
+ rawtx = self.nodes[0].signrawtransactionwithwallet(tx.serialize().hex())["hex"]
if (using_sequence_locks and not should_pass):
# This transaction should be rejected
@@ -215,7 +226,7 @@ class BIP68Test(BitcoinTestFramework):
# Create a mempool tx.
txid = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 2)
- tx1 = FromHex(CTransaction(), self.nodes[0].getrawtransaction(txid))
+ tx1 = tx_from_hex(self.nodes[0].getrawtransaction(txid))
tx1.rehash()
# Anyone-can-spend mempool tx.
@@ -224,8 +235,8 @@ class BIP68Test(BitcoinTestFramework):
tx2.nVersion = 2
tx2.vin = [CTxIn(COutPoint(tx1.sha256, 0), nSequence=0)]
tx2.vout = [CTxOut(int(tx1.vout[0].nValue - self.relayfee*COIN), DUMMY_P2WPKH_SCRIPT)]
- tx2_raw = self.nodes[0].signrawtransactionwithwallet(ToHex(tx2))["hex"]
- tx2 = FromHex(tx2, tx2_raw)
+ tx2_raw = self.nodes[0].signrawtransactionwithwallet(tx2.serialize().hex())["hex"]
+ tx2 = tx_from_hex(tx2_raw)
tx2.rehash()
self.nodes[0].sendrawtransaction(tx2_raw)
@@ -246,10 +257,10 @@ class BIP68Test(BitcoinTestFramework):
if (orig_tx.hash in node.getrawmempool()):
# sendrawtransaction should fail if the tx is in the mempool
- assert_raises_rpc_error(-26, NOT_FINAL_ERROR, node.sendrawtransaction, ToHex(tx))
+ assert_raises_rpc_error(-26, NOT_FINAL_ERROR, node.sendrawtransaction, tx.serialize().hex())
else:
# sendrawtransaction should succeed if the tx is not in the mempool
- node.sendrawtransaction(ToHex(tx))
+ node.sendrawtransaction(tx.serialize().hex())
return tx
@@ -299,7 +310,7 @@ class BIP68Test(BitcoinTestFramework):
utxos = self.nodes[0].listunspent()
tx5.vin.append(CTxIn(COutPoint(int(utxos[0]["txid"], 16), utxos[0]["vout"]), nSequence=1))
tx5.vout[0].nValue += int(utxos[0]["amount"]*COIN)
- raw_tx5 = self.nodes[0].signrawtransactionwithwallet(ToHex(tx5))["hex"]
+ raw_tx5 = self.nodes[0].signrawtransactionwithwallet(tx5.serialize().hex())["hex"]
assert_raises_rpc_error(-26, NOT_FINAL_ERROR, self.nodes[0].sendrawtransaction, raw_tx5)
@@ -325,7 +336,7 @@ class BIP68Test(BitcoinTestFramework):
block.rehash()
block.solve()
tip = block.sha256
- assert_equal(None if i == 1 else 'inconclusive', self.nodes[0].submitblock(ToHex(block)))
+ assert_equal(None if i == 1 else 'inconclusive', self.nodes[0].submitblock(block.serialize().hex()))
tmpl = self.nodes[0].getblocktemplate(NORMAL_GBT_REQUEST_PARAMS)
tmpl['previousblockhash'] = '%x' % tip
tmpl['transactions'] = []
@@ -348,7 +359,7 @@ class BIP68Test(BitcoinTestFramework):
assert not softfork_active(self.nodes[0], 'csv')
txid = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 2)
- tx1 = FromHex(CTransaction(), self.nodes[0].getrawtransaction(txid))
+ tx1 = tx_from_hex(self.nodes[0].getrawtransaction(txid))
tx1.rehash()
# Make an anyone-can-spend transaction
@@ -358,11 +369,11 @@ class BIP68Test(BitcoinTestFramework):
tx2.vout = [CTxOut(int(tx1.vout[0].nValue - self.relayfee*COIN), DUMMY_P2WPKH_SCRIPT)]
# sign tx2
- tx2_raw = self.nodes[0].signrawtransactionwithwallet(ToHex(tx2))["hex"]
- tx2 = FromHex(tx2, tx2_raw)
+ tx2_raw = self.nodes[0].signrawtransactionwithwallet(tx2.serialize().hex())["hex"]
+ tx2 = tx_from_hex(tx2_raw)
tx2.rehash()
- self.nodes[0].sendrawtransaction(ToHex(tx2))
+ self.nodes[0].sendrawtransaction(tx2.serialize().hex())
# Now make an invalid spend of tx2 according to BIP68
sequence_value = 100 # 100 block relative locktime
@@ -373,7 +384,7 @@ class BIP68Test(BitcoinTestFramework):
tx3.vout = [CTxOut(int(tx2.vout[0].nValue - self.relayfee * COIN), DUMMY_P2WPKH_SCRIPT)]
tx3.rehash()
- assert_raises_rpc_error(-26, NOT_FINAL_ERROR, self.nodes[0].sendrawtransaction, ToHex(tx3))
+ assert_raises_rpc_error(-26, NOT_FINAL_ERROR, self.nodes[0].sendrawtransaction, tx3.serialize().hex())
# make a block that violates bip68; ensure that the tip updates
block = create_block(tmpl=self.nodes[0].getblocktemplate(NORMAL_GBT_REQUEST_PARAMS))
@@ -404,9 +415,9 @@ class BIP68Test(BitcoinTestFramework):
outputs = { self.nodes[1].getnewaddress() : 1.0 }
rawtx = self.nodes[1].createrawtransaction(inputs, outputs)
rawtxfund = self.nodes[1].fundrawtransaction(rawtx)['hex']
- tx = FromHex(CTransaction(), rawtxfund)
+ tx = tx_from_hex(rawtxfund)
tx.nVersion = 2
- tx_signed = self.nodes[1].signrawtransactionwithwallet(ToHex(tx))["hex"]
+ tx_signed = self.nodes[1].signrawtransactionwithwallet(tx.serialize().hex())["hex"]
self.nodes[1].sendrawtransaction(tx_signed)
if __name__ == '__main__':
diff --git a/test/functional/feature_block.py b/test/functional/feature_block.py
index 158efb52c9..c11eabc917 100755
--- a/test/functional/feature_block.py
+++ b/test/functional/feature_block.py
@@ -37,17 +37,17 @@ from test_framework.script import (
OP_CHECKSIGVERIFY,
OP_ELSE,
OP_ENDIF,
- OP_EQUAL,
OP_DROP,
OP_FALSE,
- OP_HASH160,
OP_IF,
OP_INVALIDOPCODE,
OP_RETURN,
OP_TRUE,
SIGHASH_ALL,
LegacySignatureHash,
- hash160,
+)
+from test_framework.script_util import (
+ script_to_p2sh_script,
)
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_equal
@@ -469,8 +469,7 @@ class FullBlockTest(BitcoinTestFramework):
# Build the redeem script, hash it, use hash to create the p2sh script
redeem_script = CScript([self.coinbase_pubkey] + [OP_2DUP, OP_CHECKSIGVERIFY] * 5 + [OP_CHECKSIG])
- redeem_script_hash = hash160(redeem_script)
- p2sh_script = CScript([OP_HASH160, redeem_script_hash, OP_EQUAL])
+ p2sh_script = script_to_p2sh_script(redeem_script)
# Create a transaction that spends one satoshi to the p2sh_script, the rest to OP_TRUE
# This must be signed because it is spending a coinbase
@@ -591,6 +590,8 @@ class FullBlockTest(BitcoinTestFramework):
b44.hashPrevBlock = self.tip.sha256
b44.nBits = 0x207fffff
b44.vtx.append(coinbase)
+ tx = self.create_and_sign_transaction(out[14], 1)
+ b44.vtx.append(tx)
b44.hashMerkleRoot = b44.calc_merkle_root()
b44.solve()
self.tip = b44
@@ -678,7 +679,7 @@ class FullBlockTest(BitcoinTestFramework):
# Test block timestamps
# -> b31 (8) -> b33 (9) -> b35 (10) -> b39 (11) -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15)
# \-> b54 (15)
- #
+ # -> b44 (14)\-> b48 ()
self.move_tip(43)
b53 = self.next_block(53, spend=out[14])
self.send_blocks([b53], False)
@@ -698,6 +699,21 @@ class FullBlockTest(BitcoinTestFramework):
self.send_blocks([b55], True)
self.save_spendable_output()
+ # The block which was previously rejected because of being "too far(3 hours)" must be accepted 2 hours later.
+ # The new block is only 1 hour into future now and we must reorg onto to the new longer chain.
+ # The new bestblock b48p is invalidated manually.
+ # -> b31 (8) -> b33 (9) -> b35 (10) -> b39 (11) -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15)
+ # \-> b54 (15)
+ # -> b44 (14)\-> b48 () -> b48p ()
+ self.log.info("Accept a previously rejected future block at a later time")
+ node.setmocktime(int(time.time()) + 2*60*60)
+ self.move_tip(48)
+ self.block_heights[b48.sha256] = self.block_heights[b44.sha256] + 1 # b48 is a parent of b44
+ b48p = self.next_block("48p")
+ self.send_blocks([b48, b48p], success=True) # Reorg to the longer chain
+ node.invalidateblock(b48p.hash) # mark b48p as invalid
+ node.setmocktime(0)
+
# Test Merkle tree malleability
#
# -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15) -> b57p2 (16)
@@ -1308,7 +1324,7 @@ class FullBlockTest(BitcoinTestFramework):
return create_tx_with_script(spend_tx, n, amount=value, script_pub_key=script)
# sign a transaction, using the key we know about
- # this signs input 0 in tx, which is assumed to be spending output n in spend_tx
+ # this signs input 0 in tx, which is assumed to be spending output 0 in spend_tx
def sign_tx(self, tx, spend_tx):
scriptPubKey = bytearray(spend_tx.vout[0].scriptPubKey)
if (scriptPubKey[0] == OP_TRUE): # an anyone-can-spend
diff --git a/test/functional/feature_cltv.py b/test/functional/feature_cltv.py
index 6c51944d81..7c14f5d5a6 100755
--- a/test/functional/feature_cltv.py
+++ b/test/functional/feature_cltv.py
@@ -9,6 +9,7 @@ Test that the CHECKLOCKTIMEVERIFY soft-fork activates at (regtest) block height
"""
from test_framework.blocktools import (
+ CLTV_HEIGHT,
create_block,
create_coinbase,
)
@@ -26,15 +27,16 @@ from test_framework.script import (
)
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_equal
-from test_framework.wallet import MiniWallet
-
-CLTV_HEIGHT = 1351
+from test_framework.wallet import (
+ MiniWallet,
+ MiniWalletMode,
+)
# Helper function to modify a transaction by
# 1) prepending a given script to the scriptSig of vin 0 and
# 2) (optionally) modify the nSequence of vin 0 and the tx's nLockTime
-def cltv_modify_tx(node, tx, prepend_scriptsig, nsequence=None, nlocktime=None):
+def cltv_modify_tx(tx, prepend_scriptsig, nsequence=None, nlocktime=None):
assert_equal(len(tx.vin), 1)
if nsequence is not None:
tx.vin[0].nSequence = nsequence
@@ -42,10 +44,9 @@ def cltv_modify_tx(node, tx, prepend_scriptsig, nsequence=None, nlocktime=None):
tx.vin[0].scriptSig = CScript(prepend_scriptsig + list(CScript(tx.vin[0].scriptSig)))
tx.rehash()
- return tx
-def cltv_invalidate(node, tx, failure_reason):
+def cltv_invalidate(tx, failure_reason):
# Modify the signature in vin 0 and nSequence/nLockTime of the tx to fail CLTV
#
# According to BIP65, OP_CHECKLOCKTIMEVERIFY can fail due the following reasons:
@@ -66,14 +67,14 @@ def cltv_invalidate(node, tx, failure_reason):
[[CScriptNum(500), OP_CHECKLOCKTIMEVERIFY, OP_DROP], 0xffffffff, 500],
][failure_reason]
- return cltv_modify_tx(node, tx, prepend_scriptsig=scheme[0], nsequence=scheme[1], nlocktime=scheme[2])
+ cltv_modify_tx(tx, prepend_scriptsig=scheme[0], nsequence=scheme[1], nlocktime=scheme[2])
-def cltv_validate(node, tx, height):
+def cltv_validate(tx, height):
# Modify the signature in vin 0 and nSequence/nLockTime of the tx to pass CLTV
scheme = [[CScriptNum(height), OP_CHECKLOCKTIMEVERIFY, OP_DROP], 0, height]
- return cltv_modify_tx(node, tx, prepend_scriptsig=scheme[0], nsequence=scheme[1], nlocktime=scheme[2])
+ cltv_modify_tx(tx, prepend_scriptsig=scheme[0], nsequence=scheme[1], nlocktime=scheme[2])
class BIP65Test(BitcoinTestFramework):
@@ -97,7 +98,7 @@ class BIP65Test(BitcoinTestFramework):
def run_test(self):
peer = self.nodes[0].add_p2p_connection(P2PInterface())
- wallet = MiniWallet(self.nodes[0], raw_script=True)
+ wallet = MiniWallet(self.nodes[0], mode=MiniWalletMode.RAW_OP_TRUE)
self.test_cltv_info(is_active=False)
@@ -108,17 +109,17 @@ class BIP65Test(BitcoinTestFramework):
self.log.info("Test that invalid-according-to-CLTV transactions can still appear in a block")
# create one invalid tx per CLTV failure reason (5 in total) and collect them
- invalid_ctlv_txs = []
+ invalid_cltv_txs = []
for i in range(5):
spendtx = wallet.create_self_transfer(from_node=self.nodes[0])['tx']
- spendtx = cltv_invalidate(self.nodes[0], spendtx, i)
- invalid_ctlv_txs.append(spendtx)
+ cltv_invalidate(spendtx, i)
+ invalid_cltv_txs.append(spendtx)
tip = self.nodes[0].getbestblockhash()
block_time = self.nodes[0].getblockheader(tip)['mediantime'] + 1
block = create_block(int(tip, 16), create_coinbase(CLTV_HEIGHT - 1), block_time)
block.nVersion = 3
- block.vtx.extend(invalid_ctlv_txs)
+ block.vtx.extend(invalid_cltv_txs)
block.hashMerkleRoot = block.calc_merkle_root()
block.solve()
@@ -146,7 +147,7 @@ class BIP65Test(BitcoinTestFramework):
# create and test one invalid tx per CLTV failure reason (5 in total)
for i in range(5):
spendtx = wallet.create_self_transfer(from_node=self.nodes[0])['tx']
- spendtx = cltv_invalidate(self.nodes[0], spendtx, i)
+ cltv_invalidate(spendtx, i)
expected_cltv_reject_reason = [
"non-mandatory-script-verify-flag (Operation not valid with the current stack size)",
@@ -179,7 +180,7 @@ class BIP65Test(BitcoinTestFramework):
peer.sync_with_ping()
self.log.info("Test that a version 4 block with a valid-according-to-CLTV transaction is accepted")
- spendtx = cltv_validate(self.nodes[0], spendtx, CLTV_HEIGHT - 1)
+ cltv_validate(spendtx, CLTV_HEIGHT - 1)
block.vtx.pop(1)
block.vtx.append(spendtx)
diff --git a/test/functional/feature_coinstatsindex.py b/test/functional/feature_coinstatsindex.py
index c2bc485d6b..71d522a245 100755
--- a/test/functional/feature_coinstatsindex.py
+++ b/test/functional/feature_coinstatsindex.py
@@ -12,6 +12,7 @@ the index.
from decimal import Decimal
from test_framework.blocktools import (
+ COINBASE_MATURITY,
create_block,
create_coinbase,
)
@@ -21,7 +22,6 @@ from test_framework.messages import (
CTransaction,
CTxIn,
CTxOut,
- ToHex,
)
from test_framework.script import (
CScript,
@@ -67,7 +67,7 @@ class CoinStatsIndexTest(BitcoinTestFramework):
index_hash_options = ['none', 'muhash']
# Generate a normal transaction and mine it
- node.generate(101)
+ node.generate(COINBASE_MATURITY + 1)
address = self.nodes[0].get_deterministic_priv_key().address
node.sendtoaddress(address=address, amount=10, subtractfeefromamount=True)
node.generate(1)
@@ -165,7 +165,7 @@ class CoinStatsIndexTest(BitcoinTestFramework):
tx2 = CTransaction()
tx2.vin.append(CTxIn(COutPoint(int(tx1_txid, 16), n), b''))
tx2.vout.append(CTxOut(int(20.99 * COIN), CScript([OP_RETURN] + [OP_FALSE]*30)))
- tx2_hex = self.nodes[0].signrawtransactionwithwallet(ToHex(tx2))['hex']
+ tx2_hex = self.nodes[0].signrawtransactionwithwallet(tx2.serialize().hex())['hex']
self.nodes[0].sendrawtransaction(tx2_hex)
# Include both txs in a block
@@ -201,7 +201,7 @@ class CoinStatsIndexTest(BitcoinTestFramework):
block_time = self.nodes[0].getblock(tip)['time'] + 1
block = create_block(int(tip, 16), cb, block_time)
block.solve()
- self.nodes[0].submitblock(ToHex(block))
+ self.nodes[0].submitblock(block.serialize().hex())
self.sync_all()
for hash_option in index_hash_options:
@@ -271,6 +271,7 @@ class CoinStatsIndexTest(BitcoinTestFramework):
# Add another block, so we don't depend on reconsiderblock remembering which
# blocks were touched by invalidateblock
index_node.generate(1)
+ self.sync_all()
# Ensure that removing and re-adding blocks yields consistent results
block = index_node.getblockhash(99)
diff --git a/test/functional/feature_csv_activation.py b/test/functional/feature_csv_activation.py
index d815ad83b1..1ac1a0563f 100755
--- a/test/functional/feature_csv_activation.py
+++ b/test/functional/feature_csv_activation.py
@@ -41,6 +41,7 @@ from itertools import product
import time
from test_framework.blocktools import (
+ CSV_ACTIVATION_HEIGHT,
create_block,
create_coinbase,
)
@@ -55,12 +56,14 @@ from test_framework.util import (
assert_equal,
softfork_active,
)
-from test_framework.wallet import MiniWallet
+from test_framework.wallet import (
+ MiniWallet,
+ MiniWalletMode,
+)
TESTING_TX_COUNT = 83 # Number of testing transactions: 1 BIP113 tx, 16 BIP68 txs, 66 BIP112 txs (see comments above)
COINBASE_BLOCK_COUNT = TESTING_TX_COUNT # Number of coinbase blocks we need to generate as inputs for our txs
BASE_RELATIVE_LOCKTIME = 10
-CSV_ACTIVATION_HEIGHT = 432
SEQ_DISABLE_FLAG = 1 << 31
SEQ_RANDOM_HIGH_BIT = 1 << 25
SEQ_TYPE_FLAG = 1 << 22
@@ -181,7 +184,7 @@ class BIP68_112_113Test(BitcoinTestFramework):
def run_test(self):
self.helper_peer = self.nodes[0].add_p2p_connection(P2PDataStore())
- self.miniwallet = MiniWallet(self.nodes[0], use_p2pk=True)
+ self.miniwallet = MiniWallet(self.nodes[0], mode=MiniWalletMode.RAW_P2PK)
self.log.info("Generate blocks in the past for coinbase outputs.")
long_past_time = int(time.time()) - 600 * 1000 # enough to build up to 1000 blocks 10 minutes apart without worrying about getting into the future
diff --git a/test/functional/feature_dbcrash.py b/test/functional/feature_dbcrash.py
index 2b56bc78f5..c532300ce2 100755
--- a/test/functional/feature_dbcrash.py
+++ b/test/functional/feature_dbcrash.py
@@ -36,7 +36,6 @@ from test_framework.messages import (
CTransaction,
CTxIn,
CTxOut,
- ToHex,
)
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
@@ -208,7 +207,7 @@ class ChainstateWriteCrashTest(BitcoinTestFramework):
tx.vout.append(CTxOut(output_amount, hex_str_to_bytes(utxo['scriptPubKey'])))
# Sign and send the transaction to get into the mempool
- tx_signed_hex = node.signrawtransactionwithwallet(ToHex(tx))['hex']
+ tx_signed_hex = node.signrawtransactionwithwallet(tx.serialize().hex())['hex']
node.sendrawtransaction(tx_signed_hex)
num_transactions += 1
diff --git a/test/functional/feature_dersig.py b/test/functional/feature_dersig.py
index 3b430139b1..eb027c554a 100755
--- a/test/functional/feature_dersig.py
+++ b/test/functional/feature_dersig.py
@@ -7,7 +7,10 @@
Test that the DERSIG soft-fork activates at (regtest) height 1251.
"""
-from test_framework.blocktools import create_coinbase, create_block, create_transaction
+from test_framework.blocktools import (
+ create_block,
+ create_coinbase,
+)
from test_framework.messages import msg_block
from test_framework.p2p import P2PInterface
from test_framework.script import CScript
@@ -15,6 +18,10 @@ from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
)
+from test_framework.wallet import (
+ MiniWallet,
+ MiniWalletMode,
+)
DERSIG_HEIGHT = 1251
@@ -46,8 +53,9 @@ class BIP66Test(BitcoinTestFramework):
self.setup_clean_chain = True
self.rpc_timeout = 240
- def skip_test_if_missing_module(self):
- self.skip_if_no_wallet()
+ def create_tx(self, input_txid):
+ utxo_to_spend = self.miniwallet.get_utxo(txid=input_txid, mark_as_spent=False)
+ return self.miniwallet.create_self_transfer(from_node=self.nodes[0], utxo_to_spend=utxo_to_spend)['tx']
def test_dersig_info(self, *, is_active):
assert_equal(self.nodes[0].getblockchaininfo()['softforks']['bip66'],
@@ -60,17 +68,16 @@ class BIP66Test(BitcoinTestFramework):
def run_test(self):
peer = self.nodes[0].add_p2p_connection(P2PInterface())
+ self.miniwallet = MiniWallet(self.nodes[0], mode=MiniWalletMode.RAW_P2PK)
self.test_dersig_info(is_active=False)
self.log.info("Mining %d blocks", DERSIG_HEIGHT - 2)
- self.coinbase_txids = [self.nodes[0].getblock(b)['tx'][0] for b in self.nodes[0].generate(DERSIG_HEIGHT - 2)]
- self.nodeaddress = self.nodes[0].getnewaddress()
+ self.coinbase_txids = [self.nodes[0].getblock(b)['tx'][0] for b in self.miniwallet.generate(DERSIG_HEIGHT - 2)]
self.log.info("Test that a transaction with non-DER signature can still appear in a block")
- spendtx = create_transaction(self.nodes[0], self.coinbase_txids[0],
- self.nodeaddress, amount=1.0)
+ spendtx = self.create_tx(self.coinbase_txids[0])
unDERify(spendtx)
spendtx.rehash()
@@ -104,8 +111,7 @@ class BIP66Test(BitcoinTestFramework):
self.log.info("Test that transactions with non-DER signatures cannot appear in a block")
block.nVersion = 3
- spendtx = create_transaction(self.nodes[0], self.coinbase_txids[1],
- self.nodeaddress, amount=1.0)
+ spendtx = self.create_tx(self.coinbase_txids[1])
unDERify(spendtx)
spendtx.rehash()
@@ -133,7 +139,7 @@ class BIP66Test(BitcoinTestFramework):
peer.sync_with_ping()
self.log.info("Test that a version 3 block with a DERSIG-compliant transaction is accepted")
- block.vtx[1] = create_transaction(self.nodes[0], self.coinbase_txids[1], self.nodeaddress, amount=1.0)
+ block.vtx[1] = self.create_tx(self.coinbase_txids[1])
block.hashMerkleRoot = block.calc_merkle_root()
block.rehash()
block.solve()
diff --git a/test/functional/feature_fee_estimation.py b/test/functional/feature_fee_estimation.py
index 8f522aee66..5322b02414 100755
--- a/test/functional/feature_fee_estimation.py
+++ b/test/functional/feature_fee_estimation.py
@@ -6,8 +6,23 @@
from decimal import Decimal
import random
-from test_framework.messages import CTransaction, CTxIn, CTxOut, COutPoint, ToHex, COIN
-from test_framework.script import CScript, OP_1, OP_DROP, OP_2, OP_HASH160, OP_EQUAL, hash160, OP_TRUE
+from test_framework.messages import (
+ COIN,
+ COutPoint,
+ CTransaction,
+ CTxIn,
+ CTxOut,
+)
+from test_framework.script import (
+ CScript,
+ OP_1,
+ OP_2,
+ OP_DROP,
+ OP_TRUE,
+)
+from test_framework.script_util import (
+ script_to_p2sh_script,
+)
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
@@ -22,8 +37,8 @@ from test_framework.util import (
# time signing.
REDEEM_SCRIPT_1 = CScript([OP_1, OP_DROP])
REDEEM_SCRIPT_2 = CScript([OP_2, OP_DROP])
-P2SH_1 = CScript([OP_HASH160, hash160(REDEEM_SCRIPT_1), OP_EQUAL])
-P2SH_2 = CScript([OP_HASH160, hash160(REDEEM_SCRIPT_2), OP_EQUAL])
+P2SH_1 = script_to_p2sh_script(REDEEM_SCRIPT_1)
+P2SH_2 = script_to_p2sh_script(REDEEM_SCRIPT_2)
# Associated ScriptSig's to spend satisfy P2SH_1 and P2SH_2
SCRIPT_SIG = [CScript([OP_TRUE, REDEEM_SCRIPT_1]), CScript([OP_TRUE, REDEEM_SCRIPT_2])]
@@ -64,11 +79,11 @@ def small_txpuzzle_randfee(from_node, conflist, unconflist, amount, min_fee, fee
# the ScriptSig that will satisfy the ScriptPubKey.
for inp in tx.vin:
inp.scriptSig = SCRIPT_SIG[inp.prevout.n]
- txid = from_node.sendrawtransaction(hexstring=ToHex(tx), maxfeerate=0)
+ txid = from_node.sendrawtransaction(hexstring=tx.serialize().hex(), maxfeerate=0)
unconflist.append({"txid": txid, "vout": 0, "amount": total_in - amount - fee})
unconflist.append({"txid": txid, "vout": 1, "amount": amount})
- return (ToHex(tx), fee)
+ return (tx.serialize().hex(), fee)
def split_inputs(from_node, txins, txouts, initial_split=False):
@@ -91,10 +106,10 @@ def split_inputs(from_node, txins, txouts, initial_split=False):
# If this is the initial split we actually need to sign the transaction
# Otherwise we just need to insert the proper ScriptSig
if (initial_split):
- completetx = from_node.signrawtransactionwithwallet(ToHex(tx))["hex"]
+ completetx = from_node.signrawtransactionwithwallet(tx.serialize().hex())["hex"]
else:
tx.vin[0].scriptSig = SCRIPT_SIG[prevtxout["vout"]]
- completetx = ToHex(tx)
+ completetx = tx.serialize().hex()
txid = from_node.sendrawtransaction(hexstring=completetx, maxfeerate=0)
txouts.append({"txid": txid, "vout": 0, "amount": half_change})
txouts.append({"txid": txid, "vout": 1, "amount": rem_change})
diff --git a/test/functional/feature_loadblock.py b/test/functional/feature_loadblock.py
index 0a457ca17f..14f64d63a2 100755
--- a/test/functional/feature_loadblock.py
+++ b/test/functional/feature_loadblock.py
@@ -16,6 +16,7 @@ import sys
import tempfile
import urllib
+from test_framework.blocktools import COINBASE_MATURITY
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_equal
@@ -28,7 +29,7 @@ class LoadblockTest(BitcoinTestFramework):
def run_test(self):
self.nodes[1].setnetworkactive(state=False)
- self.nodes[0].generate(100)
+ self.nodes[0].generate(COINBASE_MATURITY)
# Parsing the url of our node to get settings for config file
data_dir = self.nodes[0].datadir
diff --git a/test/functional/feature_nulldummy.py b/test/functional/feature_nulldummy.py
index c7981d31dc..f467626801 100755
--- a/test/functional/feature_nulldummy.py
+++ b/test/functional/feature_nulldummy.py
@@ -14,15 +14,21 @@ Generate COINBASE_MATURITY (CB) more blocks to ensure the coinbases are mature.
"""
import time
-from test_framework.blocktools import NORMAL_GBT_REQUEST_PARAMS, create_block, create_transaction, add_witness_commitment
+from test_framework.blocktools import (
+ COINBASE_MATURITY,
+ NORMAL_GBT_REQUEST_PARAMS,
+ add_witness_commitment,
+ create_block,
+ create_transaction,
+)
from test_framework.messages import CTransaction
from test_framework.script import CScript
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_equal, assert_raises_rpc_error
-COINBASE_MATURITY = 100
NULLDUMMY_ERROR = "non-mandatory-script-verify-flag (Dummy CHECKMULTISIG argument must be zero)"
+
def trueDummy(tx):
scriptSig = CScript(tx.vin[0].scriptSig)
newscript = []
@@ -35,18 +41,17 @@ def trueDummy(tx):
tx.vin[0].scriptSig = CScript(newscript)
tx.rehash()
-class NULLDUMMYTest(BitcoinTestFramework):
+class NULLDUMMYTest(BitcoinTestFramework):
def set_test_params(self):
- # Need two nodes so GBT (getblocktemplate) doesn't complain that it's not connected.
- self.num_nodes = 2
+ self.num_nodes = 1
self.setup_clean_chain = True
# This script tests NULLDUMMY activation, which is part of the 'segwit' deployment, so we go through
# normal segwit activation here (and don't use the default always-on behaviour).
self.extra_args = [[
f'-segwitheight={COINBASE_MATURITY + 5}',
'-addresstype=legacy',
- ]] * 2
+ ]]
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
diff --git a/test/functional/feature_presegwit_node_upgrade.py b/test/functional/feature_presegwit_node_upgrade.py
new file mode 100755
index 0000000000..0428588da3
--- /dev/null
+++ b/test/functional/feature_presegwit_node_upgrade.py
@@ -0,0 +1,52 @@
+#!/usr/bin/env python3
+# Copyright (c) 2017-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.
+"""Test a pre-segwit node upgrading to segwit consensus"""
+
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import (
+ assert_equal,
+ softfork_active,
+)
+
+class SegwitUpgradeTest(BitcoinTestFramework):
+ def set_test_params(self):
+ self.setup_clean_chain = True
+ self.num_nodes = 1
+ self.extra_args = [["-segwitheight=10"]]
+
+ def run_test(self):
+ """A pre-segwit node with insufficiently validated blocks needs to redownload blocks"""
+
+ self.log.info("Testing upgrade behaviour for pre-segwit node to segwit rules")
+ node = self.nodes[0]
+
+ # Node hasn't been used or connected yet
+ assert_equal(node.getblockcount(), 0)
+
+ assert not softfork_active(node, "segwit")
+
+ # Generate 8 blocks without witness data
+ node.generate(8)
+ assert_equal(node.getblockcount(), 8)
+
+ self.stop_node(0)
+ # Restarting the node (with segwit activation height set to 5) should result in a shutdown
+ # because the blockchain consists of 3 insufficiently validated blocks per segwit consensus rules.
+ node.assert_start_raises_init_error(
+ extra_args=["-segwitheight=5"],
+ expected_msg=": Witness data for blocks after height 5 requires validation. Please restart with -reindex..\nPlease restart with -reindex or -reindex-chainstate to recover.")
+
+ # As directed, the user restarts the node with -reindex
+ self.start_node(0, extra_args=["-reindex", "-segwitheight=5"])
+
+ # With the segwit consensus rules, the node is able to validate only up to block 4
+ assert_equal(node.getblockcount(), 4)
+
+ # The upgraded node should now have segwit activated
+ assert softfork_active(node, "segwit")
+
+
+if __name__ == '__main__':
+ SegwitUpgradeTest().main()
diff --git a/test/functional/feature_proxy.py b/test/functional/feature_proxy.py
index 8bee43b8ad..162814815e 100755
--- a/test/functional/feature_proxy.py
+++ b/test/functional/feature_proxy.py
@@ -147,13 +147,13 @@ class ProxyTest(BitcoinTestFramework):
self.network_test(node, addr, network=NET_IPV6)
if test_onion:
- addr = "bitcoinostk4e4re.onion:8333"
+ addr = "pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion:8333"
self.log.debug("Test: outgoing onion connection through node for address {}".format(addr))
node.addnode(addr, "onetry")
cmd = proxies[2].queue.get()
assert isinstance(cmd, Socks5Command)
assert_equal(cmd.atyp, AddressType.DOMAINNAME)
- assert_equal(cmd.addr, b"bitcoinostk4e4re.onion")
+ assert_equal(cmd.addr, b"pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion")
assert_equal(cmd.port, 8333)
if not auth:
assert_equal(cmd.username, None)
diff --git a/test/functional/feature_pruning.py b/test/functional/feature_pruning.py
index f09bffe2d4..cedb7b57ca 100755
--- a/test/functional/feature_pruning.py
+++ b/test/functional/feature_pruning.py
@@ -11,8 +11,12 @@ This test takes 30 mins or more (up to 2 hours)
import os
from test_framework.blocktools import create_coinbase
-from test_framework.messages import CBlock, ToHex
-from test_framework.script import CScript, OP_RETURN, OP_NOP
+from test_framework.messages import CBlock
+from test_framework.script import (
+ CScript,
+ OP_NOP,
+ OP_RETURN,
+)
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
@@ -62,7 +66,7 @@ def mine_large_blocks(node, n):
block.solve()
# Submit to the node
- node.submitblock(ToHex(block))
+ node.submitblock(block.serialize().hex())
previousblockhash = block.sha256
height += 1
diff --git a/test/functional/feature_rbf.py b/test/functional/feature_rbf.py
index 945880cc3b..ed944274e3 100755
--- a/test/functional/feature_rbf.py
+++ b/test/functional/feature_rbf.py
@@ -6,16 +6,23 @@
from decimal import Decimal
-from test_framework.messages import COIN, COutPoint, CTransaction, CTxIn, CTxOut
+from test_framework.blocktools import COINBASE_MATURITY
+from test_framework.messages import (
+ BIP125_SEQUENCE_NUMBER,
+ COIN,
+ COutPoint,
+ CTransaction,
+ CTxIn,
+ CTxOut,
+)
from test_framework.script import CScript, OP_DROP
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_equal, assert_raises_rpc_error, satoshi_round
from test_framework.script_util import DUMMY_P2WPKH_SCRIPT, DUMMY_2_P2WPKH_SCRIPT
+from test_framework.wallet import MiniWallet
MAX_REPLACEMENT_LIMIT = 100
-def txToHex(tx):
- return tx.serialize().hex()
def make_utxo(node, amount, confirmed=True, scriptPubKey=DUMMY_P2WPKH_SCRIPT):
"""Create a txout with a given amount and scriptPubKey
@@ -25,12 +32,12 @@ def make_utxo(node, amount, confirmed=True, scriptPubKey=DUMMY_P2WPKH_SCRIPT):
confirmed - txouts created will be confirmed in the blockchain;
unconfirmed otherwise.
"""
- fee = 1*COIN
- while node.getbalance() < satoshi_round((amount + fee)/COIN):
- node.generate(100)
+ fee = 1 * COIN
+ while node.getbalance() < satoshi_round((amount + fee) / COIN):
+ node.generate(COINBASE_MATURITY)
new_addr = node.getnewaddress()
- txid = node.sendtoaddress(new_addr, satoshi_round((amount+fee)/COIN))
+ txid = node.sendtoaddress(new_addr, satoshi_round((amount + fee) / COIN))
tx1 = node.getrawtransaction(txid, 1)
txid = int(txid, 16)
i, _ = next(filter(lambda vout: new_addr == vout[1]['scriptPubKey']['address'], enumerate(tx1['vout'])))
@@ -40,7 +47,7 @@ def make_utxo(node, amount, confirmed=True, scriptPubKey=DUMMY_P2WPKH_SCRIPT):
tx2.vout = [CTxOut(amount, scriptPubKey)]
tx2.rehash()
- signed_tx = node.signrawtransactionwithwallet(txToHex(tx2))
+ signed_tx = node.signrawtransactionwithwallet(tx2.serialize().hex())
txid = node.sendrawtransaction(signed_tx['hex'], 0)
@@ -77,10 +84,7 @@ class ReplaceByFeeTest(BitcoinTestFramework):
self.skip_if_no_wallet()
def run_test(self):
- # Leave IBD
- self.nodes[0].generate(1)
-
- make_utxo(self.nodes[0], 1*COIN)
+ make_utxo(self.nodes[0], 1 * COIN)
# Ensure nodes are synced
self.sync_all()
@@ -115,11 +119,17 @@ class ReplaceByFeeTest(BitcoinTestFramework):
self.log.info("Running test prioritised transactions...")
self.test_prioritised_transactions()
+ self.log.info("Running test no inherited signaling...")
+ self.test_no_inherited_signaling()
+
+ self.log.info("Running test replacement relay fee...")
+ self.test_replacement_relay_fee()
+
self.log.info("Passed")
def test_simple_doublespend(self):
"""Simple doublespend"""
- tx0_outpoint = make_utxo(self.nodes[0], int(1.1*COIN))
+ tx0_outpoint = make_utxo(self.nodes[0], int(1.1 * COIN))
# make_utxo may have generated a bunch of blocks, so we need to sync
# before we can spend the coins generated, or else the resulting
@@ -129,7 +139,7 @@ class ReplaceByFeeTest(BitcoinTestFramework):
tx1a = CTransaction()
tx1a.vin = [CTxIn(tx0_outpoint, nSequence=0)]
tx1a.vout = [CTxOut(1 * COIN, DUMMY_P2WPKH_SCRIPT)]
- tx1a_hex = txToHex(tx1a)
+ tx1a_hex = tx1a.serialize().hex()
tx1a_txid = self.nodes[0].sendrawtransaction(tx1a_hex, 0)
self.sync_all()
@@ -138,7 +148,7 @@ class ReplaceByFeeTest(BitcoinTestFramework):
tx1b = CTransaction()
tx1b.vin = [CTxIn(tx0_outpoint, nSequence=0)]
tx1b.vout = [CTxOut(1 * COIN, DUMMY_2_P2WPKH_SCRIPT)]
- tx1b_hex = txToHex(tx1b)
+ tx1b_hex = tx1b.serialize().hex()
# This will raise an exception due to insufficient fee
assert_raises_rpc_error(-26, "insufficient fee", self.nodes[0].sendrawtransaction, tx1b_hex, 0)
@@ -147,7 +157,7 @@ class ReplaceByFeeTest(BitcoinTestFramework):
tx1b = CTransaction()
tx1b.vin = [CTxIn(tx0_outpoint, nSequence=0)]
tx1b.vout = [CTxOut(int(0.9 * COIN), DUMMY_P2WPKH_SCRIPT)]
- tx1b_hex = txToHex(tx1b)
+ tx1b_hex = tx1b.serialize().hex()
# Works when enabled
tx1b_txid = self.nodes[0].sendrawtransaction(tx1b_hex, 0)
@@ -161,18 +171,18 @@ class ReplaceByFeeTest(BitcoinTestFramework):
def test_doublespend_chain(self):
"""Doublespend of a long chain"""
- initial_nValue = 50*COIN
+ initial_nValue = 50 * COIN
tx0_outpoint = make_utxo(self.nodes[0], initial_nValue)
prevout = tx0_outpoint
remaining_value = initial_nValue
chain_txids = []
- while remaining_value > 10*COIN:
- remaining_value -= 1*COIN
+ while remaining_value > 10 * COIN:
+ remaining_value -= 1 * COIN
tx = CTransaction()
tx.vin = [CTxIn(prevout, nSequence=0)]
tx.vout = [CTxOut(remaining_value, CScript([1, OP_DROP] * 15 + [1]))]
- tx_hex = txToHex(tx)
+ tx_hex = tx.serialize().hex()
txid = self.nodes[0].sendrawtransaction(tx_hex, 0)
chain_txids.append(txid)
prevout = COutPoint(int(txid, 16), 0)
@@ -182,7 +192,7 @@ class ReplaceByFeeTest(BitcoinTestFramework):
dbl_tx = CTransaction()
dbl_tx.vin = [CTxIn(tx0_outpoint, nSequence=0)]
dbl_tx.vout = [CTxOut(initial_nValue - 30 * COIN, DUMMY_P2WPKH_SCRIPT)]
- dbl_tx_hex = txToHex(dbl_tx)
+ dbl_tx_hex = dbl_tx.serialize().hex()
# This will raise an exception due to insufficient fee
assert_raises_rpc_error(-26, "insufficient fee", self.nodes[0].sendrawtransaction, dbl_tx_hex, 0)
@@ -191,7 +201,7 @@ class ReplaceByFeeTest(BitcoinTestFramework):
dbl_tx = CTransaction()
dbl_tx.vin = [CTxIn(tx0_outpoint, nSequence=0)]
dbl_tx.vout = [CTxOut(1 * COIN, DUMMY_P2WPKH_SCRIPT)]
- dbl_tx_hex = txToHex(dbl_tx)
+ dbl_tx_hex = dbl_tx.serialize().hex()
self.nodes[0].sendrawtransaction(dbl_tx_hex, 0)
mempool = self.nodes[0].getrawmempool()
@@ -201,10 +211,10 @@ class ReplaceByFeeTest(BitcoinTestFramework):
def test_doublespend_tree(self):
"""Doublespend of a big tree of transactions"""
- initial_nValue = 50*COIN
+ initial_nValue = 50 * COIN
tx0_outpoint = make_utxo(self.nodes[0], initial_nValue)
- def branch(prevout, initial_value, max_txs, tree_width=5, fee=0.0001*COIN, _total_txs=None):
+ def branch(prevout, initial_value, max_txs, tree_width=5, fee=0.0001 * COIN, _total_txs=None):
if _total_txs is None:
_total_txs = [0]
if _total_txs[0] >= max_txs:
@@ -219,7 +229,7 @@ class ReplaceByFeeTest(BitcoinTestFramework):
tx = CTransaction()
tx.vin = [CTxIn(prevout, nSequence=0)]
tx.vout = vout
- tx_hex = txToHex(tx)
+ tx_hex = tx.serialize().hex()
assert len(tx.serialize()) < 100000
txid = self.nodes[0].sendrawtransaction(tx_hex, 0)
@@ -235,7 +245,7 @@ class ReplaceByFeeTest(BitcoinTestFramework):
_total_txs=_total_txs):
yield x
- fee = int(0.0001*COIN)
+ fee = int(0.0001 * COIN)
n = MAX_REPLACEMENT_LIMIT
tree_txs = list(branch(tx0_outpoint, initial_nValue, n, fee=fee))
assert_equal(len(tree_txs), n)
@@ -244,7 +254,7 @@ class ReplaceByFeeTest(BitcoinTestFramework):
dbl_tx = CTransaction()
dbl_tx.vin = [CTxIn(tx0_outpoint, nSequence=0)]
dbl_tx.vout = [CTxOut(initial_nValue - fee * n, DUMMY_P2WPKH_SCRIPT)]
- dbl_tx_hex = txToHex(dbl_tx)
+ dbl_tx_hex = dbl_tx.serialize().hex()
# This will raise an exception due to insufficient fee
assert_raises_rpc_error(-26, "insufficient fee", self.nodes[0].sendrawtransaction, dbl_tx_hex, 0)
@@ -252,7 +262,7 @@ class ReplaceByFeeTest(BitcoinTestFramework):
dbl_tx = CTransaction()
dbl_tx.vin = [CTxIn(tx0_outpoint, nSequence=0)]
dbl_tx.vout = [CTxOut(initial_nValue - fee * n - 1 * COIN, DUMMY_P2WPKH_SCRIPT)]
- dbl_tx_hex = txToHex(dbl_tx)
+ dbl_tx_hex = dbl_tx.serialize().hex()
self.nodes[0].sendrawtransaction(dbl_tx_hex, 0)
mempool = self.nodes[0].getrawmempool()
@@ -263,8 +273,8 @@ class ReplaceByFeeTest(BitcoinTestFramework):
# Try again, but with more total transactions than the "max txs
# double-spent at once" anti-DoS limit.
- for n in (MAX_REPLACEMENT_LIMIT+1, MAX_REPLACEMENT_LIMIT*2):
- fee = int(0.0001*COIN)
+ for n in (MAX_REPLACEMENT_LIMIT + 1, MAX_REPLACEMENT_LIMIT * 2):
+ fee = int(0.0001 * COIN)
tx0_outpoint = make_utxo(self.nodes[0], initial_nValue)
tree_txs = list(branch(tx0_outpoint, initial_nValue, n, fee=fee))
assert_equal(len(tree_txs), n)
@@ -272,7 +282,7 @@ class ReplaceByFeeTest(BitcoinTestFramework):
dbl_tx = CTransaction()
dbl_tx.vin = [CTxIn(tx0_outpoint, nSequence=0)]
dbl_tx.vout = [CTxOut(initial_nValue - 2 * fee * n, DUMMY_P2WPKH_SCRIPT)]
- dbl_tx_hex = txToHex(dbl_tx)
+ dbl_tx_hex = dbl_tx.serialize().hex()
# This will raise an exception
assert_raises_rpc_error(-26, "too many potential replacements", self.nodes[0].sendrawtransaction, dbl_tx_hex, 0)
@@ -282,33 +292,33 @@ class ReplaceByFeeTest(BitcoinTestFramework):
def test_replacement_feeperkb(self):
"""Replacement requires fee-per-KB to be higher"""
- tx0_outpoint = make_utxo(self.nodes[0], int(1.1*COIN))
+ tx0_outpoint = make_utxo(self.nodes[0], int(1.1 * COIN))
tx1a = CTransaction()
tx1a.vin = [CTxIn(tx0_outpoint, nSequence=0)]
tx1a.vout = [CTxOut(1 * COIN, DUMMY_P2WPKH_SCRIPT)]
- tx1a_hex = txToHex(tx1a)
+ tx1a_hex = tx1a.serialize().hex()
self.nodes[0].sendrawtransaction(tx1a_hex, 0)
# Higher fee, but the fee per KB is much lower, so the replacement is
# rejected.
tx1b = CTransaction()
tx1b.vin = [CTxIn(tx0_outpoint, nSequence=0)]
- tx1b.vout = [CTxOut(int(0.001*COIN), CScript([b'a'*999000]))]
- tx1b_hex = txToHex(tx1b)
+ tx1b.vout = [CTxOut(int(0.001 * COIN), CScript([b'a' * 999000]))]
+ tx1b_hex = tx1b.serialize().hex()
# This will raise an exception due to insufficient fee
assert_raises_rpc_error(-26, "insufficient fee", self.nodes[0].sendrawtransaction, tx1b_hex, 0)
def test_spends_of_conflicting_outputs(self):
"""Replacements that spend conflicting tx outputs are rejected"""
- utxo1 = make_utxo(self.nodes[0], int(1.2*COIN))
- utxo2 = make_utxo(self.nodes[0], 3*COIN)
+ utxo1 = make_utxo(self.nodes[0], int(1.2 * COIN))
+ utxo2 = make_utxo(self.nodes[0], 3 * COIN)
tx1a = CTransaction()
tx1a.vin = [CTxIn(utxo1, nSequence=0)]
tx1a.vout = [CTxOut(int(1.1 * COIN), DUMMY_P2WPKH_SCRIPT)]
- tx1a_hex = txToHex(tx1a)
+ tx1a_hex = tx1a.serialize().hex()
tx1a_txid = self.nodes[0].sendrawtransaction(tx1a_hex, 0)
tx1a_txid = int(tx1a_txid, 16)
@@ -318,7 +328,7 @@ class ReplaceByFeeTest(BitcoinTestFramework):
tx2.vin = [CTxIn(utxo1, nSequence=0), CTxIn(utxo2, nSequence=0)]
tx2.vin.append(CTxIn(COutPoint(tx1a_txid, 0), nSequence=0))
tx2.vout = tx1a.vout
- tx2_hex = txToHex(tx2)
+ tx2_hex = tx2.serialize().hex()
# This will raise an exception
assert_raises_rpc_error(-26, "bad-txns-spends-conflicting-tx", self.nodes[0].sendrawtransaction, tx2_hex, 0)
@@ -327,7 +337,7 @@ class ReplaceByFeeTest(BitcoinTestFramework):
tx1b = CTransaction()
tx1b.vin = [CTxIn(COutPoint(tx1a_txid, 0), nSequence=0)]
tx1b.vout = [CTxOut(1 * COIN, DUMMY_P2WPKH_SCRIPT)]
- tx1b_hex = txToHex(tx1b)
+ tx1b_hex = tx1b.serialize().hex()
tx1b_txid = self.nodes[0].sendrawtransaction(tx1b_hex, 0)
tx1b_txid = int(tx1b_txid, 16)
@@ -335,26 +345,26 @@ class ReplaceByFeeTest(BitcoinTestFramework):
tx2.vin = [CTxIn(utxo1, nSequence=0), CTxIn(utxo2, nSequence=0),
CTxIn(COutPoint(tx1b_txid, 0))]
tx2.vout = tx1a.vout
- tx2_hex = txToHex(tx2)
+ tx2_hex = tx2.serialize().hex()
# This will raise an exception
assert_raises_rpc_error(-26, "bad-txns-spends-conflicting-tx", self.nodes[0].sendrawtransaction, tx2_hex, 0)
def test_new_unconfirmed_inputs(self):
"""Replacements that add new unconfirmed inputs are rejected"""
- confirmed_utxo = make_utxo(self.nodes[0], int(1.1*COIN))
- unconfirmed_utxo = make_utxo(self.nodes[0], int(0.1*COIN), False)
+ confirmed_utxo = make_utxo(self.nodes[0], int(1.1 * COIN))
+ unconfirmed_utxo = make_utxo(self.nodes[0], int(0.1 * COIN), False)
tx1 = CTransaction()
tx1.vin = [CTxIn(confirmed_utxo)]
tx1.vout = [CTxOut(1 * COIN, DUMMY_P2WPKH_SCRIPT)]
- tx1_hex = txToHex(tx1)
+ tx1_hex = tx1.serialize().hex()
self.nodes[0].sendrawtransaction(tx1_hex, 0)
tx2 = CTransaction()
tx2.vin = [CTxIn(confirmed_utxo), CTxIn(unconfirmed_utxo)]
tx2.vout = tx1.vout
- tx2_hex = txToHex(tx2)
+ tx2_hex = tx2.serialize().hex()
# This will raise an exception
assert_raises_rpc_error(-26, "replacement-adds-unconfirmed", self.nodes[0].sendrawtransaction, tx2_hex, 0)
@@ -365,42 +375,42 @@ class ReplaceByFeeTest(BitcoinTestFramework):
# transactions
# Start by creating a single transaction with many outputs
- initial_nValue = 10*COIN
+ initial_nValue = 10 * COIN
utxo = make_utxo(self.nodes[0], initial_nValue)
- fee = int(0.0001*COIN)
- split_value = int((initial_nValue-fee)/(MAX_REPLACEMENT_LIMIT+1))
+ fee = int(0.0001 * COIN)
+ split_value = int((initial_nValue - fee) / (MAX_REPLACEMENT_LIMIT + 1))
outputs = []
- for _ in range(MAX_REPLACEMENT_LIMIT+1):
+ for _ in range(MAX_REPLACEMENT_LIMIT + 1):
outputs.append(CTxOut(split_value, CScript([1])))
splitting_tx = CTransaction()
splitting_tx.vin = [CTxIn(utxo, nSequence=0)]
splitting_tx.vout = outputs
- splitting_tx_hex = txToHex(splitting_tx)
+ splitting_tx_hex = splitting_tx.serialize().hex()
txid = self.nodes[0].sendrawtransaction(splitting_tx_hex, 0)
txid = int(txid, 16)
# Now spend each of those outputs individually
- for i in range(MAX_REPLACEMENT_LIMIT+1):
+ for i in range(MAX_REPLACEMENT_LIMIT + 1):
tx_i = CTransaction()
tx_i.vin = [CTxIn(COutPoint(txid, i), nSequence=0)]
tx_i.vout = [CTxOut(split_value - fee, DUMMY_P2WPKH_SCRIPT)]
- tx_i_hex = txToHex(tx_i)
+ tx_i_hex = tx_i.serialize().hex()
self.nodes[0].sendrawtransaction(tx_i_hex, 0)
# Now create doublespend of the whole lot; should fail.
# Need a big enough fee to cover all spending transactions and have
# a higher fee rate
- double_spend_value = (split_value-100*fee)*(MAX_REPLACEMENT_LIMIT+1)
+ double_spend_value = (split_value - 100 * fee) * (MAX_REPLACEMENT_LIMIT + 1)
inputs = []
- for i in range(MAX_REPLACEMENT_LIMIT+1):
+ for i in range(MAX_REPLACEMENT_LIMIT + 1):
inputs.append(CTxIn(COutPoint(txid, i), nSequence=0))
double_tx = CTransaction()
double_tx.vin = inputs
double_tx.vout = [CTxOut(double_spend_value, CScript([b'a']))]
- double_tx_hex = txToHex(double_tx)
+ double_tx_hex = double_tx.serialize().hex()
# This will raise an exception
assert_raises_rpc_error(-26, "too many potential replacements", self.nodes[0].sendrawtransaction, double_tx_hex, 0)
@@ -409,18 +419,18 @@ class ReplaceByFeeTest(BitcoinTestFramework):
double_tx = CTransaction()
double_tx.vin = inputs[0:-1]
double_tx.vout = [CTxOut(double_spend_value, CScript([b'a']))]
- double_tx_hex = txToHex(double_tx)
+ double_tx_hex = double_tx.serialize().hex()
self.nodes[0].sendrawtransaction(double_tx_hex, 0)
def test_opt_in(self):
"""Replacing should only work if orig tx opted in"""
- tx0_outpoint = make_utxo(self.nodes[0], int(1.1*COIN))
+ tx0_outpoint = make_utxo(self.nodes[0], int(1.1 * COIN))
# Create a non-opting in transaction
tx1a = CTransaction()
tx1a.vin = [CTxIn(tx0_outpoint, nSequence=0xffffffff)]
tx1a.vout = [CTxOut(1 * COIN, DUMMY_P2WPKH_SCRIPT)]
- tx1a_hex = txToHex(tx1a)
+ tx1a_hex = tx1a.serialize().hex()
tx1a_txid = self.nodes[0].sendrawtransaction(tx1a_hex, 0)
# This transaction isn't shown as replaceable
@@ -430,25 +440,25 @@ class ReplaceByFeeTest(BitcoinTestFramework):
tx1b = CTransaction()
tx1b.vin = [CTxIn(tx0_outpoint, nSequence=0)]
tx1b.vout = [CTxOut(int(0.9 * COIN), DUMMY_P2WPKH_SCRIPT)]
- tx1b_hex = txToHex(tx1b)
+ tx1b_hex = tx1b.serialize().hex()
# This will raise an exception
assert_raises_rpc_error(-26, "txn-mempool-conflict", self.nodes[0].sendrawtransaction, tx1b_hex, 0)
- tx1_outpoint = make_utxo(self.nodes[0], int(1.1*COIN))
+ tx1_outpoint = make_utxo(self.nodes[0], int(1.1 * COIN))
# Create a different non-opting in transaction
tx2a = CTransaction()
tx2a.vin = [CTxIn(tx1_outpoint, nSequence=0xfffffffe)]
tx2a.vout = [CTxOut(1 * COIN, DUMMY_P2WPKH_SCRIPT)]
- tx2a_hex = txToHex(tx2a)
+ tx2a_hex = tx2a.serialize().hex()
tx2a_txid = self.nodes[0].sendrawtransaction(tx2a_hex, 0)
# Still shouldn't be able to double-spend
tx2b = CTransaction()
tx2b.vin = [CTxIn(tx1_outpoint, nSequence=0)]
tx2b.vout = [CTxOut(int(0.9 * COIN), DUMMY_P2WPKH_SCRIPT)]
- tx2b_hex = txToHex(tx2b)
+ tx2b_hex = tx2b.serialize().hex()
# This will raise an exception
assert_raises_rpc_error(-26, "txn-mempool-conflict", self.nodes[0].sendrawtransaction, tx2b_hex, 0)
@@ -463,8 +473,8 @@ class ReplaceByFeeTest(BitcoinTestFramework):
tx3a = CTransaction()
tx3a.vin = [CTxIn(COutPoint(tx1a_txid, 0), nSequence=0xffffffff),
CTxIn(COutPoint(tx2a_txid, 0), nSequence=0xfffffffd)]
- tx3a.vout = [CTxOut(int(0.9*COIN), CScript([b'c'])), CTxOut(int(0.9*COIN), CScript([b'd']))]
- tx3a_hex = txToHex(tx3a)
+ tx3a.vout = [CTxOut(int(0.9 * COIN), CScript([b'c'])), CTxOut(int(0.9 * COIN), CScript([b'd']))]
+ tx3a_hex = tx3a.serialize().hex()
tx3a_txid = self.nodes[0].sendrawtransaction(tx3a_hex, 0)
@@ -474,12 +484,12 @@ class ReplaceByFeeTest(BitcoinTestFramework):
tx3b = CTransaction()
tx3b.vin = [CTxIn(COutPoint(tx1a_txid, 0), nSequence=0)]
tx3b.vout = [CTxOut(int(0.5 * COIN), DUMMY_P2WPKH_SCRIPT)]
- tx3b_hex = txToHex(tx3b)
+ tx3b_hex = tx3b.serialize().hex()
tx3c = CTransaction()
tx3c.vin = [CTxIn(COutPoint(tx2a_txid, 0), nSequence=0)]
tx3c.vout = [CTxOut(int(0.5 * COIN), DUMMY_P2WPKH_SCRIPT)]
- tx3c_hex = txToHex(tx3c)
+ tx3c_hex = tx3c.serialize().hex()
self.nodes[0].sendrawtransaction(tx3b_hex, 0)
# If tx3b was accepted, tx3c won't look like a replacement,
@@ -491,25 +501,25 @@ class ReplaceByFeeTest(BitcoinTestFramework):
# correctly used by replacement logic
# 1. Check that feeperkb uses modified fees
- tx0_outpoint = make_utxo(self.nodes[0], int(1.1*COIN))
+ tx0_outpoint = make_utxo(self.nodes[0], int(1.1 * COIN))
tx1a = CTransaction()
tx1a.vin = [CTxIn(tx0_outpoint, nSequence=0)]
tx1a.vout = [CTxOut(1 * COIN, DUMMY_P2WPKH_SCRIPT)]
- tx1a_hex = txToHex(tx1a)
+ tx1a_hex = tx1a.serialize().hex()
tx1a_txid = self.nodes[0].sendrawtransaction(tx1a_hex, 0)
# Higher fee, but the actual fee per KB is much lower.
tx1b = CTransaction()
tx1b.vin = [CTxIn(tx0_outpoint, nSequence=0)]
- tx1b.vout = [CTxOut(int(0.001*COIN), CScript([b'a'*740000]))]
- tx1b_hex = txToHex(tx1b)
+ tx1b.vout = [CTxOut(int(0.001 * COIN), CScript([b'a' * 740000]))]
+ tx1b_hex = tx1b.serialize().hex()
# Verify tx1b cannot replace tx1a.
assert_raises_rpc_error(-26, "insufficient fee", self.nodes[0].sendrawtransaction, tx1b_hex, 0)
# Use prioritisetransaction to set tx1a's fee to 0.
- self.nodes[0].prioritisetransaction(txid=tx1a_txid, fee_delta=int(-0.1*COIN))
+ self.nodes[0].prioritisetransaction(txid=tx1a_txid, fee_delta=int(-0.1 * COIN))
# Now tx1b should be able to replace tx1a
tx1b_txid = self.nodes[0].sendrawtransaction(tx1b_hex, 0)
@@ -517,12 +527,12 @@ class ReplaceByFeeTest(BitcoinTestFramework):
assert tx1b_txid in self.nodes[0].getrawmempool()
# 2. Check that absolute fee checks use modified fee.
- tx1_outpoint = make_utxo(self.nodes[0], int(1.1*COIN))
+ tx1_outpoint = make_utxo(self.nodes[0], int(1.1 * COIN))
tx2a = CTransaction()
tx2a.vin = [CTxIn(tx1_outpoint, nSequence=0)]
tx2a.vout = [CTxOut(1 * COIN, DUMMY_P2WPKH_SCRIPT)]
- tx2a_hex = txToHex(tx2a)
+ tx2a_hex = tx2a.serialize().hex()
self.nodes[0].sendrawtransaction(tx2a_hex, 0)
# Lower fee, but we'll prioritise it
@@ -530,13 +540,13 @@ class ReplaceByFeeTest(BitcoinTestFramework):
tx2b.vin = [CTxIn(tx1_outpoint, nSequence=0)]
tx2b.vout = [CTxOut(int(1.01 * COIN), DUMMY_P2WPKH_SCRIPT)]
tx2b.rehash()
- tx2b_hex = txToHex(tx2b)
+ tx2b_hex = tx2b.serialize().hex()
# Verify tx2b cannot replace tx2a.
assert_raises_rpc_error(-26, "insufficient fee", self.nodes[0].sendrawtransaction, tx2b_hex, 0)
# Now prioritise tx2b to have a higher modified fee
- self.nodes[0].prioritisetransaction(txid=tx2b.hash, fee_delta=int(0.1*COIN))
+ self.nodes[0].prioritisetransaction(txid=tx2b.hash, fee_delta=int(0.1 * COIN))
# tx2b should now be accepted
tx2b_txid = self.nodes[0].sendrawtransaction(tx2b_hex, 0)
@@ -546,11 +556,11 @@ class ReplaceByFeeTest(BitcoinTestFramework):
def test_rpc(self):
us0 = self.nodes[0].listunspent()[0]
ins = [us0]
- outs = {self.nodes[0].getnewaddress() : Decimal(1.0000000)}
+ outs = {self.nodes[0].getnewaddress(): Decimal(1.0000000)}
rawtx0 = self.nodes[0].createrawtransaction(ins, outs, 0, True)
rawtx1 = self.nodes[0].createrawtransaction(ins, outs, 0, False)
- json0 = self.nodes[0].decoderawtransaction(rawtx0)
- json1 = self.nodes[0].decoderawtransaction(rawtx1)
+ json0 = self.nodes[0].decoderawtransaction(rawtx0)
+ json1 = self.nodes[0].decoderawtransaction(rawtx1)
assert_equal(json0["vin"][0]["sequence"], 4294967293)
assert_equal(json1["vin"][0]["sequence"], 4294967295)
@@ -558,10 +568,77 @@ class ReplaceByFeeTest(BitcoinTestFramework):
frawtx2a = self.nodes[0].fundrawtransaction(rawtx2, {"replaceable": True})
frawtx2b = self.nodes[0].fundrawtransaction(rawtx2, {"replaceable": False})
- json0 = self.nodes[0].decoderawtransaction(frawtx2a['hex'])
- json1 = self.nodes[0].decoderawtransaction(frawtx2b['hex'])
+ json0 = self.nodes[0].decoderawtransaction(frawtx2a['hex'])
+ json1 = self.nodes[0].decoderawtransaction(frawtx2b['hex'])
assert_equal(json0["vin"][0]["sequence"], 4294967293)
assert_equal(json1["vin"][0]["sequence"], 4294967294)
+ def test_no_inherited_signaling(self):
+ wallet = MiniWallet(self.nodes[0])
+ wallet.scan_blocks(start=76, num=1)
+ confirmed_utxo = wallet.get_utxo()
+
+ # Create an explicitly opt-in parent transaction
+ optin_parent_tx = wallet.send_self_transfer(
+ from_node=self.nodes[0],
+ utxo_to_spend=confirmed_utxo,
+ sequence=BIP125_SEQUENCE_NUMBER,
+ fee_rate=Decimal('0.01'),
+ )
+ assert_equal(True, self.nodes[0].getmempoolentry(optin_parent_tx['txid'])['bip125-replaceable'])
+
+ replacement_parent_tx = wallet.create_self_transfer(
+ from_node=self.nodes[0],
+ utxo_to_spend=confirmed_utxo,
+ sequence=BIP125_SEQUENCE_NUMBER,
+ fee_rate=Decimal('0.02'),
+ )
+
+ # Test if parent tx can be replaced.
+ res = self.nodes[0].testmempoolaccept(rawtxs=[replacement_parent_tx['hex']])[0]
+
+ # Parent can be replaced.
+ assert_equal(res['allowed'], True)
+
+ # Create an opt-out child tx spending the opt-in parent
+ parent_utxo = wallet.get_utxo(txid=optin_parent_tx['txid'])
+ optout_child_tx = wallet.send_self_transfer(
+ from_node=self.nodes[0],
+ utxo_to_spend=parent_utxo,
+ sequence=0xffffffff,
+ fee_rate=Decimal('0.01'),
+ )
+
+ # Reports true due to inheritance
+ assert_equal(True, self.nodes[0].getmempoolentry(optout_child_tx['txid'])['bip125-replaceable'])
+
+ replacement_child_tx = wallet.create_self_transfer(
+ from_node=self.nodes[0],
+ utxo_to_spend=parent_utxo,
+ sequence=0xffffffff,
+ fee_rate=Decimal('0.02'),
+ mempool_valid=False,
+ )
+
+ # Broadcast replacement child tx
+ # BIP 125 :
+ # 1. The original transactions signal replaceability explicitly or through inheritance as described in the above
+ # Summary section.
+ # The original transaction (`optout_child_tx`) doesn't signal RBF but its parent (`optin_parent_tx`) does.
+ # The replacement transaction (`replacement_child_tx`) should be able to replace the original transaction.
+ # See CVE-2021-31876 for further explanations.
+ assert_equal(True, self.nodes[0].getmempoolentry(optin_parent_tx['txid'])['bip125-replaceable'])
+ assert_raises_rpc_error(-26, 'txn-mempool-conflict', self.nodes[0].sendrawtransaction, replacement_child_tx["hex"], 0)
+
+ def test_replacement_relay_fee(self):
+ wallet = MiniWallet(self.nodes[0])
+ wallet.scan_blocks(start=77, num=1)
+ tx = wallet.send_self_transfer(from_node=self.nodes[0])['tx']
+
+ # Higher fee, higher feerate, different txid, but the replacement does not provide a relay
+ # fee conforming to node's `incrementalrelayfee` policy of 1000 sat per KB.
+ tx.vout[0].nValue -= 1
+ assert_raises_rpc_error(-26, "insufficient fee", self.nodes[0].sendrawtransaction, tx.serialize().hex())
+
if __name__ == '__main__':
ReplaceByFeeTest().main()
diff --git a/test/functional/feature_segwit.py b/test/functional/feature_segwit.py
index ad8767556b..9cf46d9d11 100755
--- a/test/functional/feature_segwit.py
+++ b/test/functional/feature_segwit.py
@@ -5,7 +5,6 @@
"""Test the SegWit changeover logic."""
from decimal import Decimal
-from io import BytesIO
from test_framework.address import (
key_to_p2pkh,
@@ -14,9 +13,34 @@ from test_framework.address import (
script_to_p2sh_p2wsh,
script_to_p2wsh,
)
-from test_framework.blocktools import witness_script, send_to_witness
-from test_framework.messages import COIN, COutPoint, CTransaction, CTxIn, CTxOut, FromHex, sha256, ToHex
-from test_framework.script import CScript, OP_HASH160, OP_CHECKSIG, OP_0, hash160, OP_EQUAL, OP_DUP, OP_EQUALVERIFY, OP_1, OP_2, OP_CHECKMULTISIG, OP_TRUE, OP_DROP
+from test_framework.blocktools import (
+ send_to_witness,
+ witness_script,
+)
+from test_framework.messages import (
+ COIN,
+ COutPoint,
+ CTransaction,
+ CTxIn,
+ CTxOut,
+ tx_from_hex,
+)
+from test_framework.script import (
+ CScript,
+ OP_0,
+ OP_1,
+ OP_2,
+ OP_CHECKMULTISIG,
+ OP_CHECKSIG,
+ OP_DROP,
+ OP_TRUE,
+)
+from test_framework.script_util import (
+ key_to_p2pkh_script,
+ key_to_p2wpkh_script,
+ script_to_p2sh_script,
+ script_to_p2wsh_script,
+)
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
@@ -179,7 +203,7 @@ class SegWitTest(BitcoinTestFramework):
assert self.nodes[1].getblock(blockhash, False) == self.nodes[2].getblock(blockhash, False)
for tx_id in segwit_tx_list:
- tx = FromHex(CTransaction(), self.nodes[2].gettransaction(tx_id)["hex"])
+ tx = tx_from_hex(self.nodes[2].gettransaction(tx_id)["hex"])
assert self.nodes[2].getrawtransaction(tx_id, False, blockhash) != self.nodes[0].getrawtransaction(tx_id, False, blockhash)
assert self.nodes[1].getrawtransaction(tx_id, False, blockhash) == self.nodes[2].getrawtransaction(tx_id, False, blockhash)
assert self.nodes[0].getrawtransaction(tx_id, False, blockhash) != self.nodes[2].gettransaction(tx_id)["hex"]
@@ -225,12 +249,12 @@ class SegWitTest(BitcoinTestFramework):
# tx1 is allowed to appear in the block, but no others.
txid1 = send_to_witness(1, self.nodes[0], find_spendable_utxo(self.nodes[0], 50), self.pubkey[0], False, Decimal("49.996"))
hex_tx = self.nodes[0].gettransaction(txid)['hex']
- tx = FromHex(CTransaction(), hex_tx)
+ tx = tx_from_hex(hex_tx)
assert tx.wit.is_null() # This should not be a segwit input
assert txid1 in self.nodes[0].getrawmempool()
tx1_hex = self.nodes[0].gettransaction(txid1)['hex']
- tx1 = FromHex(CTransaction(), tx1_hex)
+ tx1 = tx_from_hex(tx1_hex)
# Check that wtxid is properly reported in mempool entry (txid1)
assert_equal(int(self.nodes[0].getmempoolentry(txid1)["wtxid"], 16), tx1.calc_sha256(True))
@@ -243,9 +267,9 @@ class SegWitTest(BitcoinTestFramework):
tx = CTransaction()
tx.vin.append(CTxIn(COutPoint(int(txid1, 16), 0), b''))
tx.vout.append(CTxOut(int(49.99 * COIN), CScript([OP_TRUE, OP_DROP] * 15 + [OP_TRUE])))
- tx2_hex = self.nodes[0].signrawtransactionwithwallet(ToHex(tx))['hex']
+ tx2_hex = self.nodes[0].signrawtransactionwithwallet(tx.serialize().hex())['hex']
txid2 = self.nodes[0].sendrawtransaction(tx2_hex)
- tx = FromHex(CTransaction(), tx2_hex)
+ tx = tx_from_hex(tx2_hex)
assert not tx.wit.is_null()
# Check that wtxid is properly reported in mempool entry (txid2)
@@ -260,7 +284,7 @@ class SegWitTest(BitcoinTestFramework):
tx.vin.append(CTxIn(COutPoint(int(txid2, 16), 0), b""))
tx.vout.append(CTxOut(int(49.95 * COIN), CScript([OP_TRUE, OP_DROP] * 15 + [OP_TRUE]))) # Huge fee
tx.calc_sha256()
- txid3 = self.nodes[0].sendrawtransaction(hexstring=ToHex(tx), maxfeerate=0)
+ txid3 = self.nodes[0].sendrawtransaction(hexstring=tx.serialize().hex(), maxfeerate=0)
assert tx.wit.is_null()
assert txid3 in self.nodes[0].getrawmempool()
@@ -329,7 +353,7 @@ class SegWitTest(BitcoinTestFramework):
multisig_without_privkey_address = self.nodes[0].addmultisigaddress(2, [pubkeys[3], pubkeys[4]])['address']
script = CScript([OP_2, hex_str_to_bytes(pubkeys[3]), hex_str_to_bytes(pubkeys[4]), OP_2, OP_CHECKMULTISIG])
- solvable_after_importaddress.append(CScript([OP_HASH160, hash160(script), OP_EQUAL]))
+ solvable_after_importaddress.append(script_to_p2sh_script(script))
for i in compressed_spendable_address:
v = self.nodes[0].getaddressinfo(i)
@@ -403,10 +427,10 @@ class SegWitTest(BitcoinTestFramework):
op0 = CScript([OP_0])
# 2N7MGY19ti4KDMSzRfPAssP6Pxyuxoi6jLe is the P2SH(P2PKH) version of mjoE3sSrb8ByYEvgnC3Aox86u1CHnfJA4V
unsolvable_address_key = hex_str_to_bytes("02341AEC7587A51CDE5279E0630A531AEA2615A9F80B17E8D9376327BAEAA59E3D")
- unsolvablep2pkh = CScript([OP_DUP, OP_HASH160, hash160(unsolvable_address_key), OP_EQUALVERIFY, OP_CHECKSIG])
- unsolvablep2wshp2pkh = CScript([OP_0, sha256(unsolvablep2pkh)])
- p2shop0 = CScript([OP_HASH160, hash160(op0), OP_EQUAL])
- p2wshop1 = CScript([OP_0, sha256(op1)])
+ unsolvablep2pkh = key_to_p2pkh_script(unsolvable_address_key)
+ unsolvablep2wshp2pkh = script_to_p2wsh_script(unsolvablep2pkh)
+ p2shop0 = script_to_p2sh_script(op0)
+ p2wshop1 = script_to_p2wsh_script(op1)
unsolvable_after_importaddress.append(unsolvablep2pkh)
unsolvable_after_importaddress.append(unsolvablep2wshp2pkh)
unsolvable_after_importaddress.append(op1) # OP_1 will be imported as script
@@ -426,16 +450,16 @@ class SegWitTest(BitcoinTestFramework):
if (v['isscript']):
bare = hex_str_to_bytes(v['hex'])
importlist.append(bare.hex())
- importlist.append(CScript([OP_0, sha256(bare)]).hex())
+ importlist.append(script_to_p2wsh_script(bare).hex())
else:
pubkey = hex_str_to_bytes(v['pubkey'])
p2pk = CScript([pubkey, OP_CHECKSIG])
- p2pkh = CScript([OP_DUP, OP_HASH160, hash160(pubkey), OP_EQUALVERIFY, OP_CHECKSIG])
+ p2pkh = key_to_p2pkh_script(pubkey)
importlist.append(p2pk.hex())
importlist.append(p2pkh.hex())
- importlist.append(CScript([OP_0, hash160(pubkey)]).hex())
- importlist.append(CScript([OP_0, sha256(p2pk)]).hex())
- importlist.append(CScript([OP_0, sha256(p2pkh)]).hex())
+ importlist.append(key_to_p2wpkh_script(pubkey).hex())
+ importlist.append(script_to_p2wsh_script(p2pk).hex())
+ importlist.append(script_to_p2wsh_script(p2pkh).hex())
importlist.append(unsolvablep2pkh.hex())
importlist.append(unsolvablep2wshp2pkh.hex())
@@ -590,31 +614,29 @@ class SegWitTest(BitcoinTestFramework):
def p2sh_address_to_script(self, v):
bare = CScript(hex_str_to_bytes(v['hex']))
p2sh = CScript(hex_str_to_bytes(v['scriptPubKey']))
- p2wsh = CScript([OP_0, sha256(bare)])
- p2sh_p2wsh = CScript([OP_HASH160, hash160(p2wsh), OP_EQUAL])
+ p2wsh = script_to_p2wsh_script(bare)
+ p2sh_p2wsh = script_to_p2sh_script(p2wsh)
return([bare, p2sh, p2wsh, p2sh_p2wsh])
def p2pkh_address_to_script(self, v):
pubkey = hex_str_to_bytes(v['pubkey'])
- p2wpkh = CScript([OP_0, hash160(pubkey)])
- p2sh_p2wpkh = CScript([OP_HASH160, hash160(p2wpkh), OP_EQUAL])
+ p2wpkh = key_to_p2wpkh_script(pubkey)
+ p2sh_p2wpkh = script_to_p2sh_script(p2wpkh)
p2pk = CScript([pubkey, OP_CHECKSIG])
p2pkh = CScript(hex_str_to_bytes(v['scriptPubKey']))
- p2sh_p2pk = CScript([OP_HASH160, hash160(p2pk), OP_EQUAL])
- p2sh_p2pkh = CScript([OP_HASH160, hash160(p2pkh), OP_EQUAL])
- p2wsh_p2pk = CScript([OP_0, sha256(p2pk)])
- p2wsh_p2pkh = CScript([OP_0, sha256(p2pkh)])
- p2sh_p2wsh_p2pk = CScript([OP_HASH160, hash160(p2wsh_p2pk), OP_EQUAL])
- p2sh_p2wsh_p2pkh = CScript([OP_HASH160, hash160(p2wsh_p2pkh), OP_EQUAL])
+ p2sh_p2pk = script_to_p2sh_script(p2pk)
+ p2sh_p2pkh = script_to_p2sh_script(p2pkh)
+ p2wsh_p2pk = script_to_p2wsh_script(p2pk)
+ p2wsh_p2pkh = script_to_p2wsh_script(p2pkh)
+ p2sh_p2wsh_p2pk = script_to_p2sh_script(p2wsh_p2pk)
+ p2sh_p2wsh_p2pkh = script_to_p2sh_script(p2wsh_p2pkh)
return [p2wpkh, p2sh_p2wpkh, p2pk, p2pkh, p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh]
def create_and_mine_tx_from_txids(self, txids, success=True):
tx = CTransaction()
for i in txids:
- txtmp = CTransaction()
txraw = self.nodes[0].getrawtransaction(i, 0, txs_mined[i])
- f = BytesIO(hex_str_to_bytes(txraw))
- txtmp.deserialize(f)
+ txtmp = tx_from_hex(txraw)
for j in range(len(txtmp.vout)):
tx.vin.append(CTxIn(COutPoint(int('0x' + i, 0), j)))
tx.vout.append(CTxOut(0, CScript()))
diff --git a/test/functional/feature_taproot.py b/test/functional/feature_taproot.py
index 183a43abd4..f27ab2057c 100755
--- a/test/functional/feature_taproot.py
+++ b/test/functional/feature_taproot.py
@@ -5,6 +5,7 @@
# Test Taproot softfork (BIPs 340-342)
from test_framework.blocktools import (
+ COINBASE_MATURITY,
create_coinbase,
create_block,
add_witness_commitment,
@@ -18,7 +19,6 @@ from test_framework.messages import (
CTxIn,
CTxInWitness,
CTxOut,
- ToHex,
)
from test_framework.script import (
ANNEX_TAG,
@@ -57,7 +57,6 @@ from test_framework.script import (
OP_ENDIF,
OP_EQUAL,
OP_EQUALVERIFY,
- OP_HASH160,
OP_IF,
OP_NOP,
OP_NOT,
@@ -76,12 +75,17 @@ from test_framework.script import (
is_op_success,
taproot_construct,
)
+from test_framework.script_util import (
+ key_to_p2wpkh_script,
+ keyhash_to_p2pkh_script,
+ script_to_p2sh_script,
+ script_to_p2wsh_script,
+)
from test_framework.test_framework import BitcoinTestFramework
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,
- sha256,
)
from collections import OrderedDict, namedtuple
from io import BytesIO
@@ -458,13 +462,13 @@ def make_spender(comment, *, tap=None, witv0=False, script=None, pkh=None, p2sh=
# P2WPKH
assert script is None
pubkeyhash = hash160(pkh)
- spk = CScript([OP_0, pubkeyhash])
- conf["scriptcode"] = CScript([OP_DUP, OP_HASH160, pubkeyhash, OP_EQUALVERIFY, OP_CHECKSIG])
+ spk = key_to_p2wpkh_script(pkh)
+ conf["scriptcode"] = keyhash_to_p2pkh_script(pubkeyhash)
conf["script_witv0"] = None
conf["inputs"] = [getter("sign"), pkh]
elif script is not None:
# P2WSH
- spk = CScript([OP_0, sha256(script)])
+ spk = script_to_p2wsh_script(script)
conf["scriptcode"] = script
conf["script_witv0"] = script
else:
@@ -475,7 +479,7 @@ def make_spender(comment, *, tap=None, witv0=False, script=None, pkh=None, p2sh=
# P2PKH
assert script is None
pubkeyhash = hash160(pkh)
- spk = CScript([OP_DUP, OP_HASH160, pubkeyhash, OP_EQUALVERIFY, OP_CHECKSIG])
+ spk = keyhash_to_p2pkh_script(pubkeyhash)
conf["scriptcode"] = spk
conf["inputs"] = [getter("sign"), pkh]
elif script is not None:
@@ -496,7 +500,7 @@ def make_spender(comment, *, tap=None, witv0=False, script=None, pkh=None, p2sh=
if p2sh:
# P2SH wrapper can be combined with anything else
conf["script_p2sh"] = spk
- spk = CScript([OP_HASH160, hash160(spk), OP_EQUAL])
+ spk = script_to_p2sh_script(spk)
conf = {**conf, **kwargs}
@@ -518,9 +522,9 @@ def add_spender(spenders, *args, **kwargs):
def random_checksig_style(pubkey):
"""Creates a random CHECKSIG* tapscript that would succeed with only the valid signature on witness stack."""
opcode = random.choice([OP_CHECKSIG, OP_CHECKSIGVERIFY, OP_CHECKSIGADD])
- if (opcode == OP_CHECKSIGVERIFY):
+ if opcode == OP_CHECKSIGVERIFY:
ret = CScript([pubkey, opcode, OP_1])
- elif (opcode == OP_CHECKSIGADD):
+ elif opcode == OP_CHECKSIGADD:
num = random.choice([0, 0x7fffffff, -0x7fffffff])
ret = CScript([num, pubkey, opcode, num + 1, OP_EQUAL])
else:
@@ -1189,19 +1193,36 @@ def dump_json_test(tx, input_utxos, idx, success, failure):
# Data type to keep track of UTXOs, where they were created, and how to spend them.
UTXOData = namedtuple('UTXOData', 'outpoint,output,spender')
+
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.setup_clean_chain = True
# Node 0 has Taproot inactive, Node 1 active.
- self.extra_args = [["-par=1", "-vbparams=taproot:1:1"], ["-par=1"]]
+ 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()
def block_submit(self, node, txs, msg, err_msg, cb_pubkey=None, fees=0, sigops_weight=0, witness=False, accept=False):
@@ -1223,7 +1244,7 @@ class TaprootTest(BitcoinTestFramework):
block_response = node.submitblock(block.serialize().hex())
if err_msg is not None:
assert block_response is not None and err_msg in block_response, "Missing error message '%s' from block response '%s': %s" % (err_msg, "(None)" if block_response is None else block_response, msg)
- if (accept):
+ if accept:
assert node.getbestblockhash() == block.hash, "Failed to accept: %s (response: %s)" % (msg, block_response)
self.tip = block.sha256
self.lastblockhash = block.hash
@@ -1305,7 +1326,7 @@ class TaprootTest(BitcoinTestFramework):
# Add change
fund_tx.vout.append(CTxOut(balance - 10000, random.choice(host_spks)))
# Ask the wallet to sign
- ss = BytesIO(bytes.fromhex(node.signrawtransactionwithwallet(ToHex(fund_tx))["hex"]))
+ ss = BytesIO(bytes.fromhex(node.signrawtransactionwithwallet(fund_tx.serialize().hex())["hex"]))
fund_tx.deserialize(ss)
# Construct UTXOData entries
fund_tx.rehash()
@@ -1440,7 +1461,7 @@ class TaprootTest(BitcoinTestFramework):
def run_test(self):
# Post-taproot activation tests go first (pre-taproot tests' blocks are invalid post-taproot).
self.log.info("Post-activation tests...")
- self.nodes[1].generate(101)
+ self.nodes[1].generate(COINBASE_MATURITY + 1)
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
diff --git a/test/functional/feature_utxo_set_hash.py b/test/functional/feature_utxo_set_hash.py
index ce00faffee..afc0bdb8c5 100755
--- a/test/functional/feature_utxo_set_hash.py
+++ b/test/functional/feature_utxo_set_hash.py
@@ -9,7 +9,7 @@ import struct
from test_framework.messages import (
CBlock,
COutPoint,
- FromHex,
+ from_hex,
)
from test_framework.muhash import MuHash3072
from test_framework.test_framework import BitcoinTestFramework
@@ -32,13 +32,13 @@ class UTXOSetHashTest(BitcoinTestFramework):
# Generate 100 blocks and remove the first since we plan to spend its
# coinbase
block_hashes = wallet.generate(1) + node.generate(99)
- blocks = list(map(lambda block: FromHex(CBlock(), node.getblock(block, False)), block_hashes))
+ blocks = list(map(lambda block: from_hex(CBlock(), node.getblock(block, False)), block_hashes))
blocks.pop(0)
# Create a spending transaction and mine a block which includes it
txid = wallet.send_self_transfer(from_node=node)['txid']
tx_block = node.generateblock(output=wallet.get_address(), transactions=[txid])
- blocks.append(FromHex(CBlock(), node.getblock(tx_block['hash'], False)))
+ blocks.append(from_hex(CBlock(), node.getblock(tx_block['hash'], False)))
# Serialize the outputs that should be in the UTXO set and add them to
# a MuHash object
diff --git a/test/functional/feature_versionbits_warning.py b/test/functional/feature_versionbits_warning.py
index 062a7affb5..1c9e237d78 100755
--- a/test/functional/feature_versionbits_warning.py
+++ b/test/functional/feature_versionbits_warning.py
@@ -21,8 +21,8 @@ VB_TOP_BITS = 0x20000000
VB_UNKNOWN_BIT = 27 # Choose a bit unassigned to any deployment
VB_UNKNOWN_VERSION = VB_TOP_BITS | (1 << VB_UNKNOWN_BIT)
-WARN_UNKNOWN_RULES_ACTIVE = "unknown new rules activated (versionbit {})".format(VB_UNKNOWN_BIT)
-VB_PATTERN = re.compile("Warning: unknown new rules activated.*versionbit")
+WARN_UNKNOWN_RULES_ACTIVE = "Unknown new rules activated (versionbit {})".format(VB_UNKNOWN_BIT)
+VB_PATTERN = re.compile("Unknown new rules activated.*versionbit")
class VersionBitsWarningTest(BitcoinTestFramework):
def set_test_params(self):
diff --git a/test/functional/interface_bitcoin_cli.py b/test/functional/interface_bitcoin_cli.py
index b5ce18a48b..dfa448a1a8 100755
--- a/test/functional/interface_bitcoin_cli.py
+++ b/test/functional/interface_bitcoin_cli.py
@@ -5,18 +5,23 @@
"""Test bitcoin-cli"""
from decimal import Decimal
+import re
+
+from test_framework.blocktools import COINBASE_MATURITY
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
+ assert_greater_than_or_equal,
assert_raises_process_error,
assert_raises_rpc_error,
get_auth_cookie,
)
+import time
# The block reward of coinbaseoutput.nValue (50) BTC/block matures after
# COINBASE_MATURITY (100) blocks. Therefore, after mining 101 blocks we expect
# node 0 to have a balance of (BLOCKS - COINBASE_MATURITY) * 50 BTC/block.
-BLOCKS = 101
+BLOCKS = COINBASE_MATURITY + 1
BALANCE = (BLOCKS - 100) * 50
JSON_PARSING_ERROR = 'error: Error parsing JSON: foo'
@@ -25,6 +30,41 @@ TOO_MANY_ARGS = 'error: too many arguments (maximum 2 for nblocks and maxtries)'
WALLET_NOT_LOADED = 'Requested wallet does not exist or is not loaded'
WALLET_NOT_SPECIFIED = 'Wallet file not specified'
+
+def cli_get_info_string_to_dict(cli_get_info_string):
+ """Helper method to convert human-readable -getinfo into a dictionary"""
+ cli_get_info = {}
+ lines = cli_get_info_string.splitlines()
+ line_idx = 0
+ ansi_escape = re.compile(r'(\x9B|\x1B\[)[0-?]*[ -\/]*[@-~]')
+ while line_idx < len(lines):
+ # Remove ansi colour code
+ line = ansi_escape.sub('', lines[line_idx])
+ if "Balances" in line:
+ # When "Balances" appears in a line, all of the following lines contain "balance: wallet" until an empty line
+ cli_get_info["Balances"] = {}
+ while line_idx < len(lines) and not (lines[line_idx + 1] == ''):
+ line_idx += 1
+ balance, wallet = lines[line_idx].strip().split(" ")
+ # Remove right justification padding
+ wallet = wallet.strip()
+ if wallet == '""':
+ # Set default wallet("") to empty string
+ wallet = ''
+ cli_get_info["Balances"][wallet] = balance.strip()
+ elif ": " in line:
+ key, value = line.split(": ")
+ if key == 'Wallet' and value == '""':
+ # Set default wallet("") to empty string
+ value = ''
+ if key == "Proxy" and value == "N/A":
+ # Set N/A to empty string to represent no proxy
+ value = ''
+ cli_get_info[key.strip()] = value.strip()
+ line_idx += 1
+ return cli_get_info
+
+
class TestBitcoinCli(BitcoinTestFramework):
def set_test_params(self):
self.setup_clean_chain = True
@@ -63,37 +103,43 @@ class TestBitcoinCli(BitcoinTestFramework):
self.log.info("Test -getinfo with arguments fails")
assert_raises_process_error(1, "-getinfo takes no arguments", self.nodes[0].cli('-getinfo').help)
+ self.log.info("Test -getinfo with -color=never does not return ANSI escape codes")
+ assert "\u001b[0m" not in self.nodes[0].cli('-getinfo', '-color=never').send_cli()
+
+ self.log.info("Test -getinfo with -color=always returns ANSI escape codes")
+ assert "\u001b[0m" in self.nodes[0].cli('-getinfo', '-color=always').send_cli()
+
+ self.log.info("Test -getinfo with invalid value for -color option")
+ assert_raises_process_error(1, "Invalid value for -color option. Valid values: always, auto, never.", self.nodes[0].cli('-getinfo', '-color=foo').send_cli)
+
self.log.info("Test -getinfo returns expected network and blockchain info")
if self.is_wallet_compiled():
self.nodes[0].encryptwallet(password)
- cli_get_info = self.nodes[0].cli('-getinfo').send_cli()
+ cli_get_info_string = self.nodes[0].cli('-getinfo').send_cli()
+ cli_get_info = cli_get_info_string_to_dict(cli_get_info_string)
+
network_info = self.nodes[0].getnetworkinfo()
blockchain_info = self.nodes[0].getblockchaininfo()
- assert_equal(cli_get_info['version'], network_info['version'])
- assert_equal(cli_get_info['blocks'], blockchain_info['blocks'])
- assert_equal(cli_get_info['headers'], blockchain_info['headers'])
- assert_equal(cli_get_info['timeoffset'], network_info['timeoffset'])
- assert_equal(
- cli_get_info['connections'],
- {
- 'in': network_info['connections_in'],
- 'out': network_info['connections_out'],
- 'total': network_info['connections']
- }
- )
- assert_equal(cli_get_info['proxy'], network_info['networks'][0]['proxy'])
- assert_equal(cli_get_info['difficulty'], blockchain_info['difficulty'])
- assert_equal(cli_get_info['chain'], blockchain_info['chain'])
+ assert_equal(int(cli_get_info['Version']), network_info['version'])
+ assert_equal(cli_get_info['Verification progress'], "%.4f%%" % (blockchain_info['verificationprogress'] * 100))
+ assert_equal(int(cli_get_info['Blocks']), blockchain_info['blocks'])
+ assert_equal(int(cli_get_info['Headers']), blockchain_info['headers'])
+ assert_equal(int(cli_get_info['Time offset (s)']), network_info['timeoffset'])
+ expected_network_info = f"in {network_info['connections_in']}, out {network_info['connections_out']}, total {network_info['connections']}"
+ assert_equal(cli_get_info["Network"], expected_network_info)
+ assert_equal(cli_get_info['Proxy'], network_info['networks'][0]['proxy'])
+ assert_equal(Decimal(cli_get_info['Difficulty']), blockchain_info['difficulty'])
+ assert_equal(cli_get_info['Chain'], blockchain_info['chain'])
if self.is_wallet_compiled():
self.log.info("Test -getinfo and bitcoin-cli getwalletinfo return expected wallet info")
- assert_equal(cli_get_info['balance'], BALANCE)
- assert 'balances' not in cli_get_info.keys()
+ assert_equal(Decimal(cli_get_info['Balance']), BALANCE)
+ assert 'Balances' not in cli_get_info_string
wallet_info = self.nodes[0].getwalletinfo()
- assert_equal(cli_get_info['keypoolsize'], wallet_info['keypoolsize'])
- assert_equal(cli_get_info['unlocked_until'], wallet_info['unlocked_until'])
- assert_equal(cli_get_info['paytxfee'], wallet_info['paytxfee'])
- assert_equal(cli_get_info['relayfee'], network_info['relayfee'])
+ assert_equal(int(cli_get_info['Keypool size']), wallet_info['keypoolsize'])
+ assert_equal(int(cli_get_info['Unlocked until']), wallet_info['unlocked_until'])
+ assert_equal(Decimal(cli_get_info['Transaction fee rate (-paytxfee) (BTC/kvB)']), wallet_info['paytxfee'])
+ assert_equal(Decimal(cli_get_info['Min tx relay fee rate (BTC/kvB)']), network_info['relayfee'])
assert_equal(self.nodes[0].cli.getwalletinfo(), wallet_info)
# Setup to test -getinfo, -generate, and -rpcwallet= with multiple wallets.
@@ -116,44 +162,57 @@ class TestBitcoinCli(BitcoinTestFramework):
self.log.info("Test -getinfo with multiple wallets and -rpcwallet returns specified wallet balance")
for i in range(len(wallets)):
- cli_get_info = self.nodes[0].cli('-getinfo', '-rpcwallet={}'.format(wallets[i])).send_cli()
- assert 'balances' not in cli_get_info.keys()
- assert_equal(cli_get_info['balance'], amounts[i])
+ cli_get_info_string = self.nodes[0].cli('-getinfo', '-rpcwallet={}'.format(wallets[i])).send_cli()
+ cli_get_info = cli_get_info_string_to_dict(cli_get_info_string)
+ assert 'Balances' not in cli_get_info_string
+ assert_equal(cli_get_info["Wallet"], wallets[i])
+ assert_equal(Decimal(cli_get_info['Balance']), amounts[i])
self.log.info("Test -getinfo with multiple wallets and -rpcwallet=non-existing-wallet returns no balances")
- cli_get_info_keys = self.nodes[0].cli('-getinfo', '-rpcwallet=does-not-exist').send_cli().keys()
- assert 'balance' not in cli_get_info_keys
- assert 'balances' not in cli_get_info_keys
+ cli_get_info_string = self.nodes[0].cli('-getinfo', '-rpcwallet=does-not-exist').send_cli()
+ assert 'Balance' not in cli_get_info_string
+ assert 'Balances' not in cli_get_info_string
self.log.info("Test -getinfo with multiple wallets returns all loaded wallet names and balances")
assert_equal(set(self.nodes[0].listwallets()), set(wallets))
- cli_get_info = self.nodes[0].cli('-getinfo').send_cli()
- assert 'balance' not in cli_get_info.keys()
- assert_equal(cli_get_info['balances'], {k: v for k, v in zip(wallets, amounts)})
+ cli_get_info_string = self.nodes[0].cli('-getinfo').send_cli()
+ cli_get_info = cli_get_info_string_to_dict(cli_get_info_string)
+ assert 'Balance' not in cli_get_info
+ for k, v in zip(wallets, amounts):
+ assert_equal(Decimal(cli_get_info['Balances'][k]), v)
# Unload the default wallet and re-verify.
self.nodes[0].unloadwallet(wallets[0])
assert wallets[0] not in self.nodes[0].listwallets()
- cli_get_info = self.nodes[0].cli('-getinfo').send_cli()
- assert 'balance' not in cli_get_info.keys()
- assert_equal(cli_get_info['balances'], {k: v for k, v in zip(wallets[1:], amounts[1:])})
+ cli_get_info_string = self.nodes[0].cli('-getinfo').send_cli()
+ cli_get_info = cli_get_info_string_to_dict(cli_get_info_string)
+ assert 'Balance' not in cli_get_info
+ assert 'Balances' in cli_get_info_string
+ for k, v in zip(wallets[1:], amounts[1:]):
+ assert_equal(Decimal(cli_get_info['Balances'][k]), v)
+ assert wallets[0] not in cli_get_info
self.log.info("Test -getinfo after unloading all wallets except a non-default one returns its balance")
self.nodes[0].unloadwallet(wallets[2])
assert_equal(self.nodes[0].listwallets(), [wallets[1]])
- cli_get_info = self.nodes[0].cli('-getinfo').send_cli()
- assert 'balances' not in cli_get_info.keys()
- assert_equal(cli_get_info['balance'], amounts[1])
+ cli_get_info_string = self.nodes[0].cli('-getinfo').send_cli()
+ cli_get_info = cli_get_info_string_to_dict(cli_get_info_string)
+ assert 'Balances' not in cli_get_info_string
+ assert_equal(cli_get_info['Wallet'], wallets[1])
+ assert_equal(Decimal(cli_get_info['Balance']), amounts[1])
self.log.info("Test -getinfo with -rpcwallet=remaining-non-default-wallet returns only its balance")
- cli_get_info = self.nodes[0].cli('-getinfo', rpcwallet2).send_cli()
- assert 'balances' not in cli_get_info.keys()
- assert_equal(cli_get_info['balance'], amounts[1])
+ cli_get_info_string = self.nodes[0].cli('-getinfo', rpcwallet2).send_cli()
+ cli_get_info = cli_get_info_string_to_dict(cli_get_info_string)
+ assert 'Balances' not in cli_get_info_string
+ assert_equal(cli_get_info['Wallet'], wallets[1])
+ assert_equal(Decimal(cli_get_info['Balance']), amounts[1])
self.log.info("Test -getinfo with -rpcwallet=unloaded wallet returns no balances")
- cli_get_info_keys = self.nodes[0].cli('-getinfo', rpcwallet3).send_cli().keys()
- assert 'balance' not in cli_get_info_keys
- assert 'balances' not in cli_get_info_keys
+ cli_get_info_string = self.nodes[0].cli('-getinfo', rpcwallet3).send_cli()
+ cli_get_info_keys = cli_get_info_string_to_dict(cli_get_info_string)
+ assert 'Balance' not in cli_get_info_keys
+ assert 'Balances' not in cli_get_info_string
# Test bitcoin-cli -generate.
n1 = 3
@@ -246,6 +305,12 @@ class TestBitcoinCli(BitcoinTestFramework):
self.nodes[0].wait_for_rpc_connection()
assert_equal(blocks, BLOCKS + 25)
+ self.log.info("Test -rpcwait option waits at most -rpcwaittimeout seconds for startup")
+ self.stop_node(0) # stop the node so we time out
+ start_time = time.time()
+ assert_raises_process_error(1, "Could not connect to the server", self.nodes[0].cli('-rpcwait', '-rpcwaittimeout=5').echo)
+ assert_greater_than_or_equal(time.time(), start_time + 5)
+
if __name__ == '__main__':
TestBitcoinCli().main()
diff --git a/test/functional/interface_zmq.py b/test/functional/interface_zmq.py
index 94e162b748..15f352d68c 100755
--- a/test/functional/interface_zmq.py
+++ b/test/functional/interface_zmq.py
@@ -5,10 +5,21 @@
"""Test the ZMQ notification interface."""
import struct
-from test_framework.address import ADDRESS_BCRT1_UNSPENDABLE, ADDRESS_BCRT1_P2WSH_OP_TRUE
-from test_framework.blocktools import create_block, create_coinbase, add_witness_commitment
+from test_framework.address import (
+ ADDRESS_BCRT1_P2WSH_OP_TRUE,
+ ADDRESS_BCRT1_UNSPENDABLE,
+)
+from test_framework.blocktools import (
+ add_witness_commitment,
+ create_block,
+ create_coinbase,
+)
from test_framework.test_framework import BitcoinTestFramework
-from test_framework.messages import CTransaction, hash256, FromHex
+from test_framework.messages import (
+ CTransaction,
+ hash256,
+ tx_from_hex,
+)
from test_framework.util import (
assert_equal,
assert_raises_rpc_error,
@@ -393,10 +404,10 @@ class ZMQTest (BitcoinTestFramework):
bump_info = self.nodes[0].bumpfee(orig_txid)
# Mine the pre-bump tx
block = create_block(int(self.nodes[0].getbestblockhash(), 16), create_coinbase(self.nodes[0].getblockcount()+1))
- tx = FromHex(CTransaction(), raw_tx)
+ tx = tx_from_hex(raw_tx)
block.vtx.append(tx)
for txid in more_tx:
- tx = FromHex(CTransaction(), self.nodes[0].getrawtransaction(txid))
+ tx = tx_from_hex(self.nodes[0].getrawtransaction(txid))
block.vtx.append(tx)
add_witness_commitment(block)
block.solve()
diff --git a/test/functional/mempool_accept.py b/test/functional/mempool_accept.py
index c4002f524a..60c0953f6f 100755
--- a/test/functional/mempool_accept.py
+++ b/test/functional/mempool_accept.py
@@ -5,7 +5,6 @@
"""Test mempool acceptance of raw transactions."""
from decimal import Decimal
-from io import BytesIO
import math
from test_framework.test_framework import BitcoinTestFramework
@@ -14,26 +13,27 @@ from test_framework.messages import (
BIP125_SEQUENCE_NUMBER,
COIN,
COutPoint,
- CTransaction,
+ CTxIn,
CTxOut,
MAX_BLOCK_BASE_SIZE,
MAX_MONEY,
+ tx_from_hex,
)
from test_framework.script import (
- hash160,
CScript,
OP_0,
OP_2,
OP_3,
OP_CHECKMULTISIG,
- OP_EQUAL,
OP_HASH160,
OP_RETURN,
)
+from test_framework.script_util import (
+ script_to_p2sh_script,
+)
from test_framework.util import (
assert_equal,
assert_raises_rpc_error,
- hex_str_to_bytes,
)
@@ -67,7 +67,8 @@ class MempoolAcceptanceTest(BitcoinTestFramework):
self.log.info('Should not accept garbage to testmempoolaccept')
assert_raises_rpc_error(-3, 'Expected type array, got string', lambda: node.testmempoolaccept(rawtxs='ff00baar'))
- assert_raises_rpc_error(-8, 'Array must contain exactly one raw transaction for now', lambda: node.testmempoolaccept(rawtxs=['ff00baar', 'ff22']))
+ assert_raises_rpc_error(-8, 'Array must contain between 1 and 25 transactions.', lambda: node.testmempoolaccept(rawtxs=['ff22']*26))
+ assert_raises_rpc_error(-8, 'Array must contain between 1 and 25 transactions.', lambda: node.testmempoolaccept(rawtxs=[]))
assert_raises_rpc_error(-22, 'TX decode failed', lambda: node.testmempoolaccept(rawtxs=['ff00baar']))
self.log.info('A transaction already in the blockchain')
@@ -90,8 +91,7 @@ class MempoolAcceptanceTest(BitcoinTestFramework):
inputs=[{"txid": txid_in_block, "vout": 0, "sequence": BIP125_SEQUENCE_NUMBER}], # RBF is used later
outputs=[{node.getnewaddress(): Decimal('0.3') - fee}],
))['hex']
- tx = CTransaction()
- tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_0)))
+ tx = tx_from_hex(raw_tx_0)
txid_0 = tx.rehash()
self.check_mempool_result(
result_expected=[{'txid': txid_0, 'allowed': True, 'vsize': tx.get_vsize(), 'fees': {'base': fee}}],
@@ -106,7 +106,7 @@ class MempoolAcceptanceTest(BitcoinTestFramework):
outputs=[{node.getnewaddress(): output_amount}],
locktime=node.getblockcount() + 2000, # Can be anything
))['hex']
- tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_final)))
+ tx = tx_from_hex(raw_tx_final)
fee_expected = coin['amount'] - output_amount
self.check_mempool_result(
result_expected=[{'txid': tx.rehash(), 'allowed': True, 'vsize': tx.get_vsize(), 'fees': {'base': fee_expected}}],
@@ -125,11 +125,11 @@ class MempoolAcceptanceTest(BitcoinTestFramework):
)
self.log.info('A transaction that replaces a mempool transaction')
- tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_0)))
+ tx = tx_from_hex(raw_tx_0)
tx.vout[0].nValue -= int(fee * COIN) # Double the fee
tx.vin[0].nSequence = BIP125_SEQUENCE_NUMBER + 1 # Now, opt out of RBF
raw_tx_0 = node.signrawtransactionwithwallet(tx.serialize().hex())['hex']
- tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_0)))
+ tx = tx_from_hex(raw_tx_0)
txid_0 = tx.rehash()
self.check_mempool_result(
result_expected=[{'txid': txid_0, 'allowed': True, 'vsize': tx.get_vsize(), 'fees': {'base': (2 * fee)}}],
@@ -140,7 +140,7 @@ class MempoolAcceptanceTest(BitcoinTestFramework):
# Send the transaction that replaces the mempool transaction and opts out of replaceability
node.sendrawtransaction(hexstring=tx.serialize().hex(), maxfeerate=0)
# take original raw_tx_0
- tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_0)))
+ tx = tx_from_hex(raw_tx_0)
tx.vout[0].nValue -= int(4 * fee * COIN) # Set more fee
# skip re-signing the tx
self.check_mempool_result(
@@ -150,7 +150,7 @@ class MempoolAcceptanceTest(BitcoinTestFramework):
)
self.log.info('A transaction with missing inputs, that never existed')
- tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_0)))
+ tx = tx_from_hex(raw_tx_0)
tx.vin[0].prevout = COutPoint(hash=int('ff' * 32, 16), n=14)
# skip re-signing the tx
self.check_mempool_result(
@@ -159,7 +159,7 @@ class MempoolAcceptanceTest(BitcoinTestFramework):
)
self.log.info('A transaction with missing inputs, that existed once in the past')
- tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_0)))
+ tx = tx_from_hex(raw_tx_0)
tx.vin[0].prevout.n = 1 # Set vout to 1, to spend the other outpoint (49 coins) of the in-chain-tx we want to double spend
raw_tx_1 = node.signrawtransactionwithwallet(tx.serialize().hex())['hex']
txid_1 = node.sendrawtransaction(hexstring=raw_tx_1, maxfeerate=0)
@@ -189,7 +189,7 @@ class MempoolAcceptanceTest(BitcoinTestFramework):
inputs=[{'txid': txid_spend_both, 'vout': 0}],
outputs=[{node.getnewaddress(): 0.05}],
))['hex']
- tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference)))
+ tx = tx_from_hex(raw_tx_reference)
# Reference tx should be valid on itself
self.check_mempool_result(
result_expected=[{'txid': tx.rehash(), 'allowed': True, 'vsize': tx.get_vsize(), 'fees': { 'base': Decimal('0.1') - Decimal('0.05')}}],
@@ -198,17 +198,17 @@ class MempoolAcceptanceTest(BitcoinTestFramework):
)
self.log.info('A transaction with no outputs')
- tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference)))
+ tx = tx_from_hex(raw_tx_reference)
tx.vout = []
# Skip re-signing the transaction for context independent checks from now on
- # tx.deserialize(BytesIO(hex_str_to_bytes(node.signrawtransactionwithwallet(tx.serialize().hex())['hex'])))
+ # tx = tx_from_hex(node.signrawtransactionwithwallet(tx.serialize().hex())['hex'])
self.check_mempool_result(
result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': 'bad-txns-vout-empty'}],
rawtxs=[tx.serialize().hex()],
)
self.log.info('A really large transaction')
- tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference)))
+ tx = tx_from_hex(raw_tx_reference)
tx.vin = [tx.vin[0]] * math.ceil(MAX_BLOCK_BASE_SIZE / len(tx.vin[0].serialize()))
self.check_mempool_result(
result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': 'bad-txns-oversize'}],
@@ -216,7 +216,7 @@ class MempoolAcceptanceTest(BitcoinTestFramework):
)
self.log.info('A transaction with negative output value')
- tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference)))
+ tx = tx_from_hex(raw_tx_reference)
tx.vout[0].nValue *= -1
self.check_mempool_result(
result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': 'bad-txns-vout-negative'}],
@@ -225,7 +225,7 @@ class MempoolAcceptanceTest(BitcoinTestFramework):
# The following two validations prevent overflow of the output amounts (see CVE-2010-5139).
self.log.info('A transaction with too large output value')
- tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference)))
+ tx = tx_from_hex(raw_tx_reference)
tx.vout[0].nValue = MAX_MONEY + 1
self.check_mempool_result(
result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': 'bad-txns-vout-toolarge'}],
@@ -233,7 +233,7 @@ class MempoolAcceptanceTest(BitcoinTestFramework):
)
self.log.info('A transaction with too large sum of output values')
- tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference)))
+ tx = tx_from_hex(raw_tx_reference)
tx.vout = [tx.vout[0]] * 2
tx.vout[0].nValue = MAX_MONEY
self.check_mempool_result(
@@ -242,36 +242,44 @@ class MempoolAcceptanceTest(BitcoinTestFramework):
)
self.log.info('A transaction with duplicate inputs')
- tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference)))
+ tx = tx_from_hex(raw_tx_reference)
tx.vin = [tx.vin[0]] * 2
self.check_mempool_result(
result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': 'bad-txns-inputs-duplicate'}],
rawtxs=[tx.serialize().hex()],
)
+ self.log.info('A non-coinbase transaction with coinbase-like outpoint')
+ tx = tx_from_hex(raw_tx_reference)
+ tx.vin.append(CTxIn(COutPoint(hash=0, n=0xffffffff)))
+ self.check_mempool_result(
+ result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': 'bad-txns-prevout-null'}],
+ rawtxs=[tx.serialize().hex()],
+ )
+
self.log.info('A coinbase transaction')
# Pick the input of the first tx we signed, so it has to be a coinbase tx
raw_tx_coinbase_spent = node.getrawtransaction(txid=node.decoderawtransaction(hexstring=raw_tx_in_block)['vin'][0]['txid'])
- tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_coinbase_spent)))
+ tx = tx_from_hex(raw_tx_coinbase_spent)
self.check_mempool_result(
result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': 'coinbase'}],
rawtxs=[tx.serialize().hex()],
)
self.log.info('Some nonstandard transactions')
- tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference)))
+ tx = tx_from_hex(raw_tx_reference)
tx.nVersion = 3 # A version currently non-standard
self.check_mempool_result(
result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': 'version'}],
rawtxs=[tx.serialize().hex()],
)
- tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference)))
+ tx = tx_from_hex(raw_tx_reference)
tx.vout[0].scriptPubKey = CScript([OP_0]) # Some non-standard script
self.check_mempool_result(
result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': 'scriptpubkey'}],
rawtxs=[tx.serialize().hex()],
)
- tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference)))
+ tx = tx_from_hex(raw_tx_reference)
key = ECKey()
key.generate()
pubkey = key.get_pubkey().get_bytes()
@@ -280,34 +288,34 @@ class MempoolAcceptanceTest(BitcoinTestFramework):
result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': 'bare-multisig'}],
rawtxs=[tx.serialize().hex()],
)
- tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference)))
+ tx = tx_from_hex(raw_tx_reference)
tx.vin[0].scriptSig = CScript([OP_HASH160]) # Some not-pushonly scriptSig
self.check_mempool_result(
result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': 'scriptsig-not-pushonly'}],
rawtxs=[tx.serialize().hex()],
)
- tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference)))
+ tx = tx_from_hex(raw_tx_reference)
tx.vin[0].scriptSig = CScript([b'a' * 1648]) # Some too large scriptSig (>1650 bytes)
self.check_mempool_result(
result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': 'scriptsig-size'}],
rawtxs=[tx.serialize().hex()],
)
- tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference)))
- output_p2sh_burn = CTxOut(nValue=540, scriptPubKey=CScript([OP_HASH160, hash160(b'burn'), OP_EQUAL]))
+ tx = tx_from_hex(raw_tx_reference)
+ output_p2sh_burn = CTxOut(nValue=540, scriptPubKey=script_to_p2sh_script(b'burn'))
num_scripts = 100000 // len(output_p2sh_burn.serialize()) # Use enough outputs to make the tx too large for our policy
tx.vout = [output_p2sh_burn] * num_scripts
self.check_mempool_result(
result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': 'tx-size'}],
rawtxs=[tx.serialize().hex()],
)
- tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference)))
+ tx = tx_from_hex(raw_tx_reference)
tx.vout[0] = output_p2sh_burn
tx.vout[0].nValue -= 1 # Make output smaller, such that it is dust for our policy
self.check_mempool_result(
result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': 'dust'}],
rawtxs=[tx.serialize().hex()],
)
- tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference)))
+ tx = tx_from_hex(raw_tx_reference)
tx.vout[0].scriptPubKey = CScript([OP_RETURN, b'\xff'])
tx.vout = [tx.vout[0]] * 2
self.check_mempool_result(
@@ -316,7 +324,7 @@ class MempoolAcceptanceTest(BitcoinTestFramework):
)
self.log.info('A timelocked transaction')
- tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference)))
+ tx = tx_from_hex(raw_tx_reference)
tx.vin[0].nSequence -= 1 # Should be non-max, so locktime is not ignored
tx.nLockTime = node.getblockcount() + 1
self.check_mempool_result(
@@ -325,7 +333,7 @@ class MempoolAcceptanceTest(BitcoinTestFramework):
)
self.log.info('A transaction that is locked by BIP68 sequence logic')
- tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference)))
+ tx = tx_from_hex(raw_tx_reference)
tx.vin[0].nSequence = 2 # We could include it in the second block mined from now, but not the very next one
# Can skip re-signing the tx because of early rejection
self.check_mempool_result(
diff --git a/test/functional/mempool_accept_wtxid.py b/test/functional/mempool_accept_wtxid.py
new file mode 100755
index 0000000000..63ecc8ee2a
--- /dev/null
+++ b/test/functional/mempool_accept_wtxid.py
@@ -0,0 +1,131 @@
+#!/usr/bin/env python3
+# Copyright (c) 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 mempool acceptance in case of an already known transaction
+with identical non-witness data different witness.
+"""
+
+from test_framework.messages import (
+ COIN,
+ COutPoint,
+ CTransaction,
+ CTxIn,
+ CTxInWitness,
+ CTxOut,
+ sha256,
+)
+from test_framework.p2p import P2PTxInvStore
+from test_framework.script import (
+ CScript,
+ OP_0,
+ OP_ELSE,
+ OP_ENDIF,
+ OP_EQUAL,
+ OP_HASH160,
+ OP_IF,
+ OP_TRUE,
+ hash160,
+)
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import (
+ assert_equal,
+)
+
+class MempoolWtxidTest(BitcoinTestFramework):
+ def set_test_params(self):
+ self.num_nodes = 1
+ self.setup_clean_chain = True
+
+ def run_test(self):
+ node = self.nodes[0]
+
+ self.log.info('Start with empty mempool and 101 blocks')
+ # The last 100 coinbase transactions are premature
+ blockhash = node.generate(101)[0]
+ txid = node.getblock(blockhash=blockhash, verbosity=2)["tx"][0]["txid"]
+ assert_equal(node.getmempoolinfo()['size'], 0)
+
+ self.log.info("Submit parent with multiple script branches to mempool")
+ hashlock = hash160(b'Preimage')
+ witness_script = CScript([OP_IF, OP_HASH160, hashlock, OP_EQUAL, OP_ELSE, OP_TRUE, OP_ENDIF])
+ witness_program = sha256(witness_script)
+ script_pubkey = CScript([OP_0, witness_program])
+
+ parent = CTransaction()
+ parent.vin.append(CTxIn(COutPoint(int(txid, 16), 0), b""))
+ parent.vout.append(CTxOut(int(9.99998 * COIN), script_pubkey))
+ parent.rehash()
+
+ privkeys = [node.get_deterministic_priv_key().key]
+ raw_parent = node.signrawtransactionwithkey(hexstring=parent.serialize().hex(), privkeys=privkeys)['hex']
+ parent_txid = node.sendrawtransaction(hexstring=raw_parent, maxfeerate=0)
+ node.generate(1)
+
+ peer_wtxid_relay = node.add_p2p_connection(P2PTxInvStore())
+
+ # Create a new transaction with witness solving first branch
+ child_witness_script = CScript([OP_TRUE])
+ child_witness_program = sha256(child_witness_script)
+ child_script_pubkey = CScript([OP_0, child_witness_program])
+
+ child_one = CTransaction()
+ child_one.vin.append(CTxIn(COutPoint(int(parent_txid, 16), 0), b""))
+ child_one.vout.append(CTxOut(int(9.99996 * COIN), child_script_pubkey))
+ child_one.wit.vtxinwit.append(CTxInWitness())
+ child_one.wit.vtxinwit[0].scriptWitness.stack = [b'Preimage', b'\x01', witness_script]
+ child_one_wtxid = child_one.getwtxid()
+ child_one_txid = child_one.rehash()
+
+ # Create another identical transaction with witness solving second branch
+ child_two = CTransaction()
+ child_two.vin.append(CTxIn(COutPoint(int(parent_txid, 16), 0), b""))
+ child_two.vout.append(CTxOut(int(9.99996 * COIN), child_script_pubkey))
+ child_two.wit.vtxinwit.append(CTxInWitness())
+ child_two.wit.vtxinwit[0].scriptWitness.stack = [b'', witness_script]
+ child_two_wtxid = child_two.getwtxid()
+ child_two_txid = child_two.rehash()
+
+ assert_equal(child_one_txid, child_two_txid)
+ assert child_one_wtxid != child_two_wtxid
+
+ self.log.info("Submit child_one to the mempool")
+ txid_submitted = node.sendrawtransaction(child_one.serialize().hex())
+ assert_equal(node.getrawmempool(True)[txid_submitted]['wtxid'], child_one_wtxid)
+
+ peer_wtxid_relay.wait_for_broadcast([child_one_wtxid])
+ assert_equal(node.getmempoolinfo()["unbroadcastcount"], 0)
+
+ # testmempoolaccept reports the "already in mempool" error
+ assert_equal(node.testmempoolaccept([child_one.serialize().hex()]), [{
+ "txid": child_one_txid,
+ "wtxid": child_one_wtxid,
+ "allowed": False,
+ "reject-reason": "txn-already-in-mempool"
+ }])
+ testres_child_two = node.testmempoolaccept([child_two.serialize().hex()])[0]
+ assert_equal(testres_child_two, {
+ "txid": child_two_txid,
+ "wtxid": child_two_wtxid,
+ "allowed": False,
+ "reject-reason": "txn-same-nonwitness-data-in-mempool"
+ })
+
+ # sendrawtransaction will not throw but quits early when the exact same transaction is already in mempool
+ node.sendrawtransaction(child_one.serialize().hex())
+
+ self.log.info("Connect another peer that hasn't seen child_one before")
+ peer_wtxid_relay_2 = node.add_p2p_connection(P2PTxInvStore())
+
+ self.log.info("Submit child_two to the mempool")
+ # sendrawtransaction will not throw but quits early when a transaction with the same non-witness data is already in mempool
+ node.sendrawtransaction(child_two.serialize().hex())
+
+ # The node should rebroadcast the transaction using the wtxid of the correct transaction
+ # (child_one, which is in its mempool).
+ peer_wtxid_relay_2.wait_for_broadcast([child_one_wtxid])
+ assert_equal(node.getmempoolinfo()["unbroadcastcount"], 0)
+
+if __name__ == '__main__':
+ MempoolWtxidTest().main()
diff --git a/test/functional/mempool_compatibility.py b/test/functional/mempool_compatibility.py
index eb08765ebf..87f40b7f2b 100755
--- a/test/functional/mempool_compatibility.py
+++ b/test/functional/mempool_compatibility.py
@@ -7,14 +7,12 @@
NOTE: The test is designed to prevent cases when compatibility is broken accidentally.
In case we need to break mempool compatibility we can continue to use the test by just bumping the version number.
-Download node binaries:
-test/get_previous_releases.py -b v0.19.1 v0.18.1 v0.17.2 v0.16.3 v0.15.2
-
-Only v0.15.2 is required by this test. The rest is used in other backwards compatibility tests.
+The previous release v0.15.2 is required by this test, see test/README.md.
"""
import os
+from test_framework.blocktools import COINBASE_MATURITY
from test_framework.test_framework import BitcoinTestFramework
from test_framework.wallet import MiniWallet
@@ -41,7 +39,7 @@ class MempoolCompatibilityTest(BitcoinTestFramework):
old_node, new_node = self.nodes
new_wallet = MiniWallet(new_node)
new_wallet.generate(1)
- new_node.generate(100)
+ new_node.generate(COINBASE_MATURITY)
# Sync the nodes to ensure old_node has the block that contains the coinbase that new_wallet will spend.
# Otherwise, because coinbases are only valid in a block and not as loose txns, if the nodes aren't synced
# unbroadcasted_tx won't pass old_node's `MemPoolAccept::PreChecks`.
diff --git a/test/functional/mempool_expiry.py b/test/functional/mempool_expiry.py
index 4c46075ae9..7d1bfef333 100755
--- a/test/functional/mempool_expiry.py
+++ b/test/functional/mempool_expiry.py
@@ -12,6 +12,7 @@ definable expiry timeout via the '-mempoolexpiry=<n>' command line argument
from datetime import timedelta
+from test_framework.blocktools import COINBASE_MATURITY
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
@@ -36,7 +37,7 @@ class MempoolExpiryTest(BitcoinTestFramework):
# Add enough mature utxos to the wallet so that all txs spend confirmed coins.
self.wallet.generate(4)
- node.generate(100)
+ node.generate(COINBASE_MATURITY)
# Send a parent transaction that will expire.
parent_txid = self.wallet.send_self_transfer(from_node=node)['txid']
diff --git a/test/functional/mempool_package_onemore.py b/test/functional/mempool_package_onemore.py
index 884a2fef11..fcd8b061fa 100755
--- a/test/functional/mempool_package_onemore.py
+++ b/test/functional/mempool_package_onemore.py
@@ -9,8 +9,13 @@
from decimal import Decimal
+from test_framework.blocktools import COINBASE_MATURITY
from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import assert_equal, assert_raises_rpc_error, satoshi_round
+from test_framework.util import (
+ assert_equal,
+ assert_raises_rpc_error,
+ chain_transaction,
+)
MAX_ANCESTORS = 25
MAX_DESCENDANTS = 25
@@ -23,26 +28,9 @@ class MempoolPackagesTest(BitcoinTestFramework):
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
- # Build a transaction that spends parent_txid:vout
- # Return amount sent
- def chain_transaction(self, node, parent_txids, vouts, value, fee, num_outputs):
- send_value = satoshi_round((value - fee)/num_outputs)
- inputs = []
- for (txid, vout) in zip(parent_txids, vouts):
- inputs.append({'txid' : txid, 'vout' : vout})
- outputs = {}
- for _ in range(num_outputs):
- outputs[node.getnewaddress()] = send_value
- rawtx = node.createrawtransaction(inputs, outputs, 0, True)
- signedtx = node.signrawtransactionwithwallet(rawtx)
- txid = node.sendrawtransaction(signedtx['hex'])
- fulltx = node.getrawtransaction(txid, 1)
- assert len(fulltx['vout']) == num_outputs # make sure we didn't generate a change output
- return (txid, send_value)
-
def run_test(self):
# Mine some blocks and have them mature.
- self.nodes[0].generate(101)
+ self.nodes[0].generate(COINBASE_MATURITY + 1)
utxo = self.nodes[0].listunspent(10)
txid = utxo[0]['txid']
vout = utxo[0]['vout']
@@ -52,32 +40,32 @@ class MempoolPackagesTest(BitcoinTestFramework):
# MAX_ANCESTORS transactions off a confirmed tx should be fine
chain = []
for _ in range(4):
- (txid, sent_value) = self.chain_transaction(self.nodes[0], [txid], [vout], value, fee, 2)
+ (txid, sent_value) = chain_transaction(self.nodes[0], [txid], [vout], value, fee, 2)
vout = 0
value = sent_value
chain.append([txid, value])
for _ in range(MAX_ANCESTORS - 4):
- (txid, sent_value) = self.chain_transaction(self.nodes[0], [txid], [0], value, fee, 1)
+ (txid, sent_value) = chain_transaction(self.nodes[0], [txid], [0], value, fee, 1)
value = sent_value
chain.append([txid, value])
- (second_chain, second_chain_value) = self.chain_transaction(self.nodes[0], [utxo[1]['txid']], [utxo[1]['vout']], utxo[1]['amount'], fee, 1)
+ (second_chain, second_chain_value) = chain_transaction(self.nodes[0], [utxo[1]['txid']], [utxo[1]['vout']], utxo[1]['amount'], fee, 1)
# Check mempool has MAX_ANCESTORS + 1 transactions in it
assert_equal(len(self.nodes[0].getrawmempool(True)), MAX_ANCESTORS + 1)
# Adding one more transaction on to the chain should fail.
- assert_raises_rpc_error(-26, "too-long-mempool-chain, too many unconfirmed ancestors [limit: 25]", self.chain_transaction, self.nodes[0], [txid], [0], value, fee, 1)
+ assert_raises_rpc_error(-26, "too-long-mempool-chain, too many unconfirmed ancestors [limit: 25]", chain_transaction, self.nodes[0], [txid], [0], value, fee, 1)
# ...even if it chains on from some point in the middle of the chain.
- assert_raises_rpc_error(-26, "too-long-mempool-chain, too many descendants", self.chain_transaction, self.nodes[0], [chain[2][0]], [1], chain[2][1], fee, 1)
- assert_raises_rpc_error(-26, "too-long-mempool-chain, too many descendants", self.chain_transaction, self.nodes[0], [chain[1][0]], [1], chain[1][1], fee, 1)
+ assert_raises_rpc_error(-26, "too-long-mempool-chain, too many descendants", chain_transaction, self.nodes[0], [chain[2][0]], [1], chain[2][1], fee, 1)
+ assert_raises_rpc_error(-26, "too-long-mempool-chain, too many descendants", chain_transaction, self.nodes[0], [chain[1][0]], [1], chain[1][1], fee, 1)
# ...even if it chains on to two parent transactions with one in the chain.
- assert_raises_rpc_error(-26, "too-long-mempool-chain, too many descendants", self.chain_transaction, self.nodes[0], [chain[0][0], second_chain], [1, 0], chain[0][1] + second_chain_value, fee, 1)
+ assert_raises_rpc_error(-26, "too-long-mempool-chain, too many descendants", chain_transaction, self.nodes[0], [chain[0][0], second_chain], [1, 0], chain[0][1] + second_chain_value, fee, 1)
# ...especially if its > 40k weight
- assert_raises_rpc_error(-26, "too-long-mempool-chain, too many descendants", self.chain_transaction, self.nodes[0], [chain[0][0]], [1], chain[0][1], fee, 350)
+ assert_raises_rpc_error(-26, "too-long-mempool-chain, too many descendants", chain_transaction, self.nodes[0], [chain[0][0]], [1], chain[0][1], fee, 350)
# But not if it chains directly off the first transaction
- (replacable_txid, replacable_orig_value) = self.chain_transaction(self.nodes[0], [chain[0][0]], [1], chain[0][1], fee, 1)
+ (replacable_txid, replacable_orig_value) = chain_transaction(self.nodes[0], [chain[0][0]], [1], chain[0][1], fee, 1)
# and the second chain should work just fine
- self.chain_transaction(self.nodes[0], [second_chain], [0], second_chain_value, fee, 1)
+ chain_transaction(self.nodes[0], [second_chain], [0], second_chain_value, fee, 1)
# Make sure we can RBF the chain which used our carve-out rule
second_tx_outputs = {self.nodes[0].getrawtransaction(replacable_txid, True)["vout"][0]['scriptPubKey']['address']: replacable_orig_value - (Decimal(1) / Decimal(100))}
diff --git a/test/functional/mempool_packages.py b/test/functional/mempool_packages.py
index 461f9237ff..5fc3ec23ae 100755
--- a/test/functional/mempool_packages.py
+++ b/test/functional/mempool_packages.py
@@ -6,12 +6,14 @@
from decimal import Decimal
+from test_framework.blocktools import COINBASE_MATURITY
from test_framework.messages import COIN
from test_framework.p2p import P2PTxInvStore
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
assert_raises_rpc_error,
+ chain_transaction,
satoshi_round,
)
@@ -41,25 +43,10 @@ class MempoolPackagesTest(BitcoinTestFramework):
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
- # Build a transaction that spends parent_txid:vout
- # Return amount sent
- def chain_transaction(self, node, parent_txid, vout, value, fee, num_outputs):
- send_value = satoshi_round((value - fee)/num_outputs)
- inputs = [ {'txid' : parent_txid, 'vout' : vout} ]
- outputs = {}
- for _ in range(num_outputs):
- outputs[node.getnewaddress()] = send_value
- rawtx = node.createrawtransaction(inputs, outputs)
- signedtx = node.signrawtransactionwithwallet(rawtx)
- txid = node.sendrawtransaction(signedtx['hex'])
- fulltx = node.getrawtransaction(txid, 1)
- assert len(fulltx['vout']) == num_outputs # make sure we didn't generate a change output
- return (txid, send_value)
-
def run_test(self):
# Mine some blocks and have them mature.
peer_inv_store = self.nodes[0].add_p2p_connection(P2PTxInvStore()) # keep track of invs
- self.nodes[0].generate(101)
+ self.nodes[0].generate(COINBASE_MATURITY + 1)
utxo = self.nodes[0].listunspent(10)
txid = utxo[0]['txid']
vout = utxo[0]['vout']
@@ -70,7 +57,7 @@ class MempoolPackagesTest(BitcoinTestFramework):
chain = []
witness_chain = []
for _ in range(MAX_ANCESTORS):
- (txid, sent_value) = self.chain_transaction(self.nodes[0], txid, 0, value, fee, 1)
+ (txid, sent_value) = chain_transaction(self.nodes[0], [txid], [0], value, fee, 1)
value = sent_value
chain.append(txid)
# We need the wtxids to check P2P announcements
@@ -188,7 +175,7 @@ class MempoolPackagesTest(BitcoinTestFramework):
assert_equal(mempool[x]['descendantfees'], descendant_fees * COIN + 1000)
# Adding one more transaction on to the chain should fail.
- assert_raises_rpc_error(-26, "too-long-mempool-chain", self.chain_transaction, self.nodes[0], txid, vout, value, fee, 1)
+ assert_raises_rpc_error(-26, "too-long-mempool-chain", chain_transaction, self.nodes[0], [txid], [vout], value, fee, 1)
# Check that prioritising a tx before it's added to the mempool works
# First clear the mempool by mining a block.
@@ -237,7 +224,7 @@ class MempoolPackagesTest(BitcoinTestFramework):
transaction_package = []
tx_children = []
# First create one parent tx with 10 children
- (txid, sent_value) = self.chain_transaction(self.nodes[0], txid, vout, value, fee, 10)
+ (txid, sent_value) = chain_transaction(self.nodes[0], [txid], [vout], value, fee, 10)
parent_transaction = txid
for i in range(10):
transaction_package.append({'txid': txid, 'vout': i, 'amount': sent_value})
@@ -246,7 +233,7 @@ class MempoolPackagesTest(BitcoinTestFramework):
chain = [] # save sent txs for the purpose of checking node1's mempool later (see below)
for _ in range(MAX_DESCENDANTS - 1):
utxo = transaction_package.pop(0)
- (txid, sent_value) = self.chain_transaction(self.nodes[0], utxo['txid'], utxo['vout'], utxo['amount'], fee, 10)
+ (txid, sent_value) = chain_transaction(self.nodes[0], [utxo['txid']], [utxo['vout']], utxo['amount'], fee, 10)
chain.append(txid)
if utxo['txid'] is parent_transaction:
tx_children.append(txid)
@@ -262,7 +249,7 @@ class MempoolPackagesTest(BitcoinTestFramework):
# Sending one more chained transaction will fail
utxo = transaction_package.pop(0)
- assert_raises_rpc_error(-26, "too-long-mempool-chain", self.chain_transaction, self.nodes[0], utxo['txid'], utxo['vout'], utxo['amount'], fee, 10)
+ assert_raises_rpc_error(-26, "too-long-mempool-chain", chain_transaction, self.nodes[0], [utxo['txid']], [utxo['vout']], utxo['amount'], fee, 10)
# Check that node1's mempool is as expected, containing:
# - txs from previous ancestor test (-> custom ancestor limit)
@@ -320,13 +307,13 @@ class MempoolPackagesTest(BitcoinTestFramework):
value = send_value
# Create tx1
- tx1_id, _ = self.chain_transaction(self.nodes[0], tx0_id, 0, value, fee, 1)
+ tx1_id, _ = chain_transaction(self.nodes[0], [tx0_id], [0], value, fee, 1)
# Create tx2-7
vout = 1
txid = tx0_id
for _ in range(6):
- (txid, sent_value) = self.chain_transaction(self.nodes[0], txid, vout, value, fee, 1)
+ (txid, sent_value) = chain_transaction(self.nodes[0], [txid], [vout], value, fee, 1)
vout = 0
value = sent_value
diff --git a/test/functional/mempool_reorg.py b/test/functional/mempool_reorg.py
index 8e1f87e42c..bcc6aa7bcc 100755
--- a/test/functional/mempool_reorg.py
+++ b/test/functional/mempool_reorg.py
@@ -8,10 +8,9 @@ Test re-org scenarios with a mempool that contains transactions
that spend (directly or indirectly) coinbase transactions.
"""
-from test_framework.blocktools import create_raw_transaction
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_equal, assert_raises_rpc_error
-
+from test_framework.wallet import MiniWallet
class MempoolCoinbaseTest(BitcoinTestFramework):
def set_test_params(self):
@@ -23,86 +22,90 @@ class MempoolCoinbaseTest(BitcoinTestFramework):
[]
]
- def skip_test_if_missing_module(self):
- self.skip_if_no_wallet()
-
def run_test(self):
+ wallet = MiniWallet(self.nodes[0])
+
# Start with a 200 block chain
assert_equal(self.nodes[0].getblockcount(), 200)
- # Mine four blocks. After this, nodes[0] blocks
- # 101, 102, and 103 are spend-able.
- new_blocks = self.nodes[1].generate(4)
- self.sync_all()
-
- node0_address = self.nodes[0].getnewaddress()
- node1_address = self.nodes[1].getnewaddress()
+ self.log.info("Add 4 coinbase utxos to the miniwallet")
+ # Block 76 contains the first spendable coinbase txs.
+ first_block = 76
+ wallet.scan_blocks(start=first_block, num=4)
# Three scenarios for re-orging coinbase spends in the memory pool:
- # 1. Direct coinbase spend : spend_101
- # 2. Indirect (coinbase spend in chain, child in mempool) : spend_102 and spend_102_1
- # 3. Indirect (coinbase and child both in chain) : spend_103 and spend_103_1
- # Use invalidatblock to make all of the above coinbase spends invalid (immature coinbase),
+ # 1. Direct coinbase spend : spend_1
+ # 2. Indirect (coinbase spend in chain, child in mempool) : spend_2 and spend_2_1
+ # 3. Indirect (coinbase and child both in chain) : spend_3 and spend_3_1
+ # Use invalidateblock to make all of the above coinbase spends invalid (immature coinbase),
# and make sure the mempool code behaves correctly.
- b = [self.nodes[0].getblockhash(n) for n in range(101, 105)]
+ b = [self.nodes[0].getblockhash(n) for n in range(first_block, first_block+4)]
coinbase_txids = [self.nodes[0].getblock(h)['tx'][0] for h in b]
- spend_101_raw = create_raw_transaction(self.nodes[0], coinbase_txids[1], node1_address, amount=49.99)
- spend_102_raw = create_raw_transaction(self.nodes[0], coinbase_txids[2], node0_address, amount=49.99)
- spend_103_raw = create_raw_transaction(self.nodes[0], coinbase_txids[3], node0_address, amount=49.99)
-
- # Create a transaction which is time-locked to two blocks in the future
- timelock_tx = self.nodes[0].createrawtransaction(
- inputs=[{
- "txid": coinbase_txids[0],
- "vout": 0,
- }],
- outputs={node0_address: 49.99},
- locktime=self.nodes[0].getblockcount() + 2,
- )
- timelock_tx = self.nodes[0].signrawtransactionwithwallet(timelock_tx)["hex"]
- # This will raise an exception because the timelock transaction is too immature to spend
+ utxo_1 = wallet.get_utxo(txid=coinbase_txids[1])
+ utxo_2 = wallet.get_utxo(txid=coinbase_txids[2])
+ utxo_3 = wallet.get_utxo(txid=coinbase_txids[3])
+ self.log.info("Create three transactions spending from coinbase utxos: spend_1, spend_2, spend_3")
+ spend_1 = wallet.create_self_transfer(from_node=self.nodes[0], utxo_to_spend=utxo_1)
+ spend_2 = wallet.create_self_transfer(from_node=self.nodes[0], utxo_to_spend=utxo_2)
+ spend_3 = wallet.create_self_transfer(from_node=self.nodes[0], utxo_to_spend=utxo_3)
+
+ self.log.info("Create another transaction which is time-locked to two blocks in the future")
+ utxo = wallet.get_utxo(txid=coinbase_txids[0])
+ timelock_tx = wallet.create_self_transfer(
+ from_node=self.nodes[0],
+ utxo_to_spend=utxo,
+ mempool_valid=False,
+ locktime=self.nodes[0].getblockcount() + 2
+ )['hex']
+
+ self.log.info("Check that the time-locked transaction is too immature to spend")
assert_raises_rpc_error(-26, "non-final", self.nodes[0].sendrawtransaction, timelock_tx)
- # Broadcast and mine spend_102 and 103:
- spend_102_id = self.nodes[0].sendrawtransaction(spend_102_raw)
- spend_103_id = self.nodes[0].sendrawtransaction(spend_103_raw)
+ self.log.info("Broadcast and mine spend_2 and spend_3")
+ wallet.sendrawtransaction(from_node=self.nodes[0], tx_hex=spend_2['hex'])
+ wallet.sendrawtransaction(from_node=self.nodes[0], tx_hex=spend_3['hex'])
+ self.log.info("Generate a block")
self.nodes[0].generate(1)
- # Time-locked transaction is still too immature to spend
+ self.log.info("Check that time-locked transaction is still too immature to spend")
assert_raises_rpc_error(-26, 'non-final', self.nodes[0].sendrawtransaction, timelock_tx)
- # Create 102_1 and 103_1:
- spend_102_1_raw = create_raw_transaction(self.nodes[0], spend_102_id, node1_address, amount=49.98)
- spend_103_1_raw = create_raw_transaction(self.nodes[0], spend_103_id, node1_address, amount=49.98)
+ self.log.info("Create spend_2_1 and spend_3_1")
+ spend_2_utxo = wallet.get_utxo(txid=spend_2['txid'])
+ spend_2_1 = wallet.create_self_transfer(from_node=self.nodes[0], utxo_to_spend=spend_2_utxo)
+ spend_3_utxo = wallet.get_utxo(txid=spend_3['txid'])
+ spend_3_1 = wallet.create_self_transfer(from_node=self.nodes[0], utxo_to_spend=spend_3_utxo)
- # Broadcast and mine 103_1:
- spend_103_1_id = self.nodes[0].sendrawtransaction(spend_103_1_raw)
+ self.log.info("Broadcast and mine spend_3_1")
+ spend_3_1_id = self.nodes[0].sendrawtransaction(spend_3_1['hex'])
+ self.log.info("Generate a block")
last_block = self.nodes[0].generate(1)
# Sync blocks, so that peer 1 gets the block before timelock_tx
# Otherwise, peer 1 would put the timelock_tx in recentRejects
self.sync_all()
- # Time-locked transaction can now be spent
+ self.log.info("The time-locked transaction can now be spent")
timelock_tx_id = self.nodes[0].sendrawtransaction(timelock_tx)
- # ... now put spend_101 and spend_102_1 in memory pools:
- spend_101_id = self.nodes[0].sendrawtransaction(spend_101_raw)
- spend_102_1_id = self.nodes[0].sendrawtransaction(spend_102_1_raw)
+ self.log.info("Add spend_1 and spend_2_1 to the mempool")
+ spend_1_id = self.nodes[0].sendrawtransaction(spend_1['hex'])
+ spend_2_1_id = self.nodes[0].sendrawtransaction(spend_2_1['hex'])
- assert_equal(set(self.nodes[0].getrawmempool()), {spend_101_id, spend_102_1_id, timelock_tx_id})
+ assert_equal(set(self.nodes[0].getrawmempool()), {spend_1_id, spend_2_1_id, timelock_tx_id})
self.sync_all()
+ self.log.info("invalidate the last block")
for node in self.nodes:
node.invalidateblock(last_block[0])
- # Time-locked transaction is now too immature and has been removed from the mempool
- # spend_103_1 has been re-orged out of the chain and is back in the mempool
- assert_equal(set(self.nodes[0].getrawmempool()), {spend_101_id, spend_102_1_id, spend_103_1_id})
+ self.log.info("The time-locked transaction is now too immature and has been removed from the mempool")
+ self.log.info("spend_3_1 has been re-orged out of the chain and is back in the mempool")
+ assert_equal(set(self.nodes[0].getrawmempool()), {spend_1_id, spend_2_1_id, spend_3_1_id})
- # Use invalidateblock to re-org back and make all those coinbase spends
- # immature/invalid:
+ self.log.info("Use invalidateblock to re-org back and make all those coinbase spends immature/invalid")
+ b = self.nodes[0].getblockhash(first_block + 100)
for node in self.nodes:
- node.invalidateblock(new_blocks[0])
+ node.invalidateblock(b)
- # mempool should be empty.
+ self.log.info("Check that the mempool is empty")
assert_equal(set(self.nodes[0].getrawmempool()), set())
self.sync_all()
diff --git a/test/functional/mempool_resurrect.py b/test/functional/mempool_resurrect.py
index 4aa58270b6..1b5ca7e15a 100755
--- a/test/functional/mempool_resurrect.py
+++ b/test/functional/mempool_resurrect.py
@@ -4,6 +4,7 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test resurrection of mined transactions when the blockchain is re-organized."""
+from test_framework.blocktools import COINBASE_MATURITY
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_equal
from test_framework.wallet import MiniWallet
@@ -20,7 +21,7 @@ class MempoolCoinbaseTest(BitcoinTestFramework):
# Add enough mature utxos to the wallet so that all txs spend confirmed coins
wallet.generate(3)
- node.generate(100)
+ node.generate(COINBASE_MATURITY)
# Spend block 1/2/3's coinbase transactions
# Mine a block
diff --git a/test/functional/mempool_unbroadcast.py b/test/functional/mempool_unbroadcast.py
index b475b65e68..7d9e6c306d 100755
--- a/test/functional/mempool_unbroadcast.py
+++ b/test/functional/mempool_unbroadcast.py
@@ -92,6 +92,12 @@ class MempoolUnbroadcastTest(BitcoinTestFramework):
self.disconnect_nodes(0, 1)
node.disconnect_p2ps()
+ self.log.info("Rebroadcast transaction and ensure it is not added to unbroadcast set when already in mempool")
+ rpc_tx_hsh = node.sendrawtransaction(txFS["hex"])
+ mempool = node.getrawmempool(True)
+ assert rpc_tx_hsh in mempool
+ assert not mempool[rpc_tx_hsh]['unbroadcast']
+
def test_txn_removal(self):
self.log.info("Test that transactions removed from mempool are removed from unbroadcast set")
node = self.nodes[0]
diff --git a/test/functional/mining_basic.py b/test/functional/mining_basic.py
index ba467c1517..01fc02f27e 100755
--- a/test/functional/mining_basic.py
+++ b/test/functional/mining_basic.py
@@ -13,6 +13,7 @@ from decimal import Decimal
from test_framework.blocktools import (
create_coinbase,
+ get_witness_script,
NORMAL_GBT_REQUEST_PARAMS,
TIME_GENESIS_BLOCK,
)
@@ -20,6 +21,7 @@ from test_framework.messages import (
CBlock,
CBlockHeader,
BLOCK_HEADER_SIZE,
+ ser_uint256,
)
from test_framework.p2p import P2PDataStore
from test_framework.test_framework import BitcoinTestFramework
@@ -49,6 +51,9 @@ class MiningTest(BitcoinTestFramework):
self.setup_clean_chain = True
self.supports_cli = False
+ def skip_test_if_missing_module(self):
+ self.skip_if_no_wallet()
+
def mine_chain(self):
self.log.info('Create some old blocks')
for t in range(TIME_GENESIS_BLOCK, TIME_GENESIS_BLOCK + 200 * 600, 600):
@@ -89,7 +94,21 @@ class MiningTest(BitcoinTestFramework):
assert_equal(mining_info['networkhashps'], Decimal('0.003333333333333334'))
assert_equal(mining_info['pooledtx'], 0)
- # Mine a block to leave initial block download
+ self.log.info("getblocktemplate: Test default witness commitment")
+ txid = int(node.sendtoaddress(node.getnewaddress(), 1), 16)
+ tmpl = node.getblocktemplate(NORMAL_GBT_REQUEST_PARAMS)
+
+ # Check that default_witness_commitment is present.
+ assert 'default_witness_commitment' in tmpl
+ witness_commitment = tmpl['default_witness_commitment']
+
+ # Check that default_witness_commitment is correct.
+ witness_root = CBlock.get_merkle_root([ser_uint256(0),
+ ser_uint256(txid)])
+ script = get_witness_script(witness_root, 0)
+ assert_equal(witness_commitment, script.hex())
+
+ # Mine a block to leave initial block download and clear the mempool
node.generatetoaddress(1, node.get_deterministic_priv_key().address)
tmpl = node.getblocktemplate(NORMAL_GBT_REQUEST_PARAMS)
self.log.info("getblocktemplate: Test capability advertised")
diff --git a/test/functional/mining_getblocktemplate_longpoll.py b/test/functional/mining_getblocktemplate_longpoll.py
index cc32f78e2e..715b68e04c 100755
--- a/test/functional/mining_getblocktemplate_longpoll.py
+++ b/test/functional/mining_getblocktemplate_longpoll.py
@@ -8,6 +8,7 @@ from decimal import Decimal
import random
import threading
+from test_framework.blocktools import COINBASE_MATURITY
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import get_rpc_proxy
from test_framework.wallet import MiniWallet
@@ -62,7 +63,7 @@ class GetBlockTemplateLPTest(BitcoinTestFramework):
assert not thr.is_alive()
# Add enough mature utxos to the wallets, so that all txs spend confirmed coins
- self.nodes[0].generate(100)
+ self.nodes[0].generate(COINBASE_MATURITY)
self.sync_blocks()
self.log.info("Test that introducing a new transaction into the mempool will terminate the longpoll")
diff --git a/test/functional/p2p_addr_relay.py b/test/functional/p2p_addr_relay.py
index 87297989ba..ff1d85a9be 100755
--- a/test/functional/p2p_addr_relay.py
+++ b/test/functional/p2p_addr_relay.py
@@ -13,40 +13,56 @@ from test_framework.messages import (
msg_addr,
msg_getaddr
)
-from test_framework.p2p import P2PInterface
-from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import (
- assert_equal,
+from test_framework.p2p import (
+ P2PInterface,
+ p2p_lock,
)
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import assert_equal
+import random
import time
class AddrReceiver(P2PInterface):
num_ipv4_received = 0
+ test_addr_contents = False
+ _tokens = 1
+
+ def __init__(self, test_addr_contents=False):
+ super().__init__()
+ self.test_addr_contents = test_addr_contents
def on_addr(self, message):
for addr in message.addrs:
- assert_equal(addr.nServices, 9)
- if not 8333 <= addr.port < 8343:
- raise AssertionError("Invalid addr.port of {} (8333-8342 expected)".format(addr.port))
- assert addr.ip.startswith('123.123.123.')
self.num_ipv4_received += 1
-
-
-class GetAddrStore(P2PInterface):
- getaddr_received = False
- num_ipv4_received = 0
+ if(self.test_addr_contents):
+ # relay_tests checks the content of the addr messages match
+ # expectations based on the message creation in setup_addr_msg
+ assert_equal(addr.nServices, 9)
+ if not 8333 <= addr.port < 8343:
+ raise AssertionError("Invalid addr.port of {} (8333-8342 expected)".format(addr.port))
+ assert addr.ip.startswith('123.123.123.')
def on_getaddr(self, message):
- self.getaddr_received = True
+ # When the node sends us a getaddr, it increments the addr relay tokens for the connection by 1000
+ self._tokens += 1000
- def on_addr(self, message):
- for addr in message.addrs:
- self.num_ipv4_received += 1
+ @property
+ def tokens(self):
+ with p2p_lock:
+ return self._tokens
+
+ def increment_tokens(self, n):
+ # When we move mocktime forward, the node increments the addr relay tokens for its peers
+ with p2p_lock:
+ self._tokens += n
def addr_received(self):
return self.num_ipv4_received != 0
+ def getaddr_received(self):
+ return self.message_count['getaddr'] > 0
+
class AddrTest(BitcoinTestFramework):
counter = 0
@@ -54,12 +70,14 @@ class AddrTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 1
+ self.extra_args = [["-whitelist=addr@127.0.0.1"]]
def run_test(self):
self.oversized_addr_test()
self.relay_tests()
self.getaddr_tests()
self.blocksonly_mode_tests()
+ self.rate_limit_tests()
def setup_addr_msg(self, num):
addrs = []
@@ -76,10 +94,23 @@ class AddrTest(BitcoinTestFramework):
msg.addrs = addrs
return msg
+ def setup_rand_addr_msg(self, num):
+ addrs = []
+ for i in range(num):
+ addr = CAddress()
+ addr.time = self.mocktime + i
+ addr.nServices = NODE_NETWORK | NODE_WITNESS
+ addr.ip = f"{random.randrange(128,169)}.{random.randrange(1,255)}.{random.randrange(1,255)}.{random.randrange(1,255)}"
+ addr.port = 8333
+ addrs.append(addr)
+ msg = msg_addr()
+ msg.addrs = addrs
+ return msg
+
def send_addr_msg(self, source, msg, receivers):
source.send_and_ping(msg)
# pop m_next_addr_send timer
- self.mocktime += 5 * 60
+ self.mocktime += 10 * 60
self.nodes[0].setmocktime(self.mocktime)
for peer in receivers:
peer.sync_send_with_ping()
@@ -101,7 +132,7 @@ class AddrTest(BitcoinTestFramework):
num_receivers = 7
receivers = []
for _ in range(num_receivers):
- receivers.append(self.nodes[0].add_p2p_connection(AddrReceiver()))
+ receivers.append(self.nodes[0].add_p2p_connection(AddrReceiver(test_addr_contents=True)))
# Keep this with length <= 10. Addresses from larger messages are not
# relayed.
@@ -125,8 +156,8 @@ class AddrTest(BitcoinTestFramework):
self.nodes[0].disconnect_p2ps()
self.log.info('Check relay of addresses received from outbound peers')
- inbound_peer = self.nodes[0].add_p2p_connection(AddrReceiver())
- full_outbound_peer = self.nodes[0].add_outbound_p2p_connection(GetAddrStore(), p2p_idx=0, connection_type="outbound-full-relay")
+ inbound_peer = self.nodes[0].add_p2p_connection(AddrReceiver(test_addr_contents=True))
+ full_outbound_peer = self.nodes[0].add_outbound_p2p_connection(AddrReceiver(), p2p_idx=0, connection_type="outbound-full-relay")
msg = self.setup_addr_msg(2)
self.send_addr_msg(full_outbound_peer, msg, [inbound_peer])
self.log.info('Check that the first addr message received from an outbound peer is not relayed')
@@ -142,7 +173,7 @@ class AddrTest(BitcoinTestFramework):
assert_equal(inbound_peer.num_ipv4_received, 2)
self.log.info('Check address relay to outbound peers')
- block_relay_peer = self.nodes[0].add_outbound_p2p_connection(GetAddrStore(), p2p_idx=1, connection_type="block-relay-only")
+ block_relay_peer = self.nodes[0].add_outbound_p2p_connection(AddrReceiver(), p2p_idx=1, connection_type="block-relay-only")
msg3 = self.setup_addr_msg(2)
self.send_addr_msg(inbound_peer, msg3, [full_outbound_peer, block_relay_peer])
@@ -156,17 +187,17 @@ class AddrTest(BitcoinTestFramework):
def getaddr_tests(self):
self.log.info('Test getaddr behavior')
self.log.info('Check that we send a getaddr message upon connecting to an outbound-full-relay peer')
- full_outbound_peer = self.nodes[0].add_outbound_p2p_connection(GetAddrStore(), p2p_idx=0, connection_type="outbound-full-relay")
+ full_outbound_peer = self.nodes[0].add_outbound_p2p_connection(AddrReceiver(), p2p_idx=0, connection_type="outbound-full-relay")
full_outbound_peer.sync_with_ping()
- assert full_outbound_peer.getaddr_received
+ assert full_outbound_peer.getaddr_received()
self.log.info('Check that we do not send a getaddr message upon connecting to a block-relay-only peer')
- block_relay_peer = self.nodes[0].add_outbound_p2p_connection(GetAddrStore(), p2p_idx=1, connection_type="block-relay-only")
+ block_relay_peer = self.nodes[0].add_outbound_p2p_connection(AddrReceiver(), p2p_idx=1, connection_type="block-relay-only")
block_relay_peer.sync_with_ping()
- assert_equal(block_relay_peer.getaddr_received, False)
+ assert_equal(block_relay_peer.getaddr_received(), False)
self.log.info('Check that we answer getaddr messages only from inbound peers')
- inbound_peer = self.nodes[0].add_p2p_connection(GetAddrStore())
+ inbound_peer = self.nodes[0].add_p2p_connection(AddrReceiver())
inbound_peer.sync_with_ping()
# Add some addresses to addrman
@@ -182,7 +213,7 @@ class AddrTest(BitcoinTestFramework):
self.mocktime += 5 * 60
self.nodes[0].setmocktime(self.mocktime)
- inbound_peer.wait_until(inbound_peer.addr_received)
+ inbound_peer.wait_until(lambda: inbound_peer.addr_received() is True)
assert_equal(full_outbound_peer.num_ipv4_received, 0)
assert_equal(block_relay_peer.num_ipv4_received, 0)
@@ -192,13 +223,13 @@ class AddrTest(BitcoinTestFramework):
def blocksonly_mode_tests(self):
self.log.info('Test addr relay in -blocksonly mode')
- self.restart_node(0, ["-blocksonly"])
+ self.restart_node(0, ["-blocksonly", "-whitelist=addr@127.0.0.1"])
self.mocktime = int(time.time())
self.log.info('Check that we send getaddr messages')
- full_outbound_peer = self.nodes[0].add_outbound_p2p_connection(GetAddrStore(), p2p_idx=0, connection_type="outbound-full-relay")
+ full_outbound_peer = self.nodes[0].add_outbound_p2p_connection(AddrReceiver(), p2p_idx=0, connection_type="outbound-full-relay")
full_outbound_peer.sync_with_ping()
- assert full_outbound_peer.getaddr_received
+ assert full_outbound_peer.getaddr_received()
self.log.info('Check that we relay address messages')
addr_source = self.nodes[0].add_p2p_connection(P2PInterface())
@@ -208,6 +239,63 @@ class AddrTest(BitcoinTestFramework):
self.nodes[0].disconnect_p2ps()
+ def send_addrs_and_test_rate_limiting(self, peer, no_relay, new_addrs, total_addrs):
+ """Send an addr message and check that the number of addresses processed and rate-limited is as expected"""
+
+ peer.send_and_ping(self.setup_rand_addr_msg(new_addrs))
+
+ peerinfo = self.nodes[0].getpeerinfo()[0]
+ addrs_processed = peerinfo['addr_processed']
+ addrs_rate_limited = peerinfo['addr_rate_limited']
+ self.log.debug(f"addrs_processed = {addrs_processed}, addrs_rate_limited = {addrs_rate_limited}")
+
+ if no_relay:
+ assert_equal(addrs_processed, 0)
+ assert_equal(addrs_rate_limited, 0)
+ else:
+ assert_equal(addrs_processed, min(total_addrs, peer.tokens))
+ assert_equal(addrs_rate_limited, max(0, total_addrs - peer.tokens))
+
+ def rate_limit_tests(self):
+
+ self.mocktime = int(time.time())
+ self.restart_node(0, [])
+ self.nodes[0].setmocktime(self.mocktime)
+
+ for contype, no_relay in [("outbound-full-relay", False), ("block-relay-only", True), ("inbound", False)]:
+ self.log.info(f'Test rate limiting of addr processing for {contype} peers')
+ if contype == "inbound":
+ peer = self.nodes[0].add_p2p_connection(AddrReceiver())
+ else:
+ peer = self.nodes[0].add_outbound_p2p_connection(AddrReceiver(), p2p_idx=0, connection_type=contype)
+
+ # Send 600 addresses. For all but the block-relay-only peer this should result in addresses being processed.
+ self.send_addrs_and_test_rate_limiting(peer, no_relay, 600, 600)
+
+ # Send 600 more addresses. For the outbound-full-relay peer (which we send a GETADDR, and thus will
+ # process up to 1001 incoming addresses), this means more addresses will be processed.
+ self.send_addrs_and_test_rate_limiting(peer, no_relay, 600, 1200)
+
+ # Send 10 more. As we reached the processing limit for all nodes, no more addresses should be procesesd.
+ self.send_addrs_and_test_rate_limiting(peer, no_relay, 10, 1210)
+
+ # Advance the time by 100 seconds, permitting the processing of 10 more addresses.
+ # Send 200 and verify that 10 are processed.
+ self.mocktime += 100
+ self.nodes[0].setmocktime(self.mocktime)
+ peer.increment_tokens(10)
+
+ self.send_addrs_and_test_rate_limiting(peer, no_relay, 200, 1410)
+
+ # Advance the time by 1000 seconds, permitting the processing of 100 more addresses.
+ # Send 200 and verify that 100 are processed.
+ self.mocktime += 1000
+ self.nodes[0].setmocktime(self.mocktime)
+ peer.increment_tokens(100)
+
+ self.send_addrs_and_test_rate_limiting(peer, no_relay, 200, 1610)
+
+ self.nodes[0].disconnect_p2ps()
if __name__ == '__main__':
AddrTest().main()
diff --git a/test/functional/p2p_addrfetch.py b/test/functional/p2p_addrfetch.py
new file mode 100755
index 0000000000..66ee1544a9
--- /dev/null
+++ b/test/functional/p2p_addrfetch.py
@@ -0,0 +1,62 @@
+#!/usr/bin/env python3
+# Copyright (c) 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 p2p addr-fetch connections
+"""
+
+import time
+
+from test_framework.messages import msg_addr, CAddress, NODE_NETWORK, NODE_WITNESS
+from test_framework.p2p import P2PInterface, p2p_lock
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import assert_equal
+
+ADDR = CAddress()
+ADDR.time = int(time.time())
+ADDR.nServices = NODE_NETWORK | NODE_WITNESS
+ADDR.ip = "192.0.0.8"
+ADDR.port = 18444
+
+
+class P2PAddrFetch(BitcoinTestFramework):
+
+ def set_test_params(self):
+ self.setup_clean_chain = True
+ self.num_nodes = 1
+
+ def run_test(self):
+ node = self.nodes[0]
+ self.log.info("Connect to an addr-fetch peer")
+ peer = node.add_outbound_p2p_connection(P2PInterface(), p2p_idx=0, connection_type="addr-fetch")
+ info = node.getpeerinfo()
+ assert_equal(len(info), 1)
+ assert_equal(info[0]['connection_type'], 'addr-fetch')
+
+ self.log.info("Check that we send getaddr but don't try to sync headers with the addr-fetch peer")
+ peer.sync_send_with_ping()
+ with p2p_lock:
+ assert peer.message_count['getaddr'] == 1
+ assert peer.message_count['getheaders'] == 0
+
+ self.log.info("Check that answering the getaddr with a single address does not lead to disconnect")
+ # This prevents disconnecting on self-announcements
+ msg = msg_addr()
+ msg.addrs = [ADDR]
+ peer.send_and_ping(msg)
+ assert_equal(len(node.getpeerinfo()), 1)
+
+ self.log.info("Check that answering with larger addr messages leads to disconnect")
+ msg.addrs = [ADDR] * 2
+ peer.send_message(msg)
+ peer.wait_for_disconnect(timeout=5)
+
+ self.log.info("Check timeout for addr-fetch peer that does not send addrs")
+ peer = node.add_outbound_p2p_connection(P2PInterface(), p2p_idx=1, connection_type="addr-fetch")
+ node.setmocktime(int(time.time()) + 301) # Timeout: 5 minutes
+ peer.wait_for_disconnect(timeout=5)
+
+
+if __name__ == '__main__':
+ P2PAddrFetch().main()
diff --git a/test/functional/p2p_addrv2_relay.py b/test/functional/p2p_addrv2_relay.py
index 23ce3e5d04..32c1d42b1c 100755
--- a/test/functional/p2p_addrv2_relay.py
+++ b/test/functional/p2p_addrv2_relay.py
@@ -18,12 +18,19 @@ from test_framework.p2p import P2PInterface
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_equal
+I2P_ADDR = "c4gfnttsuwqomiygupdqqqyy5y5emnk5c73hrfvatri67prd7vyq.b32.i2p"
+
ADDRS = []
for i in range(10):
addr = CAddress()
addr.time = int(time.time()) + i
addr.nServices = NODE_NETWORK | NODE_WITNESS
- addr.ip = "123.123.123.{}".format(i % 256)
+ # Add one I2P address at an arbitrary position.
+ if i == 5:
+ addr.net = addr.NET_I2P
+ addr.ip = I2P_ADDR
+ else:
+ addr.ip = f"123.123.123.{i % 256}"
addr.port = 8333 + i
ADDRS.append(addr)
@@ -35,11 +42,10 @@ class AddrReceiver(P2PInterface):
super().__init__(support_addrv2 = True)
def on_addrv2(self, message):
- for addr in message.addrs:
- assert_equal(addr.nServices, 9)
- assert addr.ip.startswith('123.123.123.')
- assert (8333 <= addr.port < 8343)
- self.addrv2_received_and_checked = True
+ expected_set = set((addr.ip, addr.port) for addr in ADDRS)
+ received_set = set((addr.ip, addr.port) for addr in message.addrs)
+ if expected_set == received_set:
+ self.addrv2_received_and_checked = True
def wait_for_addrv2(self):
self.wait_until(lambda: "addrv2" in self.last_message)
@@ -49,6 +55,7 @@ class AddrTest(BitcoinTestFramework):
def set_test_params(self):
self.setup_clean_chain = True
self.num_nodes = 1
+ self.extra_args = [["-whitelist=addr@127.0.0.1"]]
def run_test(self):
self.log.info('Create connection that sends addrv2 messages')
@@ -64,15 +71,18 @@ class AddrTest(BitcoinTestFramework):
addr_receiver = self.nodes[0].add_p2p_connection(AddrReceiver())
msg.addrs = ADDRS
with self.nodes[0].assert_debug_log([
- 'Added 10 addresses from 127.0.0.1: 0 tried',
- 'received: addrv2 (131 bytes) peer=0',
- 'sending addrv2 (131 bytes) peer=1',
+ # The I2P address is not added to node's own addrman because it has no
+ # I2P reachability (thus 10 - 1 = 9).
+ 'Added 9 addresses from 127.0.0.1: 0 tried',
+ 'received: addrv2 (159 bytes) peer=0',
+ 'sending addrv2 (159 bytes) peer=1',
]):
addr_source.send_and_ping(msg)
self.nodes[0].setmocktime(int(time.time()) + 30 * 60)
addr_receiver.wait_for_addrv2()
assert addr_receiver.addrv2_received_and_checked
+ assert_equal(len(self.nodes[0].getnodeaddresses(count=0, network="i2p")), 0)
if __name__ == '__main__':
diff --git a/test/functional/p2p_blockfilters.py b/test/functional/p2p_blockfilters.py
index 03662babef..63fc2a98d4 100755
--- a/test/functional/p2p_blockfilters.py
+++ b/test/functional/p2p_blockfilters.py
@@ -24,7 +24,7 @@ from test_framework.util import (
assert_equal,
)
-class CFiltersClient(P2PInterface):
+class FiltersClient(P2PInterface):
def __init__(self):
super().__init__()
# Store the cfilters received.
@@ -39,6 +39,7 @@ class CFiltersClient(P2PInterface):
"""Store cfilters received in a list."""
self.cfilters.append(message)
+
class CompactFiltersTest(BitcoinTestFramework):
def set_test_params(self):
self.setup_clean_chain = True
@@ -51,8 +52,8 @@ class CompactFiltersTest(BitcoinTestFramework):
def run_test(self):
# Node 0 supports COMPACT_FILTERS, node 1 does not.
- node0 = self.nodes[0].add_p2p_connection(CFiltersClient())
- node1 = self.nodes[1].add_p2p_connection(CFiltersClient())
+ peer_0 = self.nodes[0].add_p2p_connection(FiltersClient())
+ peer_1 = self.nodes[1].add_p2p_connection(FiltersClient())
# Nodes 0 & 1 share the same first 999 blocks in the chain.
self.nodes[0].generate(999)
@@ -61,16 +62,16 @@ class CompactFiltersTest(BitcoinTestFramework):
# Stale blocks by disconnecting nodes 0 & 1, mining, then reconnecting
self.disconnect_nodes(0, 1)
- self.nodes[0].generate(1)
- self.wait_until(lambda: self.nodes[0].getblockcount() == 1000)
- stale_block_hash = self.nodes[0].getblockhash(1000)
+ stale_block_hash = self.nodes[0].generate(1)[0]
+ self.nodes[0].syncwithvalidationinterfacequeue()
+ assert_equal(self.nodes[0].getblockcount(), 1000)
self.nodes[1].generate(1001)
- self.wait_until(lambda: self.nodes[1].getblockcount() == 2000)
+ assert_equal(self.nodes[1].getblockcount(), 2000)
# Check that nodes have signalled NODE_COMPACT_FILTERS correctly.
- assert node0.nServices & NODE_COMPACT_FILTERS != 0
- assert node1.nServices & NODE_COMPACT_FILTERS == 0
+ assert peer_0.nServices & NODE_COMPACT_FILTERS != 0
+ assert peer_1.nServices & NODE_COMPACT_FILTERS == 0
# Check that the localservices is as expected.
assert int(self.nodes[0].getnetworkinfo()['localservices'], 16) & NODE_COMPACT_FILTERS != 0
@@ -79,10 +80,10 @@ class CompactFiltersTest(BitcoinTestFramework):
self.log.info("get cfcheckpt on chain to be re-orged out.")
request = msg_getcfcheckpt(
filter_type=FILTER_TYPE_BASIC,
- stop_hash=int(stale_block_hash, 16)
+ stop_hash=int(stale_block_hash, 16),
)
- node0.send_and_ping(message=request)
- response = node0.last_message['cfcheckpt']
+ peer_0.send_and_ping(message=request)
+ response = peer_0.last_message['cfcheckpt']
assert_equal(response.filter_type, request.filter_type)
assert_equal(response.stop_hash, request.stop_hash)
assert_equal(len(response.headers), 1)
@@ -90,6 +91,7 @@ class CompactFiltersTest(BitcoinTestFramework):
self.log.info("Reorg node 0 to a new chain.")
self.connect_nodes(0, 1)
self.sync_blocks(timeout=600)
+ self.nodes[0].syncwithvalidationinterfacequeue()
main_block_hash = self.nodes[0].getblockhash(1000)
assert main_block_hash != stale_block_hash, "node 0 chain did not reorganize"
@@ -98,10 +100,10 @@ class CompactFiltersTest(BitcoinTestFramework):
tip_hash = self.nodes[0].getbestblockhash()
request = msg_getcfcheckpt(
filter_type=FILTER_TYPE_BASIC,
- stop_hash=int(tip_hash, 16)
+ stop_hash=int(tip_hash, 16),
)
- node0.send_and_ping(request)
- response = node0.last_message['cfcheckpt']
+ peer_0.send_and_ping(request)
+ response = peer_0.last_message['cfcheckpt']
assert_equal(response.filter_type, request.filter_type)
assert_equal(response.stop_hash, request.stop_hash)
@@ -109,51 +111,51 @@ class CompactFiltersTest(BitcoinTestFramework):
tip_cfcheckpt = self.nodes[0].getblockfilter(tip_hash, 'basic')['header']
assert_equal(
response.headers,
- [int(header, 16) for header in (main_cfcheckpt, tip_cfcheckpt)]
+ [int(header, 16) for header in (main_cfcheckpt, tip_cfcheckpt)],
)
self.log.info("Check that peers can fetch cfcheckpt on stale chain.")
request = msg_getcfcheckpt(
filter_type=FILTER_TYPE_BASIC,
- stop_hash=int(stale_block_hash, 16)
+ stop_hash=int(stale_block_hash, 16),
)
- node0.send_and_ping(request)
- response = node0.last_message['cfcheckpt']
+ peer_0.send_and_ping(request)
+ response = peer_0.last_message['cfcheckpt']
stale_cfcheckpt = self.nodes[0].getblockfilter(stale_block_hash, 'basic')['header']
assert_equal(
response.headers,
- [int(header, 16) for header in (stale_cfcheckpt,)]
+ [int(header, 16) for header in (stale_cfcheckpt, )],
)
self.log.info("Check that peers can fetch cfheaders on active chain.")
request = msg_getcfheaders(
filter_type=FILTER_TYPE_BASIC,
start_height=1,
- stop_hash=int(main_block_hash, 16)
+ stop_hash=int(main_block_hash, 16),
)
- node0.send_and_ping(request)
- response = node0.last_message['cfheaders']
+ peer_0.send_and_ping(request)
+ response = peer_0.last_message['cfheaders']
main_cfhashes = response.hashes
assert_equal(len(main_cfhashes), 1000)
assert_equal(
compute_last_header(response.prev_header, response.hashes),
- int(main_cfcheckpt, 16)
+ int(main_cfcheckpt, 16),
)
self.log.info("Check that peers can fetch cfheaders on stale chain.")
request = msg_getcfheaders(
filter_type=FILTER_TYPE_BASIC,
start_height=1,
- stop_hash=int(stale_block_hash, 16)
+ stop_hash=int(stale_block_hash, 16),
)
- node0.send_and_ping(request)
- response = node0.last_message['cfheaders']
+ peer_0.send_and_ping(request)
+ response = peer_0.last_message['cfheaders']
stale_cfhashes = response.hashes
assert_equal(len(stale_cfhashes), 1000)
assert_equal(
compute_last_header(response.prev_header, response.hashes),
- int(stale_cfcheckpt, 16)
+ int(stale_cfcheckpt, 16),
)
self.log.info("Check that peers can fetch cfilters.")
@@ -161,11 +163,10 @@ class CompactFiltersTest(BitcoinTestFramework):
request = msg_getcfilters(
filter_type=FILTER_TYPE_BASIC,
start_height=1,
- stop_hash=int(stop_hash, 16)
+ stop_hash=int(stop_hash, 16),
)
- node0.send_message(request)
- node0.sync_with_ping()
- response = node0.pop_cfilters()
+ peer_0.send_and_ping(request)
+ response = peer_0.pop_cfilters()
assert_equal(len(response), 10)
self.log.info("Check that cfilter responses are correct.")
@@ -180,11 +181,10 @@ class CompactFiltersTest(BitcoinTestFramework):
request = msg_getcfilters(
filter_type=FILTER_TYPE_BASIC,
start_height=1000,
- stop_hash=int(stale_block_hash, 16)
+ stop_hash=int(stale_block_hash, 16),
)
- node0.send_message(request)
- node0.sync_with_ping()
- response = node0.pop_cfilters()
+ peer_0.send_and_ping(request)
+ response = peer_0.pop_cfilters()
assert_equal(len(response), 1)
cfilter = response[0]
@@ -197,23 +197,23 @@ class CompactFiltersTest(BitcoinTestFramework):
requests = [
msg_getcfcheckpt(
filter_type=FILTER_TYPE_BASIC,
- stop_hash=int(main_block_hash, 16)
+ stop_hash=int(main_block_hash, 16),
),
msg_getcfheaders(
filter_type=FILTER_TYPE_BASIC,
start_height=1000,
- stop_hash=int(main_block_hash, 16)
+ stop_hash=int(main_block_hash, 16),
),
msg_getcfilters(
filter_type=FILTER_TYPE_BASIC,
start_height=1000,
- stop_hash=int(main_block_hash, 16)
+ stop_hash=int(main_block_hash, 16),
),
]
for request in requests:
- node1 = self.nodes[1].add_p2p_connection(P2PInterface())
- node1.send_message(request)
- node1.wait_for_disconnect()
+ peer_1 = self.nodes[1].add_p2p_connection(P2PInterface())
+ peer_1.send_message(request)
+ peer_1.wait_for_disconnect()
self.log.info("Check that invalid requests result in disconnection.")
requests = [
@@ -221,18 +221,18 @@ class CompactFiltersTest(BitcoinTestFramework):
msg_getcfilters(
filter_type=FILTER_TYPE_BASIC,
start_height=0,
- stop_hash=int(main_block_hash, 16)
+ stop_hash=int(main_block_hash, 16),
),
# Requesting too many filter headers results in disconnection.
msg_getcfheaders(
filter_type=FILTER_TYPE_BASIC,
start_height=0,
- stop_hash=int(tip_hash, 16)
+ stop_hash=int(tip_hash, 16),
),
# Requesting unknown filter type results in disconnection.
msg_getcfcheckpt(
filter_type=255,
- stop_hash=int(main_block_hash, 16)
+ stop_hash=int(main_block_hash, 16),
),
# Requesting unknown hash results in disconnection.
msg_getcfcheckpt(
@@ -241,9 +241,10 @@ class CompactFiltersTest(BitcoinTestFramework):
),
]
for request in requests:
- node0 = self.nodes[0].add_p2p_connection(P2PInterface())
- node0.send_message(request)
- node0.wait_for_disconnect()
+ peer_0 = self.nodes[0].add_p2p_connection(P2PInterface())
+ peer_0.send_message(request)
+ peer_0.wait_for_disconnect()
+
def compute_last_header(prev_header, hashes):
"""Compute the last filter header from a starting header and a sequence of filter hashes."""
@@ -252,5 +253,6 @@ def compute_last_header(prev_header, hashes):
header = hash256(ser_uint256(filter_hash) + header)
return uint256_from_str(header)
+
if __name__ == '__main__':
CompactFiltersTest().main()
diff --git a/test/functional/p2p_blocksonly.py b/test/functional/p2p_blocksonly.py
index ab2556cd72..6409d4ea82 100755
--- a/test/functional/p2p_blocksonly.py
+++ b/test/functional/p2p_blocksonly.py
@@ -6,6 +6,7 @@
import time
+from test_framework.blocktools import COINBASE_MATURITY
from test_framework.messages import msg_tx
from test_framework.p2p import P2PInterface, P2PTxInvStore
from test_framework.test_framework import BitcoinTestFramework
@@ -23,7 +24,7 @@ class P2PBlocksOnly(BitcoinTestFramework):
self.miniwallet = MiniWallet(self.nodes[0])
# Add enough mature utxos to the wallet, so that all txs spend confirmed coins
self.miniwallet.generate(2)
- self.nodes[0].generate(100)
+ self.nodes[0].generate(COINBASE_MATURITY)
self.blocksonly_mode_tests()
self.blocks_relay_conn_tests()
diff --git a/test/functional/p2p_compactblocks.py b/test/functional/p2p_compactblocks.py
index 55573efc06..b4e662de2e 100755
--- a/test/functional/p2p_compactblocks.py
+++ b/test/functional/p2p_compactblocks.py
@@ -9,12 +9,62 @@ Version 2 compact blocks are post-segwit (wtxids)
"""
import random
-from test_framework.blocktools import create_block, NORMAL_GBT_REQUEST_PARAMS, add_witness_commitment
-from test_framework.messages import BlockTransactions, BlockTransactionsRequest, calculate_shortid, CBlock, CBlockHeader, CInv, COutPoint, CTransaction, CTxIn, CTxInWitness, CTxOut, FromHex, HeaderAndShortIDs, msg_no_witness_block, msg_no_witness_blocktxn, msg_cmpctblock, msg_getblocktxn, msg_getdata, msg_getheaders, msg_headers, msg_inv, msg_sendcmpct, msg_sendheaders, msg_tx, msg_block, msg_blocktxn, MSG_BLOCK, MSG_CMPCT_BLOCK, MSG_WITNESS_FLAG, NODE_NETWORK, P2PHeaderAndShortIDs, PrefilledTransaction, ser_uint256, ToHex
-from test_framework.p2p import p2p_lock, P2PInterface
-from test_framework.script import CScript, OP_TRUE, OP_DROP
+from test_framework.blocktools import (
+ COINBASE_MATURITY,
+ NORMAL_GBT_REQUEST_PARAMS,
+ add_witness_commitment,
+ create_block,
+)
+from test_framework.messages import (
+ BlockTransactions,
+ BlockTransactionsRequest,
+ CBlock,
+ CBlockHeader,
+ CInv,
+ COutPoint,
+ CTransaction,
+ CTxIn,
+ CTxInWitness,
+ CTxOut,
+ from_hex,
+ HeaderAndShortIDs,
+ MSG_BLOCK,
+ MSG_CMPCT_BLOCK,
+ MSG_WITNESS_FLAG,
+ NODE_NETWORK,
+ P2PHeaderAndShortIDs,
+ PrefilledTransaction,
+ calculate_shortid,
+ msg_block,
+ msg_blocktxn,
+ msg_cmpctblock,
+ msg_getblocktxn,
+ msg_getdata,
+ msg_getheaders,
+ msg_headers,
+ msg_inv,
+ msg_no_witness_block,
+ msg_no_witness_blocktxn,
+ msg_sendcmpct,
+ msg_sendheaders,
+ msg_tx,
+ ser_uint256,
+ tx_from_hex,
+)
+from test_framework.p2p import (
+ P2PInterface,
+ p2p_lock,
+)
+from test_framework.script import (
+ CScript,
+ OP_DROP,
+ OP_TRUE,
+)
from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import assert_equal, softfork_active
+from test_framework.util import (
+ assert_equal,
+ softfork_active,
+)
# TestP2PConn: A peer we use to send messages to bitcoind, and store responses.
class TestP2PConn(P2PInterface):
@@ -115,7 +165,7 @@ class CompactBlocksTest(BitcoinTestFramework):
block = self.build_block_on_tip(self.nodes[0])
self.segwit_node.send_and_ping(msg_no_witness_block(block))
assert int(self.nodes[0].getbestblockhash(), 16) == block.sha256
- self.nodes[0].generatetoaddress(100, self.nodes[0].getnewaddress(address_type="bech32"))
+ self.nodes[0].generatetoaddress(COINBASE_MATURITY, self.nodes[0].getnewaddress(address_type="bech32"))
total_value = block.vtx[0].vout[0].nValue
out_value = total_value // 10
@@ -226,7 +276,7 @@ class CompactBlocksTest(BitcoinTestFramework):
# This test actually causes bitcoind to (reasonably!) disconnect us, so do this last.
def test_invalid_cmpctblock_message(self):
- self.nodes[0].generate(101)
+ self.nodes[0].generate(COINBASE_MATURITY + 1)
block = self.build_block_on_tip(self.nodes[0])
cmpct_block = P2PHeaderAndShortIDs()
@@ -244,7 +294,7 @@ class CompactBlocksTest(BitcoinTestFramework):
version = test_node.cmpct_version
node = self.nodes[0]
# Generate a bunch of transactions.
- node.generate(101)
+ node.generate(COINBASE_MATURITY + 1)
num_transactions = 25
address = node.getnewaddress()
@@ -252,7 +302,7 @@ class CompactBlocksTest(BitcoinTestFramework):
for _ in range(num_transactions):
txid = node.sendtoaddress(address, 0.1)
hex_tx = node.gettransaction(txid)["hex"]
- tx = FromHex(CTransaction(), hex_tx)
+ tx = tx_from_hex(hex_tx)
if not tx.wit.is_null():
segwit_tx_generated = True
@@ -271,7 +321,7 @@ class CompactBlocksTest(BitcoinTestFramework):
block_hash = int(node.generate(1)[0], 16)
# Store the raw block in our internal format.
- block = FromHex(CBlock(), node.getblock("%064x" % block_hash, False))
+ block = from_hex(CBlock(), node.getblock("%064x" % block_hash, False))
for tx in block.vtx:
tx.calc_sha256()
block.rehash()
@@ -564,7 +614,7 @@ class CompactBlocksTest(BitcoinTestFramework):
current_height = chain_height
while (current_height >= chain_height - MAX_GETBLOCKTXN_DEPTH):
block_hash = node.getblockhash(current_height)
- block = FromHex(CBlock(), node.getblock(block_hash, False))
+ block = from_hex(CBlock(), node.getblock(block_hash, False))
msg = msg_getblocktxn()
msg.block_txn_request = BlockTransactionsRequest(int(block_hash, 16), [])
@@ -667,9 +717,9 @@ class CompactBlocksTest(BitcoinTestFramework):
[l.clear_block_announcement() for l in listeners]
- # ToHex() won't serialize with witness, but this block has no witnesses
- # anyway. TODO: repeat this test with witness tx's to a segwit node.
- node.submitblock(ToHex(block))
+ # serialize without witness (this block has no witnesses anyway).
+ # TODO: repeat this test with witness tx's to a segwit node.
+ node.submitblock(block.serialize().hex())
for l in listeners:
l.wait_until(lambda: "cmpctblock" in l.last_message, timeout=30)
diff --git a/test/functional/p2p_compactblocks_hb.py b/test/functional/p2p_compactblocks_hb.py
new file mode 100755
index 0000000000..a3d30a6f04
--- /dev/null
+++ b/test/functional/p2p_compactblocks_hb.py
@@ -0,0 +1,97 @@
+#!/usr/bin/env python3
+# Copyright (c) 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 HB selection logic."""
+
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import assert_equal
+
+
+class CompactBlocksConnectionTest(BitcoinTestFramework):
+ """Test class for verifying selection of HB peer connections."""
+
+ def set_test_params(self):
+ self.setup_clean_chain = True
+ self.num_nodes = 6
+
+ def peer_info(self, from_node, to_node):
+ """Query from_node for its getpeerinfo about to_node."""
+ for peerinfo in self.nodes[from_node].getpeerinfo():
+ if "(testnode%i)" % to_node in peerinfo['subver']:
+ return peerinfo
+ return None
+
+ def setup_network(self):
+ self.setup_nodes()
+ # Start network with everyone disconnected
+ self.sync_all()
+
+ def relay_block_through(self, peer):
+ """Relay a new block through peer peer, and return HB status between 1 and [2,3,4,5]."""
+ self.connect_nodes(peer, 0)
+ self.nodes[0].generate(1)
+ self.sync_blocks()
+ self.disconnect_nodes(peer, 0)
+ status_to = [self.peer_info(1, i)['bip152_hb_to'] for i in range(2, 6)]
+ status_from = [self.peer_info(i, 1)['bip152_hb_from'] for i in range(2, 6)]
+ assert_equal(status_to, status_from)
+ return status_to
+
+ def run_test(self):
+ self.log.info("Testing reserved high-bandwidth mode slot for outbound peer...")
+
+ # Connect everyone to node 0, and mine some blocks to get all nodes out of IBD.
+ for i in range(1, 6):
+ self.connect_nodes(i, 0)
+ self.nodes[0].generate(2)
+ self.sync_blocks()
+ for i in range(1, 6):
+ self.disconnect_nodes(i, 0)
+
+ # Construct network topology:
+ # - Node 0 is the block producer
+ # - Node 1 is the "target" node being tested
+ # - Nodes 2-5 are intermediaries.
+ # - Node 1 has an outbound connection to node 2
+ # - Node 1 has inbound connections from nodes 3-5
+ self.connect_nodes(3, 1)
+ self.connect_nodes(4, 1)
+ self.connect_nodes(5, 1)
+ self.connect_nodes(1, 2)
+
+ # Mine blocks subsequently relaying through nodes 3,4,5 (inbound to node 1)
+ for nodeid in range(3, 6):
+ status = self.relay_block_through(nodeid)
+ assert_equal(status, [False, nodeid >= 3, nodeid >= 4, nodeid >= 5])
+
+ # And again through each. This should not change HB status.
+ for nodeid in range(3, 6):
+ status = self.relay_block_through(nodeid)
+ assert_equal(status, [False, True, True, True])
+
+ # Now relay one block through peer 2 (outbound from node 1), so it should take HB status
+ # from one of the inbounds.
+ status = self.relay_block_through(2)
+ assert_equal(status[0], True)
+ assert_equal(sum(status), 3)
+
+ # Now relay again through nodes 3,4,5. Since 2 is outbound, it should remain HB.
+ for nodeid in range(3, 6):
+ status = self.relay_block_through(nodeid)
+ assert status[0]
+ assert status[nodeid - 2]
+ assert_equal(sum(status), 3)
+
+ # Reconnect peer 2, and retry. Now the three inbounds should be HB again.
+ self.disconnect_nodes(1, 2)
+ self.connect_nodes(1, 2)
+ for nodeid in range(3, 6):
+ status = self.relay_block_through(nodeid)
+ assert not status[0]
+ assert status[nodeid - 2]
+ assert_equal(status, [False, True, True, True])
+
+
+if __name__ == '__main__':
+ CompactBlocksConnectionTest().main()
diff --git a/test/functional/p2p_dos_header_tree.py b/test/functional/p2p_dos_header_tree.py
index 2349afa1ee..52a47c9bc2 100755
--- a/test/functional/p2p_dos_header_tree.py
+++ b/test/functional/p2p_dos_header_tree.py
@@ -6,7 +6,7 @@
from test_framework.messages import (
CBlockHeader,
- FromHex,
+ from_hex,
)
from test_framework.p2p import (
P2PInterface,
@@ -42,8 +42,8 @@ class RejectLowDifficultyHeadersTest(BitcoinTestFramework):
self.headers = [l for l in h_lines if not l.startswith(FORK_PREFIX)]
self.headers_fork = [l[len(FORK_PREFIX):] for l in h_lines if l.startswith(FORK_PREFIX)]
- self.headers = [FromHex(CBlockHeader(), h) for h in self.headers]
- self.headers_fork = [FromHex(CBlockHeader(), h) for h in self.headers_fork]
+ self.headers = [from_hex(CBlockHeader(), h) for h in self.headers]
+ self.headers_fork = [from_hex(CBlockHeader(), h) for h in self.headers_fork]
self.log.info("Feed all non-fork headers, including and up to the first checkpoint")
peer_checkpoint = self.nodes[0].add_p2p_connection(P2PInterface())
diff --git a/test/functional/p2p_eviction.py b/test/functional/p2p_eviction.py
index d60aa5b383..35bce7c69e 100755
--- a/test/functional/p2p_eviction.py
+++ b/test/functional/p2p_eviction.py
@@ -15,8 +15,16 @@ Therefore, this test is limited to the remaining protection criteria.
import time
-from test_framework.blocktools import create_block, create_coinbase
-from test_framework.messages import CTransaction, FromHex, msg_pong, msg_tx
+from test_framework.blocktools import (
+ COINBASE_MATURITY,
+ create_block,
+ create_coinbase,
+)
+from test_framework.messages import (
+ msg_pong,
+ msg_tx,
+ tx_from_hex,
+)
from test_framework.p2p import P2PDataStore, P2PInterface
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_equal
@@ -45,7 +53,7 @@ class P2PEvict(BitcoinTestFramework):
protected_peers = set() # peers that we expect to be protected from eviction
current_peer = -1
node = self.nodes[0]
- node.generatetoaddress(101, node.get_deterministic_priv_key().address)
+ node.generatetoaddress(COINBASE_MATURITY + 1, node.get_deterministic_priv_key().address)
self.log.info("Create 4 peers and protect them from eviction by sending us a block")
for _ in range(4):
@@ -85,7 +93,7 @@ class P2PEvict(BitcoinTestFramework):
'scriptPubKey': prevtx['vout'][0]['scriptPubKey']['hex'],
}],
)['hex']
- txpeer.send_message(msg_tx(FromHex(CTransaction(), sigtx)))
+ txpeer.send_message(msg_tx(tx_from_hex(sigtx)))
protected_peers.add(current_peer)
self.log.info("Create 8 peers and protect them from eviction by having faster pings")
diff --git a/test/functional/p2p_feefilter.py b/test/functional/p2p_feefilter.py
index 52dc4de3bd..0175b9f6c0 100755
--- a/test/functional/p2p_feefilter.py
+++ b/test/functional/p2p_feefilter.py
@@ -6,6 +6,7 @@
from decimal import Decimal
+from test_framework.blocktools import COINBASE_MATURITY
from test_framework.messages import MSG_TX, MSG_WTX, msg_feefilter
from test_framework.p2p import P2PInterface, p2p_lock
from test_framework.test_framework import BitcoinTestFramework
@@ -81,7 +82,7 @@ class FeeFilterTest(BitcoinTestFramework):
miniwallet = MiniWallet(node1)
# Add enough mature utxos to the wallet, so that all txs spend confirmed coins
miniwallet.generate(5)
- node1.generate(100)
+ node1.generate(COINBASE_MATURITY)
conn = self.nodes[0].add_p2p_connection(TestP2PConn())
diff --git a/test/functional/p2p_i2p_ports.py b/test/functional/p2p_i2p_ports.py
new file mode 100755
index 0000000000..13188b9305
--- /dev/null
+++ b/test/functional/p2p_i2p_ports.py
@@ -0,0 +1,43 @@
+#!/usr/bin/env python3
+# Copyright (c) 2021-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 ports handling for I2P hosts
+"""
+
+import re
+
+from test_framework.test_framework import BitcoinTestFramework
+
+
+class I2PPorts(BitcoinTestFramework):
+ def set_test_params(self):
+ self.num_nodes = 1
+ # The test assumes that an I2P SAM proxy is not listening here.
+ self.extra_args = [["-i2psam=127.0.0.1:60000"]]
+
+ def run_test(self):
+ node = self.nodes[0]
+
+ self.log.info("Ensure we don't try to connect if port!=0")
+ addr = "zsxwyo6qcn3chqzwxnseusqgsnuw3maqnztkiypyfxtya4snkoka.b32.i2p:8333"
+ raised = False
+ try:
+ with node.assert_debug_log(expected_msgs=[f"Error connecting to {addr}"]):
+ node.addnode(node=addr, command="onetry")
+ except AssertionError as e:
+ raised = True
+ if not re.search(r"Expected messages .* does not partially match log", str(e)):
+ raise AssertionError(f"Assertion raised as expected, but with an unexpected message: {str(e)}")
+ if not raised:
+ raise AssertionError("Assertion should have been raised")
+
+ self.log.info("Ensure we try to connect if port=0 and get an error due to missing I2P proxy")
+ addr = "h3r6bkn46qxftwja53pxiykntegfyfjqtnzbm6iv6r5mungmqgmq.b32.i2p:0"
+ with node.assert_debug_log(expected_msgs=[f"Error connecting to {addr}"]):
+ node.addnode(node=addr, command="onetry")
+
+
+if __name__ == '__main__':
+ I2PPorts().main()
diff --git a/test/functional/p2p_invalid_block.py b/test/functional/p2p_invalid_block.py
index 483f25f48c..91666d0f08 100755
--- a/test/functional/p2p_invalid_block.py
+++ b/test/functional/p2p_invalid_block.py
@@ -9,8 +9,11 @@ In this test we connect to one node over p2p, and test block requests:
2) Invalid block with duplicated transaction should be re-requested.
3) Invalid block with bad coinbase value should be rejected and not
re-requested.
+4) Invalid block due to future timestamp is later accepted when that timestamp
+becomes valid.
"""
import copy
+import time
from test_framework.blocktools import create_block, create_coinbase, create_tx_with_script
from test_framework.messages import COIN
@@ -18,6 +21,9 @@ from test_framework.p2p import P2PDataStore
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_equal
+MAX_FUTURE_BLOCK_TIME = 2 * 60 * 60
+
+
class InvalidBlockRequestTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 1
@@ -133,5 +139,18 @@ class InvalidBlockRequestTest(BitcoinTestFramework):
self.log.info("Test inflation by duplicating input")
peer.send_blocks_and_test([block4], node, success=False, reject_reason='bad-txns-inputs-duplicate')
+ self.log.info("Test accepting identical block after rejecting it due to a future timestamp.")
+ t = int(time.time())
+ node.setmocktime(t)
+ # Set block time +1 second past max future validity
+ block = create_block(tip, create_coinbase(height), t + MAX_FUTURE_BLOCK_TIME + 1)
+ block.hashMerkleRoot = block.calc_merkle_root()
+ block.solve()
+ # Need force_send because the block will get rejected without a getdata otherwise
+ peer.send_blocks_and_test([block], node, force_send=True, success=False, reject_reason='time-too-new')
+ node.setmocktime(t + 1)
+ peer.send_blocks_and_test([block], node, success=True)
+
+
if __name__ == '__main__':
InvalidBlockRequestTest().main()
diff --git a/test/functional/p2p_invalid_messages.py b/test/functional/p2p_invalid_messages.py
index 788a81d4af..9c34506320 100755
--- a/test/functional/p2p_invalid_messages.py
+++ b/test/functional/p2p_invalid_messages.py
@@ -58,6 +58,7 @@ class InvalidMessagesTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 1
self.setup_clean_chain = True
+ self.extra_args = [["-whitelist=addr@127.0.0.1"]]
def run_test(self):
self.test_buffer()
diff --git a/test/functional/p2p_leak.py b/test/functional/p2p_leak.py
index 71d5ca92b3..f1538e8ac7 100755
--- a/test/functional/p2p_leak.py
+++ b/test/functional/p2p_leak.py
@@ -29,6 +29,8 @@ from test_framework.util import (
assert_greater_than_or_equal,
)
+PEER_TIMEOUT = 3
+
class LazyPeer(P2PInterface):
def __init__(self):
@@ -98,7 +100,7 @@ class P2PVersionStore(P2PInterface):
class P2PLeakTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 1
- self.extra_args = [['-peertimeout=4']]
+ self.extra_args = [[f"-peertimeout={PEER_TIMEOUT}"]]
def create_old_version(self, nversion):
old_version_msg = msg_version()
@@ -134,7 +136,7 @@ class P2PLeakTest(BitcoinTestFramework):
self.nodes[0].generate(nblocks=1)
# Give the node enough time to possibly leak out a message
- time.sleep(5)
+ time.sleep(PEER_TIMEOUT + 2)
# Make sure only expected messages came in
assert not no_version_idle_peer.unexpected_msg
diff --git a/test/functional/p2p_leak_tx.py b/test/functional/p2p_leak_tx.py
index a45f792e81..9a4ceb86ae 100755
--- a/test/functional/p2p_leak_tx.py
+++ b/test/functional/p2p_leak_tx.py
@@ -4,6 +4,7 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test that we don't leak txs to inbound peers that we haven't yet announced to"""
+from test_framework.blocktools import COINBASE_MATURITY
from test_framework.messages import msg_getdata, CInv, MSG_TX
from test_framework.p2p import p2p_lock, P2PDataStore
from test_framework.test_framework import BitcoinTestFramework
@@ -27,7 +28,7 @@ class P2PLeakTxTest(BitcoinTestFramework):
miniwallet = MiniWallet(gen_node)
# Add enough mature utxos to the wallet, so that all txs spend confirmed coins
miniwallet.generate(1)
- gen_node.generate(100)
+ gen_node.generate(COINBASE_MATURITY)
inbound_peer = self.nodes[0].add_p2p_connection(P2PNode()) # An "attacking" inbound peer
diff --git a/test/functional/p2p_permissions.py b/test/functional/p2p_permissions.py
index 62652d949d..594a28d662 100755
--- a/test/functional/p2p_permissions.py
+++ b/test/functional/p2p_permissions.py
@@ -9,9 +9,8 @@ Test that permissions are correctly calculated and applied
from test_framework.address import ADDRESS_BCRT1_P2WSH_OP_TRUE
from test_framework.messages import (
- CTransaction,
CTxInWitness,
- FromHex,
+ tx_from_hex,
)
from test_framework.p2p import P2PDataStore
from test_framework.script import (
@@ -105,8 +104,7 @@ class P2PPermissionsTests(BitcoinTestFramework):
p2p_rebroadcast_wallet = self.nodes[1].add_p2p_connection(P2PDataStore())
self.log.debug("Send a tx from the wallet initially")
- tx = FromHex(
- CTransaction(),
+ tx = tx_from_hex(
self.nodes[0].createrawtransaction(
inputs=[{
'txid': block_op_true['tx'][0],
diff --git a/test/functional/p2p_segwit.py b/test/functional/p2p_segwit.py
index 9d32c1cb86..db96e6bdcf 100755
--- a/test/functional/p2p_segwit.py
+++ b/test/functional/p2p_segwit.py
@@ -9,11 +9,10 @@ import random
import struct
import time
-from test_framework.blocktools import create_block, create_coinbase, add_witness_commitment, get_witness_script, WITNESS_COMMITMENT_HEADER
+from test_framework.blocktools import create_block, create_coinbase, add_witness_commitment, WITNESS_COMMITMENT_HEADER
from test_framework.key import ECKey
from test_framework.messages import (
BIP125_SEQUENCE_NUMBER,
- CBlock,
CBlockHeader,
CInv,
COutPoint,
@@ -40,8 +39,7 @@ from test_framework.messages import (
ser_uint256,
ser_vector,
sha256,
- uint256_from_str,
- FromHex,
+ tx_from_hex,
)
from test_framework.p2p import (
P2PInterface,
@@ -60,12 +58,8 @@ from test_framework.script import (
OP_CHECKMULTISIG,
OP_CHECKSIG,
OP_DROP,
- OP_DUP,
OP_ELSE,
OP_ENDIF,
- OP_EQUAL,
- OP_EQUALVERIFY,
- OP_HASH160,
OP_IF,
OP_RETURN,
OP_TRUE,
@@ -77,6 +71,12 @@ from test_framework.script import (
LegacySignatureHash,
hash160,
)
+from test_framework.script_util import (
+ key_to_p2wpkh_script,
+ keyhash_to_p2pkh_script,
+ script_to_p2sh_script,
+ script_to_p2wsh_script,
+)
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
@@ -100,10 +100,6 @@ class UTXO():
self.n = n
self.nValue = value
-def get_p2pkh_script(pubkeyhash):
- """Get the script associated with a P2PKH."""
- return CScript([CScriptOp(OP_DUP), CScriptOp(OP_HASH160), pubkeyhash, CScriptOp(OP_EQUALVERIFY), CScriptOp(OP_CHECKSIG)])
-
def sign_p2pk_witness_input(script, tx_to, in_idx, hashtype, value, key):
"""Add signature for a P2PK witness program."""
tx_hash = SegwitV0SignatureHash(script, tx_to, in_idx, hashtype, value)
@@ -209,24 +205,17 @@ class TestP2PConn(P2PInterface):
class SegWitTest(BitcoinTestFramework):
def set_test_params(self):
self.setup_clean_chain = True
- self.num_nodes = 3
+ self.num_nodes = 2
# This test tests SegWit both pre and post-activation, so use the normal BIP9 activation.
self.extra_args = [
["-acceptnonstdtxn=1", "-segwitheight={}".format(SEGWIT_HEIGHT), "-whitelist=noban@127.0.0.1"],
["-acceptnonstdtxn=0", "-segwitheight={}".format(SEGWIT_HEIGHT)],
- ["-acceptnonstdtxn=1", "-segwitheight=-1"],
]
self.supports_cli = False
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
- def setup_network(self):
- self.setup_nodes()
- self.connect_nodes(0, 1)
- self.connect_nodes(0, 2)
- self.sync_all()
-
# Helper functions
def build_next_block(self, version=4):
@@ -267,7 +256,6 @@ class SegWitTest(BitcoinTestFramework):
self.test_non_witness_transaction()
self.test_v0_outputs_arent_spendable()
self.test_block_relay()
- self.test_getblocktemplate_before_lockin()
self.test_unnecessary_witness_before_segwit_activation()
self.test_witness_tx_relay_before_segwit_activation()
self.test_standardness_v0()
@@ -295,7 +283,6 @@ class SegWitTest(BitcoinTestFramework):
self.test_signature_version_1()
self.test_non_standard_witness_blinding()
self.test_non_standard_witness()
- self.test_upgrade_after_activation()
self.test_witness_sigops()
self.test_superfluous_witness()
self.test_wtxid_relay()
@@ -485,18 +472,10 @@ class SegWitTest(BitcoinTestFramework):
witness, and so can't be spent before segwit activation (the point at which
blocks are permitted to contain witnesses)."""
- # node2 doesn't need to be connected for this test.
- # (If it's connected, node0 may propagate an invalid block to it over
- # compact blocks and the nodes would have inconsistent tips.)
- self.disconnect_nodes(0, 2)
-
# Create two outputs, a p2wsh and p2sh-p2wsh
witness_program = CScript([OP_TRUE])
- witness_hash = sha256(witness_program)
- script_pubkey = CScript([OP_0, witness_hash])
-
- p2sh_pubkey = hash160(script_pubkey)
- p2sh_script_pubkey = CScript([OP_HASH160, p2sh_pubkey, OP_EQUAL])
+ script_pubkey = script_to_p2wsh_script(witness_program)
+ p2sh_script_pubkey = script_to_p2sh_script(script_pubkey)
value = self.utxo[0].nValue // 3
@@ -550,38 +529,10 @@ class SegWitTest(BitcoinTestFramework):
# TODO: support multiple acceptable reject reasons.
test_witness_block(self.nodes[0], self.test_node, block, accepted=False, with_witness=False)
- self.connect_nodes(0, 2)
-
self.utxo.pop(0)
self.utxo.append(UTXO(txid, 2, value))
@subtest # type: ignore
- def test_getblocktemplate_before_lockin(self):
- txid = int(self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 1), 16)
-
- for node in [self.nodes[0], self.nodes[2]]:
- gbt_results = node.getblocktemplate({"rules": ["segwit"]})
- if node == self.nodes[2]:
- # If this is a non-segwit node, we should not get a witness
- # commitment.
- assert 'default_witness_commitment' not in gbt_results
- else:
- # For segwit-aware nodes, check the witness
- # commitment is correct.
- assert 'default_witness_commitment' in gbt_results
- witness_commitment = gbt_results['default_witness_commitment']
-
- # Check that default_witness_commitment is present.
- witness_root = CBlock.get_merkle_root([ser_uint256(0),
- ser_uint256(txid)])
- script = get_witness_script(witness_root, 0)
- assert_equal(witness_commitment, script.hex())
-
- # Clear out the mempool
- self.nodes[0].generate(1)
- self.sync_blocks()
-
- @subtest # type: ignore
def test_witness_tx_relay_before_segwit_activation(self):
# Generate a transaction that doesn't require a witness, but send it
@@ -631,11 +582,8 @@ class SegWitTest(BitcoinTestFramework):
V0 segwit inputs may only be mined after activation, but not before."""
witness_program = CScript([OP_TRUE])
- witness_hash = sha256(witness_program)
- script_pubkey = CScript([OP_0, witness_hash])
-
- p2sh_pubkey = hash160(witness_program)
- p2sh_script_pubkey = CScript([OP_HASH160, p2sh_pubkey, OP_EQUAL])
+ script_pubkey = script_to_p2wsh_script(witness_program)
+ p2sh_script_pubkey = script_to_p2sh_script(witness_program)
# First prepare a p2sh output (so that spending it will pass standardness)
p2sh_tx = CTransaction()
@@ -662,6 +610,7 @@ class SegWitTest(BitcoinTestFramework):
test_transaction_acceptance(self.nodes[1], self.std_node, tx, with_witness=True, accepted=True)
# Now create something that looks like a P2PKH output. This won't be spendable.
+ witness_hash = sha256(witness_program)
script_pubkey = CScript([OP_0, hash160(witness_hash)])
tx2 = CTransaction()
# tx was accepted, so we spend the second output.
@@ -740,10 +689,8 @@ class SegWitTest(BitcoinTestFramework):
# Prepare the p2sh-wrapped witness output
witness_program = CScript([OP_DROP, OP_TRUE])
- witness_hash = sha256(witness_program)
- p2wsh_pubkey = CScript([OP_0, witness_hash])
- p2sh_witness_hash = hash160(p2wsh_pubkey)
- script_pubkey = CScript([OP_HASH160, p2sh_witness_hash, OP_EQUAL])
+ p2wsh_pubkey = script_to_p2wsh_script(witness_program)
+ script_pubkey = script_to_p2sh_script(p2wsh_pubkey)
script_sig = CScript([p2wsh_pubkey]) # a push of the redeem script
# Fund the P2SH output
@@ -837,8 +784,7 @@ class SegWitTest(BitcoinTestFramework):
# Let's construct a witness program
witness_program = CScript([OP_TRUE])
- witness_hash = sha256(witness_program)
- script_pubkey = CScript([OP_0, witness_hash])
+ script_pubkey = script_to_p2wsh_script(witness_program)
tx.vout.append(CTxOut(self.utxo[0].nValue - 1000, script_pubkey))
tx.rehash()
@@ -951,8 +897,7 @@ class SegWitTest(BitcoinTestFramework):
NUM_OUTPUTS = 50
witness_program = CScript([OP_2DROP] * NUM_DROPS + [OP_TRUE])
- witness_hash = uint256_from_str(sha256(witness_program))
- script_pubkey = CScript([OP_0, ser_uint256(witness_hash)])
+ script_pubkey = script_to_p2wsh_script(witness_program)
prevout = COutPoint(self.utxo[0].sha256, self.utxo[0].n)
value = self.utxo[0].nValue
@@ -1054,8 +999,7 @@ class SegWitTest(BitcoinTestFramework):
block = self.build_next_block()
witness_program = CScript([OP_DROP, OP_TRUE])
- witness_hash = sha256(witness_program)
- script_pubkey = CScript([OP_0, witness_hash])
+ script_pubkey = script_to_p2wsh_script(witness_program)
# First try extra witness data on a tx that doesn't require a witness
tx = CTransaction()
@@ -1127,8 +1071,7 @@ class SegWitTest(BitcoinTestFramework):
block = self.build_next_block()
witness_program = CScript([OP_DROP, OP_TRUE])
- witness_hash = sha256(witness_program)
- script_pubkey = CScript([OP_0, witness_hash])
+ script_pubkey = script_to_p2wsh_script(witness_program)
tx = CTransaction()
tx.vin.append(CTxIn(COutPoint(self.utxo[0].sha256, self.utxo[0].n), b""))
@@ -1166,8 +1109,7 @@ class SegWitTest(BitcoinTestFramework):
# This program is 19 max pushes (9937 bytes), then 64 more opcode-bytes.
long_witness_program = CScript([b'a' * MAX_SCRIPT_ELEMENT_SIZE] * 19 + [OP_DROP] * 63 + [OP_TRUE])
assert len(long_witness_program) == MAX_PROGRAM_LENGTH + 1
- long_witness_hash = sha256(long_witness_program)
- long_script_pubkey = CScript([OP_0, long_witness_hash])
+ long_script_pubkey = script_to_p2wsh_script(long_witness_program)
block = self.build_next_block()
@@ -1190,8 +1132,7 @@ class SegWitTest(BitcoinTestFramework):
# Try again with one less byte in the witness program
witness_program = CScript([b'a' * MAX_SCRIPT_ELEMENT_SIZE] * 19 + [OP_DROP] * 62 + [OP_TRUE])
assert len(witness_program) == MAX_PROGRAM_LENGTH
- witness_hash = sha256(witness_program)
- script_pubkey = CScript([OP_0, witness_hash])
+ script_pubkey = script_to_p2wsh_script(witness_program)
tx.vout[0] = CTxOut(tx.vout[0].nValue, script_pubkey)
tx.rehash()
@@ -1210,8 +1151,7 @@ class SegWitTest(BitcoinTestFramework):
"""Test that vin length must match vtxinwit length."""
witness_program = CScript([OP_DROP, OP_TRUE])
- witness_hash = sha256(witness_program)
- script_pubkey = CScript([OP_0, witness_hash])
+ script_pubkey = script_to_p2wsh_script(witness_program)
# Create a transaction that splits our utxo into many outputs
tx = CTransaction()
@@ -1318,8 +1258,7 @@ class SegWitTest(BitcoinTestFramework):
# Now try to add extra witness data to a valid witness tx.
witness_program = CScript([OP_TRUE])
- witness_hash = sha256(witness_program)
- script_pubkey = CScript([OP_0, witness_hash])
+ script_pubkey = script_to_p2wsh_script(witness_program)
tx2 = CTransaction()
tx2.vin.append(CTxIn(COutPoint(tx_hash, 0), b""))
tx2.vout.append(CTxOut(tx.vout[0].nValue - 1000, script_pubkey))
@@ -1331,9 +1270,8 @@ class SegWitTest(BitcoinTestFramework):
# Add too-large for IsStandard witness and check that it does not enter reject filter
p2sh_program = CScript([OP_TRUE])
- p2sh_pubkey = hash160(p2sh_program)
witness_program2 = CScript([b'a' * 400000])
- tx3.vout.append(CTxOut(tx2.vout[0].nValue - 1000, CScript([OP_HASH160, p2sh_pubkey, OP_EQUAL])))
+ tx3.vout.append(CTxOut(tx2.vout[0].nValue - 1000, script_to_p2sh_script(p2sh_program)))
tx3.wit.vtxinwit[0].scriptWitness.stack = [witness_program2]
tx3.rehash()
@@ -1482,8 +1420,7 @@ class SegWitTest(BitcoinTestFramework):
block = self.build_next_block()
# Change the output of the block to be a witness output.
witness_program = CScript([OP_TRUE])
- witness_hash = sha256(witness_program)
- script_pubkey = CScript([OP_0, witness_hash])
+ script_pubkey = script_to_p2wsh_script(witness_program)
block.vtx[0].vout[0].scriptPubKey = script_pubkey
# This next line will rehash the coinbase and update the merkle
# root, and solve.
@@ -1530,7 +1467,7 @@ class SegWitTest(BitcoinTestFramework):
# Test 1: P2WPKH
# First create a P2WPKH output that uses an uncompressed pubkey
pubkeyhash = hash160(pubkey)
- script_pkh = CScript([OP_0, pubkeyhash])
+ script_pkh = key_to_p2wpkh_script(pubkey)
tx = CTransaction()
tx.vin.append(CTxIn(COutPoint(utxo.sha256, utxo.n), b""))
tx.vout.append(CTxOut(utxo.nValue - 1000, script_pkh))
@@ -1544,13 +1481,12 @@ class SegWitTest(BitcoinTestFramework):
# Now try to spend it. Send it to a P2WSH output, which we'll
# use in the next test.
witness_program = CScript([pubkey, CScriptOp(OP_CHECKSIG)])
- witness_hash = sha256(witness_program)
- script_wsh = CScript([OP_0, witness_hash])
+ script_wsh = script_to_p2wsh_script(witness_program)
tx2 = CTransaction()
tx2.vin.append(CTxIn(COutPoint(tx.sha256, 0), b""))
tx2.vout.append(CTxOut(tx.vout[0].nValue - 1000, script_wsh))
- script = get_p2pkh_script(pubkeyhash)
+ script = keyhash_to_p2pkh_script(pubkeyhash)
sig_hash = SegwitV0SignatureHash(script, tx2, 0, SIGHASH_ALL, tx.vout[0].nValue)
signature = key.sign_ecdsa(sig_hash) + b'\x01' # 0x1 is SIGHASH_ALL
tx2.wit.vtxinwit.append(CTxInWitness())
@@ -1567,8 +1503,7 @@ class SegWitTest(BitcoinTestFramework):
# Test 2: P2WSH
# Try to spend the P2WSH output created in last test.
# Send it to a P2SH(P2WSH) output, which we'll use in the next test.
- p2sh_witness_hash = hash160(script_wsh)
- script_p2sh = CScript([OP_HASH160, p2sh_witness_hash, OP_EQUAL])
+ script_p2sh = script_to_p2sh_script(script_wsh)
script_sig = CScript([script_wsh])
tx3 = CTransaction()
@@ -1587,7 +1522,7 @@ class SegWitTest(BitcoinTestFramework):
# Test 3: P2SH(P2WSH)
# Try to spend the P2SH output created in the last test.
# Send it to a P2PKH output, which we'll use in the next test.
- script_pubkey = get_p2pkh_script(pubkeyhash)
+ script_pubkey = keyhash_to_p2pkh_script(pubkeyhash)
tx4 = CTransaction()
tx4.vin.append(CTxIn(COutPoint(tx3.sha256, 0), script_sig))
tx4.vout.append(CTxOut(tx3.vout[0].nValue - 1000, script_pubkey))
@@ -1624,8 +1559,7 @@ class SegWitTest(BitcoinTestFramework):
pubkey = key.get_pubkey().get_bytes()
witness_program = CScript([pubkey, CScriptOp(OP_CHECKSIG)])
- witness_hash = sha256(witness_program)
- script_pubkey = CScript([OP_0, witness_hash])
+ script_pubkey = script_to_p2wsh_script(witness_program)
# First create a witness output for use in the tests.
tx = CTransaction()
@@ -1744,7 +1678,7 @@ class SegWitTest(BitcoinTestFramework):
# Now test witness version 0 P2PKH transactions
pubkeyhash = hash160(pubkey)
- script_pkh = CScript([OP_0, pubkeyhash])
+ script_pkh = key_to_p2wpkh_script(pubkey)
tx = CTransaction()
tx.vin.append(CTxIn(COutPoint(temp_utxos[0].sha256, temp_utxos[0].n), b""))
tx.vout.append(CTxOut(temp_utxos[0].nValue, script_pkh))
@@ -1754,7 +1688,7 @@ class SegWitTest(BitcoinTestFramework):
tx2.vin.append(CTxIn(COutPoint(tx.sha256, 0), b""))
tx2.vout.append(CTxOut(tx.vout[0].nValue, CScript([OP_TRUE])))
- script = get_p2pkh_script(pubkeyhash)
+ script = keyhash_to_p2pkh_script(pubkeyhash)
sig_hash = SegwitV0SignatureHash(script, tx2, 0, SIGHASH_ALL, tx.vout[0].nValue)
signature = key.sign_ecdsa(sig_hash) + b'\x01' # 0x1 is SIGHASH_ALL
@@ -1806,8 +1740,7 @@ class SegWitTest(BitcoinTestFramework):
# rules (an anyone-can-spend OP_TRUE would be rejected, if not wrapped
# in P2SH).
p2sh_program = CScript([OP_TRUE])
- p2sh_pubkey = hash160(p2sh_program)
- script_pubkey = CScript([OP_HASH160, p2sh_pubkey, OP_EQUAL])
+ script_pubkey = script_to_p2sh_script(p2sh_program)
# Now check that unnecessary witnesses can't be used to blind a node
# to a transaction, eg by violating standardness checks.
@@ -1872,11 +1805,10 @@ class SegWitTest(BitcoinTestFramework):
# For each script, generate a pair of P2WSH and P2SH-P2WSH output.
outputvalue = (self.utxo[0].nValue - 1000) // (len(scripts) * 2)
for i in scripts:
- p2wsh = CScript([OP_0, sha256(i)])
- p2sh = hash160(p2wsh)
+ p2wsh = script_to_p2wsh_script(i)
p2wsh_scripts.append(p2wsh)
tx.vout.append(CTxOut(outputvalue, p2wsh))
- tx.vout.append(CTxOut(outputvalue, CScript([OP_HASH160, p2sh, OP_EQUAL])))
+ tx.vout.append(CTxOut(outputvalue, script_to_p2sh_script(p2wsh)))
tx.rehash()
txid = tx.sha256
test_transaction_acceptance(self.nodes[0], self.test_node, tx, with_witness=False, accepted=True)
@@ -1890,13 +1822,13 @@ class SegWitTest(BitcoinTestFramework):
for i in range(len(scripts)):
p2wsh_tx = CTransaction()
p2wsh_tx.vin.append(CTxIn(COutPoint(txid, i * 2)))
- p2wsh_tx.vout.append(CTxOut(outputvalue - 5000, CScript([OP_0, hash160(hex_str_to_bytes(""))])))
+ p2wsh_tx.vout.append(CTxOut(outputvalue - 5000, CScript([OP_0, hash160(b"")])))
p2wsh_tx.wit.vtxinwit.append(CTxInWitness())
p2wsh_tx.rehash()
p2wsh_txs.append(p2wsh_tx)
p2sh_tx = CTransaction()
p2sh_tx.vin.append(CTxIn(COutPoint(txid, i * 2 + 1), CScript([p2wsh_scripts[i]])))
- p2sh_tx.vout.append(CTxOut(outputvalue - 5000, CScript([OP_0, hash160(hex_str_to_bytes(""))])))
+ p2sh_tx.vout.append(CTxOut(outputvalue - 5000, CScript([OP_0, hash160(b"")])))
p2sh_tx.wit.vtxinwit.append(CTxInWitness())
p2sh_tx.rehash()
p2sh_txs.append(p2sh_tx)
@@ -1953,46 +1885,12 @@ class SegWitTest(BitcoinTestFramework):
self.utxo.pop(0)
@subtest # type: ignore
- def test_upgrade_after_activation(self):
- """Test the behavior of starting up a segwit-aware node after the softfork has activated."""
-
- # All nodes are caught up and node 2 is a pre-segwit node that will soon upgrade.
- for n in range(2):
- assert_equal(self.nodes[n].getblockcount(), self.nodes[2].getblockcount())
- assert softfork_active(self.nodes[n], "segwit")
- assert SEGWIT_HEIGHT < self.nodes[2].getblockcount()
- assert 'segwit' not in self.nodes[2].getblockchaininfo()['softforks']
-
- # Restarting node 2 should result in a shutdown because the blockchain consists of
- # insufficiently validated blocks per segwit consensus rules.
- self.stop_node(2)
- self.nodes[2].assert_start_raises_init_error(
- extra_args=[f"-segwitheight={SEGWIT_HEIGHT}"],
- expected_msg=f": Witness data for blocks after height {SEGWIT_HEIGHT} requires validation. Please restart with -reindex..\nPlease restart with -reindex or -reindex-chainstate to recover.",
- )
-
- # As directed, the user restarts the node with -reindex
- self.start_node(2, extra_args=["-reindex", f"-segwitheight={SEGWIT_HEIGHT}"])
-
- # With the segwit consensus rules, the node is able to validate only up to SEGWIT_HEIGHT - 1
- assert_equal(self.nodes[2].getblockcount(), SEGWIT_HEIGHT - 1)
- self.connect_nodes(0, 2)
-
- # We reconnect more than 100 blocks, give it plenty of time
- # sync_blocks() also verifies the best block hash is the same for all nodes
- self.sync_blocks(timeout=240)
-
- # The upgraded node should now have segwit activated
- assert softfork_active(self.nodes[2], "segwit")
-
- @subtest # type: ignore
def test_witness_sigops(self):
"""Test sigop counting is correct inside witnesses."""
# Keep this under MAX_OPS_PER_SCRIPT (201)
witness_program = CScript([OP_TRUE, OP_IF, OP_TRUE, OP_ELSE] + [OP_CHECKMULTISIG] * 5 + [OP_CHECKSIG] * 193 + [OP_ENDIF])
- witness_hash = sha256(witness_program)
- script_pubkey = CScript([OP_0, witness_hash])
+ script_pubkey = script_to_p2wsh_script(witness_program)
sigops_per_script = 20 * 5 + 193 * 1
# We'll produce 2 extra outputs, one with a program that would take us
@@ -2008,14 +1906,12 @@ class SegWitTest(BitcoinTestFramework):
# N(=MAX_SIGOP_COST//sigops_per_script) outputs of our transaction,
# would push us just over the block sigop limit.
witness_program_toomany = CScript([OP_TRUE, OP_IF, OP_TRUE, OP_ELSE] + [OP_CHECKSIG] * (extra_sigops_available + 1) + [OP_ENDIF])
- witness_hash_toomany = sha256(witness_program_toomany)
- script_pubkey_toomany = CScript([OP_0, witness_hash_toomany])
+ script_pubkey_toomany = script_to_p2wsh_script(witness_program_toomany)
# If we spend this script instead, we would exactly reach our sigop
# limit (for witness sigops).
witness_program_justright = CScript([OP_TRUE, OP_IF, OP_TRUE, OP_ELSE] + [OP_CHECKSIG] * (extra_sigops_available) + [OP_ENDIF])
- witness_hash_justright = sha256(witness_program_justright)
- script_pubkey_justright = CScript([OP_0, witness_hash_justright])
+ script_pubkey_justright = script_to_p2wsh_script(witness_program_justright)
# First split our available utxo into a bunch of outputs
split_value = self.utxo[0].nValue // outputs
@@ -2122,14 +2018,14 @@ class SegWitTest(BitcoinTestFramework):
unspent = next(u for u in self.nodes[0].listunspent() if u['spendable'] and u['address'].startswith('bcrt'))
raw = self.nodes[0].createrawtransaction([{"txid": unspent['txid'], "vout": unspent['vout']}], {self.nodes[0].getnewaddress(): 1})
- tx = FromHex(CTransaction(), raw)
+ tx = tx_from_hex(raw)
assert_raises_rpc_error(-22, "TX decode failed", self.nodes[0].decoderawtransaction, hexstring=serialize_with_bogus_witness(tx).hex(), iswitness=True)
with self.nodes[0].assert_debug_log(['Superfluous witness record']):
self.test_node.send_and_ping(msg_bogus_tx(tx))
raw = self.nodes[0].signrawtransactionwithwallet(raw)
assert raw['complete']
raw = raw['hex']
- tx = FromHex(CTransaction(), raw)
+ tx = tx_from_hex(raw)
assert_raises_rpc_error(-22, "TX decode failed", self.nodes[0].decoderawtransaction, hexstring=serialize_with_bogus_witness(tx).hex(), iswitness=True)
with self.nodes[0].assert_debug_log(['Unknown transaction optional data']):
self.test_node.send_and_ping(msg_bogus_tx(tx))
@@ -2148,8 +2044,7 @@ class SegWitTest(BitcoinTestFramework):
# Create a Segwit output from the latest UTXO
# and announce it to the network
witness_program = CScript([OP_TRUE])
- witness_hash = sha256(witness_program)
- script_pubkey = CScript([OP_0, witness_hash])
+ script_pubkey = script_to_p2wsh_script(witness_program)
tx = CTransaction()
tx.vin.append(CTxIn(COutPoint(self.utxo[0].sha256, self.utxo[0].n), b""))
diff --git a/test/functional/p2p_tx_download.py b/test/functional/p2p_tx_download.py
index 4bf96cb0e6..3e962b4450 100755
--- a/test/functional/p2p_tx_download.py
+++ b/test/functional/p2p_tx_download.py
@@ -8,13 +8,12 @@ Test transaction download behavior
from test_framework.messages import (
CInv,
- CTransaction,
- FromHex,
MSG_TX,
MSG_TYPE_MASK,
MSG_WTX,
msg_inv,
msg_notfound,
+ tx_from_hex,
)
from test_framework.p2p import (
P2PInterface,
@@ -100,7 +99,7 @@ class TxDownloadTest(BitcoinTestFramework):
hexstring=tx,
privkeys=[self.nodes[0].get_deterministic_priv_key().key],
)['hex']
- ctx = FromHex(CTransaction(), tx)
+ ctx = tx_from_hex(tx)
txid = int(ctx.rehash(), 16)
self.log.info(
diff --git a/test/functional/rpc_addresses_deprecation.py b/test/functional/rpc_addresses_deprecation.py
index bc0559f3b5..ac430f5b39 100755
--- a/test/functional/rpc_addresses_deprecation.py
+++ b/test/functional/rpc_addresses_deprecation.py
@@ -4,9 +4,9 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test deprecation of reqSigs and addresses RPC fields."""
-from io import BytesIO
-
-from test_framework.messages import CTransaction
+from test_framework.messages import (
+ tx_from_hex,
+)
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
@@ -35,8 +35,7 @@ class AddressesDeprecationTest(BitcoinTestFramework):
signed = node.signrawtransactionwithwallet(raw)['hex']
# This transaction is derived from test/util/data/txcreatemultisig1.json
- tx = CTransaction()
- tx.deserialize(BytesIO(hex_str_to_bytes(signed)))
+ tx = tx_from_hex(signed)
tx.vout[0].scriptPubKey = hex_str_to_bytes("522102a5613bd857b7048924264d1e70e08fb2a7e6527d32b7ab1bb993ac59964ff39721021ac43c7ff740014c3b33737ede99c967e4764553d1b2b83db77c83b8715fa72d2102df2089105c77f266fa11a9d33f05c735234075f2e8780824c6b709415f9fb48553ae")
tx_signed = node.signrawtransactionwithwallet(tx.serialize().hex())['hex']
txid = node.sendrawtransaction(hexstring=tx_signed, maxfeerate=0)
diff --git a/test/functional/rpc_blockchain.py b/test/functional/rpc_blockchain.py
index 00324347ed..f7290ff229 100755
--- a/test/functional/rpc_blockchain.py
+++ b/test/functional/rpc_blockchain.py
@@ -31,7 +31,7 @@ from test_framework.blocktools import (
)
from test_framework.messages import (
CBlockHeader,
- FromHex,
+ from_hex,
msg_block,
)
from test_framework.p2p import P2PInterface
@@ -93,11 +93,14 @@ class BlockchainTest(BitcoinTestFramework):
'pruned',
'size_on_disk',
'softforks',
+ 'time',
'verificationprogress',
'warnings',
]
res = self.nodes[0].getblockchaininfo()
+ assert isinstance(res['time'], int)
+
# result should have these additional pruning keys if manual pruning is enabled
assert_equal(sorted(res.keys()), sorted(['pruneheight', 'automatic_pruning'] + keys))
@@ -314,7 +317,7 @@ class BlockchainTest(BitcoinTestFramework):
header_hex = node.getblockheader(blockhash=besthash, verbose=False)
assert_is_hex_string(header_hex)
- header = FromHex(CBlockHeader(), header_hex)
+ header = from_hex(CBlockHeader(), header_hex)
header.calc_sha256()
assert_equal(header.hash, besthash)
diff --git a/test/functional/rpc_createmultisig.py b/test/functional/rpc_createmultisig.py
index 19f0d5765a..816ec67492 100755
--- a/test/functional/rpc_createmultisig.py
+++ b/test/functional/rpc_createmultisig.py
@@ -9,6 +9,7 @@ import itertools
import json
import os
+from test_framework.blocktools import COINBASE_MATURITY
from test_framework.authproxy import JSONRPCException
from test_framework.descriptors import descsum_create, drop_origins
from test_framework.key import ECPubKey, ECKey
@@ -96,6 +97,9 @@ class RpcCreateMultiSigTest(BitcoinTestFramework):
sorted_key_desc = descsum_create('sh(multi(2,{}))'.format(sorted_key_str))
assert_equal(self.nodes[0].deriveaddresses(sorted_key_desc)[0], t['address'])
+ # Check that bech32m is currently not allowed
+ assert_raises_rpc_error(-5, "createmultisig cannot create bech32m multisig addresses", self.nodes[0].createmultisig, 2, self.pub, "bech32m")
+
def check_addmultisigaddress_errors(self):
if self.options.descriptors:
return
@@ -107,9 +111,13 @@ class RpcCreateMultiSigTest(BitcoinTestFramework):
self.nodes[0].importaddress(a)
assert_raises_rpc_error(-5, 'no full public key for address', lambda: self.nodes[0].addmultisigaddress(nrequired=1, keys=addresses))
+ # Bech32m address type is disallowed for legacy wallets
+ pubs = [self.nodes[1].getaddressinfo(addr)["pubkey"] for addr in addresses]
+ assert_raises_rpc_error(-5, "Bech32m multisig addresses cannot be created with legacy wallets", self.nodes[0].addmultisigaddress, 2, pubs, "", "bech32m")
+
def checkbalances(self):
node0, node1, node2 = self.nodes
- node0.generate(100)
+ node0.generate(COINBASE_MATURITY)
self.sync_all()
bal0 = node0.getbalance()
diff --git a/test/functional/rpc_decodescript.py b/test/functional/rpc_decodescript.py
index 01b8cb1854..f6643c7167 100755
--- a/test/functional/rpc_decodescript.py
+++ b/test/functional/rpc_decodescript.py
@@ -4,11 +4,16 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test decoding scripts via decodescript RPC command."""
-from test_framework.messages import CTransaction, sha256
+from test_framework.messages import (
+ sha256,
+ tx_from_hex,
+)
from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import assert_equal, hex_str_to_bytes
+from test_framework.util import (
+ assert_equal,
+ hex_str_to_bytes,
+)
-from io import BytesIO
class DecodeScriptTest(BitcoinTestFramework):
def set_test_params(self):
@@ -179,8 +184,7 @@ class DecodeScriptTest(BitcoinTestFramework):
assert_equal('0 3045022100ae3b4e589dfc9d48cb82d41008dc5fa6a86f94d5c54f9935531924602730ab8002202f88cf464414c4ed9fa11b773c5ee944f66e9b05cc1e51d97abc22ce098937ea[ALL] 3045022100b44883be035600e9328a01b66c7d8439b74db64187e76b99a68f7893b701d5380220225bf286493e4c4adcf928c40f785422572eb232f84a0b83b0dea823c3a19c75[ALL] 5221020743d44be989540d27b1b4bbbcfd17721c337cb6bc9af20eb8a32520b393532f2102c0120a1dda9e51a938d39ddd9fe0ebc45ea97e1d27a7cbd671d5431416d3dd87210213820eb3d5f509d7438c9eeecb4157b2f595105e7cd564b3cdbb9ead3da41eed53ae', rpc_result['vin'][0]['scriptSig']['asm'])
assert_equal('OP_DUP OP_HASH160 dc863734a218bfe83ef770ee9d41a27f824a6e56 OP_EQUALVERIFY OP_CHECKSIG', rpc_result['vout'][0]['scriptPubKey']['asm'])
assert_equal('OP_HASH160 2a5edea39971049a540474c6a99edf0aa4074c58 OP_EQUAL', rpc_result['vout'][1]['scriptPubKey']['asm'])
- txSave = CTransaction()
- txSave.deserialize(BytesIO(hex_str_to_bytes(tx)))
+ txSave = tx_from_hex(tx)
# make sure that a specifically crafted op_return value will not pass all the IsDERSignature checks and then get decoded as a sighash type
tx = '01000000015ded05872fdbda629c7d3d02b194763ce3b9b1535ea884e3c8e765d42e316724020000006b48304502204c10d4064885c42638cbff3585915b322de33762598321145ba033fc796971e2022100bb153ad3baa8b757e30a2175bd32852d2e1cb9080f84d7e32fcdfd667934ef1b012103163c0ff73511ea1743fb5b98384a2ff09dd06949488028fd819f4d83f56264efffffffff0200000000000000000b6a0930060201000201000180380100000000001976a9141cabd296e753837c086da7a45a6c2fe0d49d7b7b88ac00000000'
diff --git a/test/functional/rpc_dumptxoutset.py b/test/functional/rpc_dumptxoutset.py
index dc469ba552..3efbdab013 100755
--- a/test/functional/rpc_dumptxoutset.py
+++ b/test/functional/rpc_dumptxoutset.py
@@ -4,6 +4,8 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test the generation of UTXO snapshots using `dumptxoutset`.
"""
+
+from test_framework.blocktools import COINBASE_MATURITY
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_equal, assert_raises_rpc_error
@@ -21,7 +23,7 @@ class DumptxoutsetTest(BitcoinTestFramework):
node = self.nodes[0]
mocktime = node.getblockheader(node.getblockhash(0))['time'] + 1
node.setmocktime(mocktime)
- node.generate(100)
+ node.generate(COINBASE_MATURITY)
FILENAME = 'txoutset.dat'
out = node.dumptxoutset(FILENAME)
diff --git a/test/functional/rpc_fundrawtransaction.py b/test/functional/rpc_fundrawtransaction.py
index 4b07a32c54..fa98c44152 100755
--- a/test/functional/rpc_fundrawtransaction.py
+++ b/test/functional/rpc_fundrawtransaction.py
@@ -551,7 +551,7 @@ class RawTransactionsTest(BitcoinTestFramework):
# creating the key must be impossible because the wallet is locked
outputs = {self.nodes[0].getnewaddress():1.1}
rawtx = self.nodes[1].createrawtransaction(inputs, outputs)
- assert_raises_rpc_error(-4, "Transaction needs a change address, but we can't generate it. Please call keypoolrefill first.", self.nodes[1].fundrawtransaction, rawtx)
+ assert_raises_rpc_error(-4, "Transaction needs a change address, but we can't generate it.", self.nodes[1].fundrawtransaction, rawtx)
# Refill the keypool.
self.nodes[1].walletpassphrase("test", 100)
diff --git a/test/functional/rpc_getblockstats.py b/test/functional/rpc_getblockstats.py
index 57794ae973..4af518c870 100755
--- a/test/functional/rpc_getblockstats.py
+++ b/test/functional/rpc_getblockstats.py
@@ -6,6 +6,8 @@
#
# Test getblockstats rpc call
#
+
+from test_framework.blocktools import COINBASE_MATURITY
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
@@ -41,7 +43,7 @@ class GetblockstatsTest(BitcoinTestFramework):
def generate_test_data(self, filename):
mocktime = 1525107225
self.nodes[0].setmocktime(mocktime)
- self.nodes[0].generate(101)
+ self.nodes[0].generate(COINBASE_MATURITY + 1)
address = self.nodes[0].get_deterministic_priv_key().address
self.nodes[0].sendtoaddress(address=address, amount=10, subtractfeefromamount=True)
diff --git a/test/functional/rpc_misc.py b/test/functional/rpc_misc.py
index 52c8fa883d..563f2ea43e 100755
--- a/test/functional/rpc_misc.py
+++ b/test/functional/rpc_misc.py
@@ -54,13 +54,27 @@ class RpcMiscTest(BitcoinTestFramework):
assert_raises_rpc_error(-8, "unknown mode foobar", node.getmemoryinfo, mode="foobar")
- self.log.info("test logging")
+ self.log.info("test logging rpc and help")
+
+ # Test logging RPC returns the expected number of logging categories.
+ assert_equal(len(node.logging()), 24)
+
+ # Test toggling a logging category on/off/on with the logging RPC.
assert_equal(node.logging()['qt'], True)
node.logging(exclude=['qt'])
assert_equal(node.logging()['qt'], False)
node.logging(include=['qt'])
assert_equal(node.logging()['qt'], True)
+ # Test logging RPC returns the logging categories in alphabetical order.
+ sorted_logging_categories = sorted(node.logging())
+ assert_equal(list(node.logging()), sorted_logging_categories)
+
+ # Test logging help returns the logging categories string in alphabetical order.
+ categories = ', '.join(sorted_logging_categories)
+ logging_help = self.nodes[0].help('logging')
+ assert f"valid logging categories are: {categories}" in logging_help
+
self.log.info("test echoipc (testing spawned process in multiprocess build)")
assert_equal(node.echoipc("hello"), "hello")
diff --git a/test/functional/rpc_net.py b/test/functional/rpc_net.py
index 2a58f8b3f7..6e5ef770d1 100755
--- a/test/functional/rpc_net.py
+++ b/test/functional/rpc_net.py
@@ -11,6 +11,7 @@ from decimal import Decimal
from itertools import product
import time
+from test_framework.blocktools import COINBASE_MATURITY
from test_framework.p2p import P2PInterface
import test_framework.messages
from test_framework.messages import (
@@ -53,7 +54,7 @@ class NetTest(BitcoinTestFramework):
self.wallet = MiniWallet(self.nodes[0])
self.wallet.generate(1)
# Get out of IBD for the minfeefilter and getpeerinfo tests.
- self.nodes[0].generate(101)
+ self.nodes[0].generate(COINBASE_MATURITY + 1)
# By default, the test framework sets up an addnode connection from
# node 1 --> node0. By connecting node0 --> node 1, we're left with
@@ -69,6 +70,7 @@ class NetTest(BitcoinTestFramework):
self.test_getaddednodeinfo()
self.test_service_flags()
self.test_getnodeaddresses()
+ self.test_addpeeraddress()
def test_connection_count(self):
self.log.info("Test getconnectioncount")
@@ -236,6 +238,30 @@ class NetTest(BitcoinTestFramework):
assert_raises_rpc_error(-8, "Address count out of range", self.nodes[0].getnodeaddresses, -1)
assert_raises_rpc_error(-8, "Network not recognized: Foo", self.nodes[0].getnodeaddresses, 1, "Foo")
+ def test_addpeeraddress(self):
+ self.log.info("Test addpeeraddress")
+ node = self.nodes[1]
+
+ self.log.debug("Test that addpeerinfo is a hidden RPC")
+ # It is hidden from general help, but its detailed help may be called directly.
+ assert "addpeerinfo" not in node.help()
+ assert "addpeerinfo" in node.help("addpeerinfo")
+
+ self.log.debug("Test that adding an empty address fails")
+ assert_equal(node.addpeeraddress(address="", port=8333), {"success": False})
+ assert_equal(node.getnodeaddresses(count=0), [])
+
+ self.log.debug("Test that adding a valid address succeeds")
+ assert_equal(node.addpeeraddress(address="1.2.3.4", port=8333), {"success": True})
+ addrs = node.getnodeaddresses(count=0)
+ assert_equal(len(addrs), 1)
+ assert_equal(addrs[0]["address"], "1.2.3.4")
+ assert_equal(addrs[0]["port"], 8333)
+
+ self.log.debug("Test that adding the same address again when already present fails")
+ assert_equal(node.addpeeraddress(address="1.2.3.4", port=8333), {"success": False})
+ assert_equal(len(node.getnodeaddresses(count=0)), 1)
+
if __name__ == '__main__':
NetTest().main()
diff --git a/test/functional/rpc_packages.py b/test/functional/rpc_packages.py
new file mode 100755
index 0000000000..4b2ed20958
--- /dev/null
+++ b/test/functional/rpc_packages.py
@@ -0,0 +1,351 @@
+#!/usr/bin/env python3
+# Copyright (c) 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.
+"""RPCs that handle raw transaction packages."""
+
+from decimal import Decimal
+import random
+
+from test_framework.address import ADDRESS_BCRT1_P2WSH_OP_TRUE
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.messages import (
+ BIP125_SEQUENCE_NUMBER,
+ COIN,
+ CTxInWitness,
+ tx_from_hex,
+)
+from test_framework.script import (
+ CScript,
+ OP_TRUE,
+)
+from test_framework.util import (
+ assert_equal,
+)
+
+class RPCPackagesTest(BitcoinTestFramework):
+ def set_test_params(self):
+ self.num_nodes = 1
+ self.setup_clean_chain = True
+
+ def assert_testres_equal(self, package_hex, testres_expected):
+ """Shuffle package_hex and assert that the testmempoolaccept result matches testres_expected. This should only
+ be used to test packages where the order does not matter. The ordering of transactions in package_hex and
+ testres_expected must match.
+ """
+ shuffled_indeces = list(range(len(package_hex)))
+ random.shuffle(shuffled_indeces)
+ shuffled_package = [package_hex[i] for i in shuffled_indeces]
+ shuffled_testres = [testres_expected[i] for i in shuffled_indeces]
+ assert_equal(shuffled_testres, self.nodes[0].testmempoolaccept(shuffled_package))
+
+ def run_test(self):
+ self.log.info("Generate blocks to create UTXOs")
+ node = self.nodes[0]
+ self.privkeys = [node.get_deterministic_priv_key().key]
+ self.address = node.get_deterministic_priv_key().address
+ self.coins = []
+ # The last 100 coinbase transactions are premature
+ for b in node.generatetoaddress(200, self.address)[:100]:
+ coinbase = node.getblock(blockhash=b, verbosity=2)["tx"][0]
+ self.coins.append({
+ "txid": coinbase["txid"],
+ "amount": coinbase["vout"][0]["value"],
+ "scriptPubKey": coinbase["vout"][0]["scriptPubKey"],
+ })
+
+ # Create some transactions that can be reused throughout the test. Never submit these to mempool.
+ self.independent_txns_hex = []
+ self.independent_txns_testres = []
+ for _ in range(3):
+ coin = self.coins.pop()
+ rawtx = node.createrawtransaction([{"txid": coin["txid"], "vout": 0}],
+ {self.address : coin["amount"] - Decimal("0.0001")})
+ signedtx = node.signrawtransactionwithkey(hexstring=rawtx, privkeys=self.privkeys)
+ assert signedtx["complete"]
+ testres = node.testmempoolaccept([signedtx["hex"]])
+ assert testres[0]["allowed"]
+ self.independent_txns_hex.append(signedtx["hex"])
+ # testmempoolaccept returns a list of length one, avoid creating a 2D list
+ self.independent_txns_testres.append(testres[0])
+ self.independent_txns_testres_blank = [{
+ "txid": res["txid"], "wtxid": res["wtxid"]} for res in self.independent_txns_testres]
+
+ self.test_independent()
+ self.test_chain()
+ self.test_multiple_children()
+ self.test_multiple_parents()
+ self.test_conflicting()
+ self.test_rbf()
+
+ def chain_transaction(self, parent_txid, parent_value, n=0, parent_locking_script=None):
+ """Build a transaction that spends parent_txid.vout[n] and produces one output with
+ amount = parent_value with a fee deducted.
+ Return tuple (CTransaction object, raw hex, nValue, scriptPubKey of the output created).
+ """
+ node = self.nodes[0]
+ inputs = [{"txid": parent_txid, "vout": n}]
+ my_value = parent_value - Decimal("0.0001")
+ outputs = {self.address : my_value}
+ rawtx = node.createrawtransaction(inputs, outputs)
+ prevtxs = [{
+ "txid": parent_txid,
+ "vout": n,
+ "scriptPubKey": parent_locking_script,
+ "amount": parent_value,
+ }] if parent_locking_script else None
+ signedtx = node.signrawtransactionwithkey(hexstring=rawtx, privkeys=self.privkeys, prevtxs=prevtxs)
+ assert signedtx["complete"]
+ tx = tx_from_hex(signedtx["hex"])
+ return (tx, signedtx["hex"], my_value, tx.vout[0].scriptPubKey.hex())
+
+ def test_independent(self):
+ self.log.info("Test multiple independent transactions in a package")
+ node = self.nodes[0]
+ # For independent transactions, order doesn't matter.
+ self.assert_testres_equal(self.independent_txns_hex, self.independent_txns_testres)
+
+ self.log.info("Test an otherwise valid package with an extra garbage tx appended")
+ garbage_tx = node.createrawtransaction([{"txid": "00" * 32, "vout": 5}], {self.address: 1})
+ tx = tx_from_hex(garbage_tx)
+ # Only the txid and wtxids are returned because validation is incomplete for the independent txns.
+ # Package validation is atomic: if the node cannot find a UTXO for any single tx in the package,
+ # it terminates immediately to avoid unnecessary, expensive signature verification.
+ package_bad = self.independent_txns_hex + [garbage_tx]
+ testres_bad = self.independent_txns_testres_blank + [{"txid": tx.rehash(), "wtxid": tx.getwtxid(), "allowed": False, "reject-reason": "missing-inputs"}]
+ self.assert_testres_equal(package_bad, testres_bad)
+
+ self.log.info("Check testmempoolaccept tells us when some transactions completed validation successfully")
+ coin = self.coins.pop()
+ tx_bad_sig_hex = node.createrawtransaction([{"txid": coin["txid"], "vout": 0}],
+ {self.address : coin["amount"] - Decimal("0.0001")})
+ tx_bad_sig = tx_from_hex(tx_bad_sig_hex)
+ testres_bad_sig = node.testmempoolaccept(self.independent_txns_hex + [tx_bad_sig_hex])
+ # By the time the signature for the last transaction is checked, all the other transactions
+ # have been fully validated, which is why the node returns full validation results for all
+ # transactions here but empty results in other cases.
+ assert_equal(testres_bad_sig, self.independent_txns_testres + [{
+ "txid": tx_bad_sig.rehash(),
+ "wtxid": tx_bad_sig.getwtxid(), "allowed": False,
+ "reject-reason": "mandatory-script-verify-flag-failed (Operation not valid with the current stack size)"
+ }])
+
+ self.log.info("Check testmempoolaccept reports txns in packages that exceed max feerate")
+ coin = self.coins.pop()
+ tx_high_fee_raw = node.createrawtransaction([{"txid": coin["txid"], "vout": 0}],
+ {self.address : coin["amount"] - Decimal("0.999")})
+ tx_high_fee_signed = node.signrawtransactionwithkey(hexstring=tx_high_fee_raw, privkeys=self.privkeys)
+ assert tx_high_fee_signed["complete"]
+ tx_high_fee = tx_from_hex(tx_high_fee_signed["hex"])
+ testres_high_fee = node.testmempoolaccept([tx_high_fee_signed["hex"]])
+ assert_equal(testres_high_fee, [
+ {"txid": tx_high_fee.rehash(), "wtxid": tx_high_fee.getwtxid(), "allowed": False, "reject-reason": "max-fee-exceeded"}
+ ])
+ package_high_fee = [tx_high_fee_signed["hex"]] + self.independent_txns_hex
+ testres_package_high_fee = node.testmempoolaccept(package_high_fee)
+ assert_equal(testres_package_high_fee, testres_high_fee + self.independent_txns_testres_blank)
+
+ def test_chain(self):
+ node = self.nodes[0]
+ first_coin = self.coins.pop()
+
+ # Chain of 25 transactions
+ parent_locking_script = None
+ txid = first_coin["txid"]
+ chain_hex = []
+ chain_txns = []
+ value = first_coin["amount"]
+
+ for _ in range(25):
+ (tx, txhex, value, parent_locking_script) = self.chain_transaction(txid, value, 0, parent_locking_script)
+ txid = tx.rehash()
+ chain_hex.append(txhex)
+ chain_txns.append(tx)
+
+ self.log.info("Check that testmempoolaccept requires packages to be sorted by dependency")
+ assert_equal(node.testmempoolaccept(rawtxs=chain_hex[::-1]),
+ [{"txid": tx.rehash(), "wtxid": tx.getwtxid(), "package-error": "package-not-sorted"} for tx in chain_txns[::-1]])
+
+ self.log.info("Testmempoolaccept a chain of 25 transactions")
+ testres_multiple = node.testmempoolaccept(rawtxs=chain_hex)
+
+ testres_single = []
+ # Test accept and then submit each one individually, which should be identical to package test accept
+ for rawtx in chain_hex:
+ testres = node.testmempoolaccept([rawtx])
+ testres_single.append(testres[0])
+ # Submit the transaction now so its child should have no problem validating
+ node.sendrawtransaction(rawtx)
+ assert_equal(testres_single, testres_multiple)
+
+ # Clean up by clearing the mempool
+ node.generate(1)
+
+ def test_multiple_children(self):
+ node = self.nodes[0]
+
+ self.log.info("Testmempoolaccept a package in which a transaction has two children within the package")
+ first_coin = self.coins.pop()
+ value = (first_coin["amount"] - Decimal("0.0002")) / 2 # Deduct reasonable fee and make 2 outputs
+ inputs = [{"txid": first_coin["txid"], "vout": 0}]
+ outputs = [{self.address : value}, {ADDRESS_BCRT1_P2WSH_OP_TRUE : value}]
+ rawtx = node.createrawtransaction(inputs, outputs)
+
+ parent_signed = node.signrawtransactionwithkey(hexstring=rawtx, privkeys=self.privkeys)
+ assert parent_signed["complete"]
+ parent_tx = tx_from_hex(parent_signed["hex"])
+ parent_txid = parent_tx.rehash()
+ assert node.testmempoolaccept([parent_signed["hex"]])[0]["allowed"]
+
+ parent_locking_script_a = parent_tx.vout[0].scriptPubKey.hex()
+ child_value = value - Decimal("0.0001")
+
+ # Child A
+ (_, tx_child_a_hex, _, _) = self.chain_transaction(parent_txid, child_value, 0, parent_locking_script_a)
+ assert not node.testmempoolaccept([tx_child_a_hex])[0]["allowed"]
+
+ # Child B
+ rawtx_b = node.createrawtransaction([{"txid": parent_txid, "vout": 1}], {self.address : child_value})
+ tx_child_b = tx_from_hex(rawtx_b)
+ tx_child_b.wit.vtxinwit = [CTxInWitness()]
+ tx_child_b.wit.vtxinwit[0].scriptWitness.stack = [CScript([OP_TRUE])]
+ tx_child_b_hex = tx_child_b.serialize().hex()
+ assert not node.testmempoolaccept([tx_child_b_hex])[0]["allowed"]
+
+ self.log.info("Testmempoolaccept with entire package, should work with children in either order")
+ testres_multiple_ab = node.testmempoolaccept(rawtxs=[parent_signed["hex"], tx_child_a_hex, tx_child_b_hex])
+ testres_multiple_ba = node.testmempoolaccept(rawtxs=[parent_signed["hex"], tx_child_b_hex, tx_child_a_hex])
+ assert all([testres["allowed"] for testres in testres_multiple_ab + testres_multiple_ba])
+
+ testres_single = []
+ # Test accept and then submit each one individually, which should be identical to package testaccept
+ for rawtx in [parent_signed["hex"], tx_child_a_hex, tx_child_b_hex]:
+ testres = node.testmempoolaccept([rawtx])
+ testres_single.append(testres[0])
+ # Submit the transaction now so its child should have no problem validating
+ node.sendrawtransaction(rawtx)
+ assert_equal(testres_single, testres_multiple_ab)
+
+ def create_child_with_parents(self, parents_tx, values, locking_scripts):
+ """Creates a transaction that spends the first output of each parent in parents_tx."""
+ num_parents = len(parents_tx)
+ total_value = sum(values)
+ inputs = [{"txid": tx.rehash(), "vout": 0} for tx in parents_tx]
+ outputs = {self.address : total_value - num_parents * Decimal("0.0001")}
+ rawtx_child = self.nodes[0].createrawtransaction(inputs, outputs)
+ prevtxs = []
+ for i in range(num_parents):
+ prevtxs.append({"txid": parents_tx[i].rehash(), "vout": 0, "scriptPubKey": locking_scripts[i], "amount": values[i]})
+ signedtx_child = self.nodes[0].signrawtransactionwithkey(hexstring=rawtx_child, privkeys=self.privkeys, prevtxs=prevtxs)
+ assert signedtx_child["complete"]
+ return signedtx_child["hex"]
+
+ def test_multiple_parents(self):
+ node = self.nodes[0]
+
+ self.log.info("Testmempoolaccept a package in which a transaction has multiple parents within the package")
+ for num_parents in [2, 10, 24]:
+ # Test a package with num_parents parents and 1 child transaction.
+ package_hex = []
+ parents_tx = []
+ values = []
+ parent_locking_scripts = []
+ for _ in range(num_parents):
+ parent_coin = self.coins.pop()
+ value = parent_coin["amount"]
+ (tx, txhex, value, parent_locking_script) = self.chain_transaction(parent_coin["txid"], value)
+ package_hex.append(txhex)
+ parents_tx.append(tx)
+ values.append(value)
+ parent_locking_scripts.append(parent_locking_script)
+ child_hex = self.create_child_with_parents(parents_tx, values, parent_locking_scripts)
+ # Package accept should work with the parents in any order (as long as parents come before child)
+ for _ in range(10):
+ random.shuffle(package_hex)
+ testres_multiple = node.testmempoolaccept(rawtxs=package_hex + [child_hex])
+ assert all([testres["allowed"] for testres in testres_multiple])
+
+ testres_single = []
+ # Test accept and then submit each one individually, which should be identical to package testaccept
+ for rawtx in package_hex + [child_hex]:
+ testres_single.append(node.testmempoolaccept([rawtx])[0])
+ # Submit the transaction now so its child should have no problem validating
+ node.sendrawtransaction(rawtx)
+ assert_equal(testres_single, testres_multiple)
+
+ def test_conflicting(self):
+ node = self.nodes[0]
+ prevtx = self.coins.pop()
+ inputs = [{"txid": prevtx["txid"], "vout": 0}]
+ output1 = {node.get_deterministic_priv_key().address: 50 - 0.00125}
+ output2 = {ADDRESS_BCRT1_P2WSH_OP_TRUE: 50 - 0.00125}
+
+ # tx1 and tx2 share the same inputs
+ rawtx1 = node.createrawtransaction(inputs, output1)
+ rawtx2 = node.createrawtransaction(inputs, output2)
+ signedtx1 = node.signrawtransactionwithkey(hexstring=rawtx1, privkeys=self.privkeys)
+ signedtx2 = node.signrawtransactionwithkey(hexstring=rawtx2, privkeys=self.privkeys)
+ tx1 = tx_from_hex(signedtx1["hex"])
+ tx2 = tx_from_hex(signedtx2["hex"])
+ assert signedtx1["complete"]
+ assert signedtx2["complete"]
+
+ # Ensure tx1 and tx2 are valid by themselves
+ assert node.testmempoolaccept([signedtx1["hex"]])[0]["allowed"]
+ assert node.testmempoolaccept([signedtx2["hex"]])[0]["allowed"]
+
+ self.log.info("Test duplicate transactions in the same package")
+ testres = node.testmempoolaccept([signedtx1["hex"], signedtx1["hex"]])
+ assert_equal(testres, [
+ {"txid": tx1.rehash(), "wtxid": tx1.getwtxid(), "package-error": "conflict-in-package"},
+ {"txid": tx1.rehash(), "wtxid": tx1.getwtxid(), "package-error": "conflict-in-package"}
+ ])
+
+ self.log.info("Test conflicting transactions in the same package")
+ testres = node.testmempoolaccept([signedtx1["hex"], signedtx2["hex"]])
+ assert_equal(testres, [
+ {"txid": tx1.rehash(), "wtxid": tx1.getwtxid(), "package-error": "conflict-in-package"},
+ {"txid": tx2.rehash(), "wtxid": tx2.getwtxid(), "package-error": "conflict-in-package"}
+ ])
+
+ def test_rbf(self):
+ node = self.nodes[0]
+ coin = self.coins.pop()
+ inputs = [{"txid": coin["txid"], "vout": 0, "sequence": BIP125_SEQUENCE_NUMBER}]
+ fee = Decimal('0.00125000')
+ output = {node.get_deterministic_priv_key().address: 50 - fee}
+ raw_replaceable_tx = node.createrawtransaction(inputs, output)
+ signed_replaceable_tx = node.signrawtransactionwithkey(hexstring=raw_replaceable_tx, privkeys=self.privkeys)
+ testres_replaceable = node.testmempoolaccept([signed_replaceable_tx["hex"]])
+ replaceable_tx = tx_from_hex(signed_replaceable_tx["hex"])
+ assert_equal(testres_replaceable, [
+ {"txid": replaceable_tx.rehash(), "wtxid": replaceable_tx.getwtxid(),
+ "allowed": True, "vsize": replaceable_tx.get_vsize(), "fees": { "base": fee }}
+ ])
+
+ # Replacement transaction is identical except has double the fee
+ replacement_tx = tx_from_hex(signed_replaceable_tx["hex"])
+ replacement_tx.vout[0].nValue -= int(fee * COIN) # Doubled fee
+ signed_replacement_tx = node.signrawtransactionwithkey(replacement_tx.serialize().hex(), self.privkeys)
+ replacement_tx = tx_from_hex(signed_replacement_tx["hex"])
+
+ self.log.info("Test that transactions within a package cannot replace each other")
+ testres_rbf_conflicting = node.testmempoolaccept([signed_replaceable_tx["hex"], signed_replacement_tx["hex"]])
+ assert_equal(testres_rbf_conflicting, [
+ {"txid": replaceable_tx.rehash(), "wtxid": replaceable_tx.getwtxid(), "package-error": "conflict-in-package"},
+ {"txid": replacement_tx.rehash(), "wtxid": replacement_tx.getwtxid(), "package-error": "conflict-in-package"}
+ ])
+
+ self.log.info("Test that packages cannot conflict with mempool transactions, even if a valid BIP125 RBF")
+ node.sendrawtransaction(signed_replaceable_tx["hex"])
+ testres_rbf_single = node.testmempoolaccept([signed_replacement_tx["hex"]])
+ # This transaction is a valid BIP125 replace-by-fee
+ assert testres_rbf_single[0]["allowed"]
+ testres_rbf_package = self.independent_txns_testres_blank + [{
+ "txid": replacement_tx.rehash(), "wtxid": replacement_tx.getwtxid(), "allowed": False,
+ "reject-reason": "bip125-replacement-disallowed"
+ }]
+ self.assert_testres_equal(self.independent_txns_hex + [signed_replacement_tx["hex"]], testres_rbf_package)
+
+if __name__ == "__main__":
+ RPCPackagesTest().main()
diff --git a/test/functional/rpc_rawtransaction.py b/test/functional/rpc_rawtransaction.py
index 86c7b3fbcc..9d4a5525d1 100755
--- a/test/functional/rpc_rawtransaction.py
+++ b/test/functional/rpc_rawtransaction.py
@@ -14,14 +14,17 @@ Test the following RPCs:
from collections import OrderedDict
from decimal import Decimal
-from io import BytesIO
-from test_framework.messages import CTransaction, ToHex
+
+from test_framework.blocktools import COINBASE_MATURITY
+from test_framework.messages import (
+ CTransaction,
+ tx_from_hex,
+)
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
assert_raises_rpc_error,
find_vout_for_address,
- hex_str_to_bytes,
)
@@ -53,6 +56,10 @@ class RawTransactionsTest(BitcoinTestFramework):
["-txindex"],
["-txindex"],
]
+ # whitelist all peers to speed up tx relay / mempool sync
+ for args in self.extra_args:
+ args.append("-whitelist=noban@127.0.0.1")
+
self.supports_cli = False
def skip_test_if_missing_module(self):
@@ -66,7 +73,7 @@ class RawTransactionsTest(BitcoinTestFramework):
self.log.info('prepare some coins for multiple *rawtransaction commands')
self.nodes[2].generate(1)
self.sync_all()
- self.nodes[0].generate(101)
+ self.nodes[0].generate(COINBASE_MATURITY + 1)
self.sync_all()
self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(),1.5)
self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(),1.0)
@@ -125,23 +132,22 @@ class RawTransactionsTest(BitcoinTestFramework):
assert_raises_rpc_error(-3, "Expected type bool", self.nodes[0].createrawtransaction, [], {}, 0, 'foo')
self.log.info('Check that createrawtransaction accepts an array and object as outputs')
- tx = CTransaction()
# One output
- tx.deserialize(BytesIO(hex_str_to_bytes(self.nodes[2].createrawtransaction(inputs=[{'txid': txid, 'vout': 9}], outputs={address: 99}))))
+ tx = tx_from_hex(self.nodes[2].createrawtransaction(inputs=[{'txid': txid, 'vout': 9}], outputs={address: 99}))
assert_equal(len(tx.vout), 1)
assert_equal(
tx.serialize().hex(),
self.nodes[2].createrawtransaction(inputs=[{'txid': txid, 'vout': 9}], outputs=[{address: 99}]),
)
# Two outputs
- tx.deserialize(BytesIO(hex_str_to_bytes(self.nodes[2].createrawtransaction(inputs=[{'txid': txid, 'vout': 9}], outputs=OrderedDict([(address, 99), (address2, 99)])))))
+ tx = tx_from_hex(self.nodes[2].createrawtransaction(inputs=[{'txid': txid, 'vout': 9}], outputs=OrderedDict([(address, 99), (address2, 99)])))
assert_equal(len(tx.vout), 2)
assert_equal(
tx.serialize().hex(),
self.nodes[2].createrawtransaction(inputs=[{'txid': txid, 'vout': 9}], outputs=[{address: 99}, {address2: 99}]),
)
# Multiple mixed outputs
- tx.deserialize(BytesIO(hex_str_to_bytes(self.nodes[2].createrawtransaction(inputs=[{'txid': txid, 'vout': 9}], outputs=multidict([(address, 99), (address2, 99), ('data', '99')])))))
+ tx = tx_from_hex(self.nodes[2].createrawtransaction(inputs=[{'txid': txid, 'vout': 9}], outputs=multidict([(address, 99), (address2, 99), ('data', '99')])))
assert_equal(len(tx.vout), 3)
assert_equal(
tx.serialize().hex(),
@@ -448,14 +454,14 @@ class RawTransactionsTest(BitcoinTestFramework):
# As transaction version is unsigned, this should convert to its unsigned equivalent.
tx = CTransaction()
tx.nVersion = -0x80000000
- rawtx = ToHex(tx)
+ rawtx = tx.serialize().hex()
decrawtx = self.nodes[0].decoderawtransaction(rawtx)
assert_equal(decrawtx['version'], 0x80000000)
# Test the maximum transaction version number that fits in a signed 32-bit integer.
tx = CTransaction()
tx.nVersion = 0x7fffffff
- rawtx = ToHex(tx)
+ rawtx = tx.serialize().hex()
decrawtx = self.nodes[0].decoderawtransaction(rawtx)
assert_equal(decrawtx['version'], 0x7fffffff)
@@ -509,6 +515,15 @@ class RawTransactionsTest(BitcoinTestFramework):
assert_equal(testres['allowed'], True)
self.nodes[2].sendrawtransaction(hexstring=rawTxSigned['hex'], maxfeerate='0.20000000')
+ self.log.info('sendrawtransaction/testmempoolaccept with tx that is already in the chain')
+ self.nodes[2].generate(1)
+ self.sync_blocks()
+ for node in self.nodes:
+ testres = node.testmempoolaccept([rawTxSigned['hex']])[0]
+ assert_equal(testres['allowed'], False)
+ assert_equal(testres['reject-reason'], 'txn-already-known')
+ assert_raises_rpc_error(-27, 'Transaction already in block chain', node.sendrawtransaction, rawTxSigned['hex'])
+
if __name__ == '__main__':
RawTransactionsTest().main()
diff --git a/test/functional/rpc_setban.py b/test/functional/rpc_setban.py
index fd5f8aa098..36873f964b 100755
--- a/test/functional/rpc_setban.py
+++ b/test/functional/rpc_setban.py
@@ -22,7 +22,7 @@ class SetBanTests(BitcoinTestFramework):
# Node 0 connects to Node 1, check that the noban permission is not granted
self.connect_nodes(0, 1)
peerinfo = self.nodes[1].getpeerinfo()[0]
- assert(not 'noban' in peerinfo['permissions'])
+ assert not "noban" in peerinfo["permissions"]
# Node 0 get banned by Node 1
self.nodes[1].setban("127.0.0.1", "add")
@@ -36,27 +36,40 @@ class SetBanTests(BitcoinTestFramework):
self.restart_node(1, ['-whitelist=127.0.0.1'])
self.connect_nodes(0, 1)
peerinfo = self.nodes[1].getpeerinfo()[0]
- assert('noban' in peerinfo['permissions'])
+ assert "noban" in peerinfo["permissions"]
# If we remove the ban, Node 0 should be able to reconnect even without noban permission
self.nodes[1].setban("127.0.0.1", "remove")
self.restart_node(1, [])
self.connect_nodes(0, 1)
peerinfo = self.nodes[1].getpeerinfo()[0]
- assert(not 'noban' in peerinfo['permissions'])
+ assert not "noban" in peerinfo["permissions"]
self.log.info("Test that a non-IP address can be banned/unbanned")
node = self.nodes[1]
tor_addr = "pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion"
ip_addr = "1.2.3.4"
- assert(not self.is_banned(node, tor_addr))
- assert(not self.is_banned(node, ip_addr))
+ assert not self.is_banned(node, tor_addr)
+ assert not self.is_banned(node, ip_addr)
+
node.setban(tor_addr, "add")
- assert(self.is_banned(node, tor_addr))
- assert(not self.is_banned(node, ip_addr))
+ assert self.is_banned(node, tor_addr)
+ assert not self.is_banned(node, ip_addr)
+
+ self.log.info("Test the ban list is preserved through restart")
+
+ self.restart_node(1)
+ assert self.is_banned(node, tor_addr)
+ assert not self.is_banned(node, ip_addr)
+
node.setban(tor_addr, "remove")
- assert(not self.is_banned(self.nodes[1], tor_addr))
- assert(not self.is_banned(node, ip_addr))
+ assert not self.is_banned(self.nodes[1], tor_addr)
+ assert not self.is_banned(node, ip_addr)
+
+ self.restart_node(1)
+ assert not self.is_banned(node, tor_addr)
+ assert not self.is_banned(node, ip_addr)
+
if __name__ == '__main__':
SetBanTests().main()
diff --git a/test/functional/rpc_signrawtransaction.py b/test/functional/rpc_signrawtransaction.py
index 60b4d1c744..571029155e 100755
--- a/test/functional/rpc_signrawtransaction.py
+++ b/test/functional/rpc_signrawtransaction.py
@@ -4,17 +4,47 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test transaction signing using the signrawtransaction* RPCs."""
-from test_framework.address import check_script, script_to_p2sh, script_to_p2wsh
+from test_framework.blocktools import (
+ CLTV_HEIGHT,
+ COINBASE_MATURITY,
+ CSV_ACTIVATION_HEIGHT,
+)
+from test_framework.address import (
+ script_to_p2sh,
+ script_to_p2wsh,
+)
from test_framework.key import ECKey
from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import assert_equal, assert_raises_rpc_error, find_vout_for_address, hex_str_to_bytes
-from test_framework.messages import sha256, CTransaction, CTxInWitness
-from test_framework.script import CScript, OP_0, OP_CHECKSIG, OP_CHECKSEQUENCEVERIFY, OP_CHECKLOCKTIMEVERIFY, OP_DROP, OP_TRUE
-from test_framework.script_util import key_to_p2pkh_script, script_to_p2sh_p2wsh_script, script_to_p2wsh_script
+from test_framework.util import (
+ assert_equal,
+ assert_raises_rpc_error,
+ find_vout_for_address,
+ generate_to_height,
+ hex_str_to_bytes,
+)
+from test_framework.messages import (
+ CTxInWitness,
+ tx_from_hex,
+)
+from test_framework.script import (
+ CScript,
+ OP_CHECKLOCKTIMEVERIFY,
+ OP_CHECKSIG,
+ OP_CHECKSEQUENCEVERIFY,
+ OP_DROP,
+ OP_TRUE,
+)
+from test_framework.script_util import (
+ key_to_p2pkh_script,
+ script_to_p2sh_p2wsh_script,
+ script_to_p2wsh_script,
+)
from test_framework.wallet_util import bytes_to_wif
-from decimal import Decimal, getcontext
-from io import BytesIO
+from decimal import (
+ Decimal,
+ getcontext,
+)
class SignRawTransactionsTest(BitcoinTestFramework):
def set_test_params(self):
@@ -155,7 +185,7 @@ class SignRawTransactionsTest(BitcoinTestFramework):
def test_fully_signed_tx(self):
self.log.info("Test signing a fully signed transaction does nothing")
self.nodes[0].walletpassphrase("password", 9999)
- self.nodes[0].generate(101)
+ self.nodes[0].generate(COINBASE_MATURITY + 1)
rawtx = self.nodes[0].createrawtransaction([], [{self.nodes[0].getnewaddress(): 10}])
fundedtx = self.nodes[0].fundrawtransaction(rawtx)
signedtx = self.nodes[0].signrawtransactionwithwallet(fundedtx["hex"])
@@ -174,7 +204,7 @@ class SignRawTransactionsTest(BitcoinTestFramework):
embedded_pubkey = eckey.get_pubkey().get_bytes().hex()
p2sh_p2wsh_address = self.nodes[1].createmultisig(1, [embedded_pubkey], "p2sh-segwit")
# send transaction to P2SH-P2WSH 1-of-1 multisig address
- self.nodes[0].generate(101)
+ self.nodes[0].generate(COINBASE_MATURITY + 1)
self.nodes[0].sendtoaddress(p2sh_p2wsh_address["address"], 49.999)
self.nodes[0].generate(1)
self.sync_all()
@@ -205,7 +235,7 @@ class SignRawTransactionsTest(BitcoinTestFramework):
'P2PKH': key_to_p2pkh_script(embedded_pubkey).hex(),
'P2PK': CScript([hex_str_to_bytes(embedded_pubkey), OP_CHECKSIG]).hex()
}.get(tx_type, "Invalid tx_type")
- redeem_script = CScript([OP_0, sha256(check_script(witness_script))]).hex()
+ redeem_script = script_to_p2wsh_script(witness_script).hex()
addr = script_to_p2sh(redeem_script)
script_pub_key = self.nodes[1].validateaddress(addr)['scriptPubKey']
# Fund that address
@@ -245,7 +275,8 @@ class SignRawTransactionsTest(BitcoinTestFramework):
getcontext().prec = 8
# Make sure CSV is active
- self.nodes[0].generate(500)
+ generate_to_height(self.nodes[0], CSV_ACTIVATION_HEIGHT)
+ assert self.nodes[0].getblockchaininfo()['softforks']['csv']['active']
# Create a P2WSH script with CSV
script = CScript([1, OP_CHECKSEQUENCEVERIFY, OP_DROP])
@@ -264,8 +295,7 @@ class SignRawTransactionsTest(BitcoinTestFramework):
)
# Set the witness script
- ctx = CTransaction()
- ctx.deserialize(BytesIO(hex_str_to_bytes(tx)))
+ ctx = tx_from_hex(tx)
ctx.wit.vtxinwit.append(CTxInWitness())
ctx.wit.vtxinwit[0].scriptWitness.stack = [CScript([OP_TRUE]), script]
tx = ctx.serialize_with_witness().hex()
@@ -280,8 +310,9 @@ class SignRawTransactionsTest(BitcoinTestFramework):
self.nodes[0].walletpassphrase("password", 9999)
getcontext().prec = 8
- # Make sure CSV is active
- self.nodes[0].generate(1500)
+ # Make sure CLTV is active
+ generate_to_height(self.nodes[0], CLTV_HEIGHT)
+ assert self.nodes[0].getblockchaininfo()['softforks']['bip65']['active']
# Create a P2WSH script with CLTV
script = CScript([1000, OP_CHECKLOCKTIMEVERIFY, OP_DROP])
@@ -300,8 +331,7 @@ class SignRawTransactionsTest(BitcoinTestFramework):
)
# Set the witness script
- ctx = CTransaction()
- ctx.deserialize(BytesIO(hex_str_to_bytes(tx)))
+ ctx = tx_from_hex(tx)
ctx.wit.vtxinwit.append(CTxInWitness())
ctx.wit.vtxinwit[0].scriptWitness.stack = [CScript([OP_TRUE]), script]
tx = ctx.serialize_with_witness().hex()
diff --git a/test/functional/rpc_txoutproof.py b/test/functional/rpc_txoutproof.py
index 528da0cbfc..67af6b8f8e 100755
--- a/test/functional/rpc_txoutproof.py
+++ b/test/functional/rpc_txoutproof.py
@@ -4,9 +4,16 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test gettxoutproof and verifytxoutproof RPCs."""
-from test_framework.messages import CMerkleBlock, FromHex, ToHex
+from test_framework.blocktools import COINBASE_MATURITY
+from test_framework.messages import (
+ CMerkleBlock,
+ from_hex,
+)
from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import assert_equal, assert_raises_rpc_error
+from test_framework.util import (
+ assert_equal,
+ assert_raises_rpc_error,
+)
from test_framework.wallet import MiniWallet
@@ -23,7 +30,7 @@ class MerkleBlockTest(BitcoinTestFramework):
miniwallet = MiniWallet(self.nodes[0])
# Add enough mature utxos to the wallet, so that all txs spend confirmed coins
miniwallet.generate(5)
- self.nodes[0].generate(100)
+ self.nodes[0].generate(COINBASE_MATURITY)
self.sync_all()
chain_height = self.nodes[1].getblockcount()
@@ -87,10 +94,10 @@ class MerkleBlockTest(BitcoinTestFramework):
assert txid1 in self.nodes[0].verifytxoutproof(proof)
assert txid2 in self.nodes[1].verifytxoutproof(proof)
- tweaked_proof = FromHex(CMerkleBlock(), proof)
+ tweaked_proof = from_hex(CMerkleBlock(), proof)
# Make sure that our serialization/deserialization is working
- assert txid1 in self.nodes[0].verifytxoutproof(ToHex(tweaked_proof))
+ assert txid1 in self.nodes[0].verifytxoutproof(tweaked_proof.serialize().hex())
# Check to see if we can go up the merkle tree and pass this off as a
# single-transaction block
@@ -99,7 +106,7 @@ class MerkleBlockTest(BitcoinTestFramework):
tweaked_proof.txn.vBits = [True] + [False]*7
for n in self.nodes:
- assert not n.verifytxoutproof(ToHex(tweaked_proof))
+ assert not n.verifytxoutproof(tweaked_proof.serialize().hex())
# TODO: try more variants, eg transactions at different depths, and
# verify that the proofs are invalid
diff --git a/test/functional/test_framework/blocktools.py b/test/functional/test_framework/blocktools.py
index d08e025178..2ab720aafb 100644
--- a/test/functional/test_framework/blocktools.py
+++ b/test/functional/test_framework/blocktools.py
@@ -23,25 +23,25 @@ from .messages import (
CTxIn,
CTxInWitness,
CTxOut,
- FromHex,
- ToHex,
hash256,
hex_str_to_bytes,
ser_uint256,
- sha256,
+ tx_from_hex,
uint256_from_str,
)
from .script import (
CScript,
CScriptNum,
CScriptOp,
- OP_0,
OP_1,
OP_CHECKMULTISIG,
OP_CHECKSIG,
OP_RETURN,
OP_TRUE,
- hash160,
+)
+from .script_util import (
+ key_to_p2wpkh_script,
+ script_to_p2wsh_script,
)
from .util import assert_equal
@@ -52,6 +52,13 @@ MAX_BLOCK_SIGOPS_WEIGHT = MAX_BLOCK_SIGOPS * WITNESS_SCALE_FACTOR
# Genesis block time (regtest)
TIME_GENESIS_BLOCK = 1296688602
+# Coinbase transaction outputs can only be spent after this number of new blocks (network rule)
+COINBASE_MATURITY = 100
+
+# Soft-fork activation heights
+CLTV_HEIGHT = 1351
+CSV_ACTIVATION_HEIGHT = 432
+
# From BIP141
WITNESS_COMMITMENT_HEADER = b"\xaa\x21\xa9\xed"
@@ -76,7 +83,7 @@ def create_block(hashprev=None, coinbase=None, ntime=None, *, version=None, tmpl
if txlist:
for tx in txlist:
if not hasattr(tx, 'calc_sha256'):
- tx = FromHex(CTransaction(), tx)
+ tx = tx_from_hex(tx)
block.vtx.append(tx)
block.hashMerkleRoot = block.calc_merkle_root()
block.calc_sha256()
@@ -163,7 +170,7 @@ def create_transaction(node, txid, to_address, *, amount):
sign for the output that is being spent.
"""
raw_tx = create_raw_transaction(node, txid, to_address, amount=amount)
- tx = FromHex(CTransaction(), raw_tx)
+ tx = tx_from_hex(raw_tx)
return tx
def create_raw_transaction(node, txid, to_address, *, amount):
@@ -204,13 +211,11 @@ def witness_script(use_p2wsh, pubkey):
scriptPubKey."""
if not use_p2wsh:
# P2WPKH instead
- pubkeyhash = hash160(hex_str_to_bytes(pubkey))
- pkscript = CScript([OP_0, pubkeyhash])
+ pkscript = key_to_p2wpkh_script(pubkey)
else:
# 1-of-1 multisig
witness_program = CScript([OP_1, hex_str_to_bytes(pubkey), OP_1, OP_CHECKMULTISIG])
- scripthash = sha256(witness_program)
- pkscript = CScript([OP_0, scripthash])
+ pkscript = script_to_p2wsh_script(witness_program)
return pkscript.hex()
def create_witness_tx(node, use_p2wsh, utxo, pubkey, encode_p2sh, amount):
@@ -240,9 +245,9 @@ def send_to_witness(use_p2wsh, node, utxo, pubkey, encode_p2sh, amount, sign=Tru
return node.sendrawtransaction(signed["hex"])
else:
if (insert_redeem_script):
- tx = FromHex(CTransaction(), tx_to_witness)
+ tx = tx_from_hex(tx_to_witness)
tx.vin[0].scriptSig += CScript([hex_str_to_bytes(insert_redeem_script)])
- tx_to_witness = ToHex(tx)
+ tx_to_witness = tx.serialize().hex()
return node.sendrawtransaction(tx_to_witness)
diff --git a/test/functional/test_framework/messages.py b/test/functional/test_framework/messages.py
index 5a9736a7a3..065e8961ae 100755
--- a/test/functional/test_framework/messages.py
+++ b/test/functional/test_framework/messages.py
@@ -18,6 +18,7 @@ ser_*, deser_*: functions that handle serialization/deserialization.
Classes use __slots__ to ensure extraneous attributes aren't accidentally added
by tests, compromising their intended effect.
"""
+from base64 import b32decode, b32encode
from codecs import encode
import copy
import hashlib
@@ -39,7 +40,7 @@ MAX_BLOOM_HASH_FUNCS = 50
COIN = 100000000 # 1 btc in satoshis
MAX_MONEY = 21000000 * COIN
-BIP125_SEQUENCE_NUMBER = 0xfffffffd # Sequence number that is BIP 125 opt-in and BIP 68-opt-out
+BIP125_SEQUENCE_NUMBER = 0xfffffffd # Sequence number that is rbf-opt-in (BIP 125) and csv-opt-out (BIP 68)
MAX_PROTOCOL_MESSAGE_LENGTH = 4000000 # Maximum length of incoming protocol messages
MAX_HEADERS_RESULTS = 2000 # Number of headers sent in one getheaders result
@@ -190,14 +191,20 @@ def ser_string_vector(l):
return r
-# Deserialize from a hex string representation (eg from RPC)
-def FromHex(obj, hex_string):
+def from_hex(obj, hex_string):
+ """Deserialize from a hex string representation (e.g. from RPC)
+
+ Note that there is no complementary helper like e.g. `to_hex` for the
+ inverse operation. To serialize a message object to a hex string, simply
+ use obj.serialize().hex()"""
obj.deserialize(BytesIO(hex_str_to_bytes(hex_string)))
return obj
-# Convert a binary-serializable object to hex (eg for submission via RPC)
-def ToHex(obj):
- return obj.serialize().hex()
+
+def tx_from_hex(hex_string):
+ """Deserialize from hex string to a transaction object"""
+ return from_hex(CTransaction(), hex_string)
+
# Objects that map to bitcoind objects, which can be serialized/deserialized
@@ -207,15 +214,20 @@ class CAddress:
# see https://github.com/bitcoin/bips/blob/master/bip-0155.mediawiki
NET_IPV4 = 1
+ NET_I2P = 5
ADDRV2_NET_NAME = {
- NET_IPV4: "IPv4"
+ NET_IPV4: "IPv4",
+ NET_I2P: "I2P"
}
ADDRV2_ADDRESS_LENGTH = {
- NET_IPV4: 4
+ NET_IPV4: 4,
+ NET_I2P: 32
}
+ I2P_PAD = "===="
+
def __init__(self):
self.time = 0
self.nServices = 1
@@ -223,6 +235,9 @@ class CAddress:
self.ip = "0.0.0.0"
self.port = 0
+ def __eq__(self, other):
+ return self.net == other.net and self.ip == other.ip and self.nServices == other.nServices and self.port == other.port and self.time == other.time
+
def deserialize(self, f, *, with_time=True):
"""Deserialize from addrv1 format (pre-BIP155)"""
if with_time:
@@ -255,24 +270,33 @@ class CAddress:
self.nServices = deser_compact_size(f)
self.net = struct.unpack("B", f.read(1))[0]
- assert self.net == self.NET_IPV4
+ assert self.net in (self.NET_IPV4, self.NET_I2P)
address_length = deser_compact_size(f)
assert address_length == self.ADDRV2_ADDRESS_LENGTH[self.net]
- self.ip = socket.inet_ntoa(f.read(4))
+ addr_bytes = f.read(address_length)
+ if self.net == self.NET_IPV4:
+ self.ip = socket.inet_ntoa(addr_bytes)
+ else:
+ self.ip = b32encode(addr_bytes)[0:-len(self.I2P_PAD)].decode("ascii").lower() + ".b32.i2p"
self.port = struct.unpack(">H", f.read(2))[0]
def serialize_v2(self):
"""Serialize in addrv2 format (BIP155)"""
- assert self.net == self.NET_IPV4
+ assert self.net in (self.NET_IPV4, self.NET_I2P)
r = b""
r += struct.pack("<I", self.time)
r += ser_compact_size(self.nServices)
r += struct.pack("B", self.net)
r += ser_compact_size(self.ADDRV2_ADDRESS_LENGTH[self.net])
- r += socket.inet_aton(self.ip)
+ if self.net == self.NET_IPV4:
+ r += socket.inet_aton(self.ip)
+ else:
+ sfx = ".b32.i2p"
+ assert self.ip.endswith(sfx)
+ r += b32decode(self.ip[0:-len(sfx)] + self.I2P_PAD, True)
r += struct.pack(">H", self.port)
return r
diff --git a/test/functional/test_framework/netutil.py b/test/functional/test_framework/netutil.py
index e047e7fa14..5dc723c1d5 100644
--- a/test/functional/test_framework/netutil.py
+++ b/test/functional/test_framework/netutil.py
@@ -151,7 +151,7 @@ def test_ipv6_local():
have_ipv6 = True
try:
s = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
- s.connect(('::1', 0))
+ s.connect(('::1', 1))
except socket.error:
have_ipv6 = False
return have_ipv6
diff --git a/test/functional/test_framework/test_framework.py b/test/functional/test_framework/test_framework.py
index a89a26caea..40360c54a0 100755
--- a/test/functional/test_framework/test_framework.py
+++ b/test/functional/test_framework/test_framework.py
@@ -194,6 +194,10 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
help="Run test using legacy wallets", dest='descriptors')
self.add_options(parser)
+ # Running TestShell in a Jupyter notebook causes an additional -f argument
+ # To keep TestShell from failing with an "unrecognized argument" error, we add a dummy "-f" argument
+ # source: https://stackoverflow.com/questions/48796169/how-to-fix-ipykernel-launcher-py-error-unrecognized-arguments-in-jupyter/56349168#56349168
+ parser.add_argument("-f", "--fff", help="a dummy argument to fool ipython", default="1")
self.options = parser.parse_args()
self.options.previous_releases_path = previous_releases_path
diff --git a/test/functional/test_framework/test_node.py b/test/functional/test_framework/test_node.py
index c17c16f797..afa904c8d7 100755
--- a/test/functional/test_framework/test_node.py
+++ b/test/functional/test_framework/test_node.py
@@ -400,14 +400,14 @@ class TestNode():
self._raise_assertion_error('Expected messages "{}" does not partially match log:\n\n{}\n\n'.format(str(expected_msgs), print_log))
@contextlib.contextmanager
- def profile_with_perf(self, profile_name):
+ def profile_with_perf(self, profile_name: str):
"""
Context manager that allows easy profiling of node activity using `perf`.
See `test/functional/README.md` for details on perf usage.
Args:
- profile_name (str): This string will be appended to the
+ profile_name: This string will be appended to the
profile data filename generated by perf.
"""
subp = self._start_perf(profile_name)
@@ -557,9 +557,8 @@ class TestNode():
return p2p_conn
def add_outbound_p2p_connection(self, p2p_conn, *, p2p_idx, connection_type="outbound-full-relay", **kwargs):
- """Add an outbound p2p connection from node. Either
- full-relay("outbound-full-relay") or
- block-relay-only("block-relay-only") connection.
+ """Add an outbound p2p connection from node. Must be an
+ "outbound-full-relay", "block-relay-only" or "addr-fetch" connection.
This method adds the p2p connection to the self.p2ps list and returns
the connection to the caller.
diff --git a/test/functional/test_framework/util.py b/test/functional/test_framework/util.py
index 55166ba0ad..fcaf3b2c29 100644
--- a/test/functional/test_framework/util.py
+++ b/test/functional/test_framework/util.py
@@ -19,7 +19,7 @@ import unittest
from . import coverage
from .authproxy import AuthServiceProxy, JSONRPCException
-from io import BytesIO
+from typing import Callable, Optional
logger = logging.getLogger("TestFramework.utils")
@@ -80,7 +80,7 @@ def assert_raises_message(exc, message, fun, *args, **kwds):
raise AssertionError("No exception raised")
-def assert_raises_process_error(returncode, output, fun, *args, **kwds):
+def assert_raises_process_error(returncode: int, output: str, fun: Callable, *args, **kwds):
"""Execute a process and asserts the process return code and output.
Calls function `fun` with arguments `args` and `kwds`. Catches a CalledProcessError
@@ -88,9 +88,9 @@ def assert_raises_process_error(returncode, output, fun, *args, **kwds):
no CalledProcessError was raised or if the return code and output are not as expected.
Args:
- returncode (int): the process return code.
- output (string): [a substring of] the process output.
- fun (function): the function to call. This should execute a process.
+ returncode: the process return code.
+ output: [a substring of] the process output.
+ fun: the function to call. This should execute a process.
args*: positional arguments for the function.
kwds**: named arguments for the function.
"""
@@ -105,7 +105,7 @@ def assert_raises_process_error(returncode, output, fun, *args, **kwds):
raise AssertionError("No exception raised")
-def assert_raises_rpc_error(code, message, fun, *args, **kwds):
+def assert_raises_rpc_error(code: Optional[int], message: Optional[str], fun: Callable, *args, **kwds):
"""Run an RPC and verify that a specific JSONRPC exception code and message is raised.
Calls function `fun` with arguments `args` and `kwds`. Catches a JSONRPCException
@@ -113,11 +113,11 @@ def assert_raises_rpc_error(code, message, fun, *args, **kwds):
no JSONRPCException was raised or if the error code/message are not as expected.
Args:
- code (int), optional: the error code returned by the RPC call (defined
- in src/rpc/protocol.h). Set to None if checking the error code is not required.
- message (string), optional: [a substring of] the error string returned by the
- RPC call. Set to None if checking the error string is not required.
- fun (function): the function to call. This should be the name of an RPC.
+ code: the error code returned by the RPC call (defined in src/rpc/protocol.h).
+ Set to None if checking the error code is not required.
+ message: [a substring of] the error string returned by the RPC call.
+ Set to None if checking the error string is not required.
+ fun: the function to call. This should be the name of an RPC.
args*: positional arguments for the function.
kwds**: named arguments for the function.
"""
@@ -480,6 +480,28 @@ def create_confirmed_utxos(fee, node, count):
return utxos
+def chain_transaction(node, parent_txids, vouts, value, fee, num_outputs):
+ """Build and send a transaction that spends the given inputs (specified
+ by lists of parent_txid:vout each), with the desired total value and fee,
+ equally divided up to the desired number of outputs.
+
+ Returns a tuple with the txid and the amount sent per output.
+ """
+ send_value = satoshi_round((value - fee)/num_outputs)
+ inputs = []
+ for (txid, vout) in zip(parent_txids, vouts):
+ inputs.append({'txid' : txid, 'vout' : vout})
+ outputs = {}
+ for _ in range(num_outputs):
+ outputs[node.getnewaddress()] = send_value
+ rawtx = node.createrawtransaction(inputs, outputs, 0, True)
+ signedtx = node.signrawtransactionwithwallet(rawtx)
+ txid = node.sendrawtransaction(signedtx['hex'])
+ fulltx = node.getrawtransaction(txid, 1)
+ assert len(fulltx['vout']) == num_outputs # make sure we didn't generate a change output
+ return (txid, send_value)
+
+
# Create large OP_RETURN txouts that can be appended to a transaction
# to make it large (helper for constructing large transactions).
def gen_return_txouts():
@@ -505,7 +527,7 @@ def gen_return_txouts():
def create_lots_of_big_transactions(node, txouts, utxos, num, fee):
addr = node.getnewaddress()
txids = []
- from .messages import CTransaction
+ from .messages import tx_from_hex
for _ in range(num):
t = utxos.pop()
inputs = [{"txid": t["txid"], "vout": t["vout"]}]
@@ -513,8 +535,7 @@ def create_lots_of_big_transactions(node, txouts, utxos, num, fee):
change = t['amount'] - fee
outputs[addr] = satoshi_round(change)
rawtx = node.createrawtransaction(inputs, outputs)
- tx = CTransaction()
- tx.deserialize(BytesIO(hex_str_to_bytes(rawtx)))
+ tx = tx_from_hex(rawtx)
for txout in txouts:
tx.vout.append(txout)
newtx = tx.serialize().hex()
@@ -538,6 +559,17 @@ def mine_large_block(node, utxos=None):
node.generate(1)
+def generate_to_height(node, target_height):
+ """Generates blocks until a given target block height has been reached.
+ To prevent timeouts, only up to 200 blocks are generated per RPC call.
+ Can be used to activate certain soft-forks (e.g. CSV, CLTV)."""
+ current_height = node.getblockcount()
+ while current_height < target_height:
+ nblocks = min(200, target_height - current_height)
+ current_height += len(node.generate(nblocks))
+ assert_equal(node.getblockcount(), target_height)
+
+
def find_vout_for_address(node, txid, addr):
"""
Locate the vout index of the given transaction sending to the
diff --git a/test/functional/test_framework/wallet.py b/test/functional/test_framework/wallet.py
index 18822fc610..47ec6b0be2 100644
--- a/test/functional/test_framework/wallet.py
+++ b/test/functional/test_framework/wallet.py
@@ -5,6 +5,8 @@
"""A limited-functionality wallet, which may replace a real wallet in tests"""
from decimal import Decimal
+from enum import Enum
+from typing import Optional
from test_framework.address import ADDRESS_BCRT1_P2WSH_OP_TRUE
from test_framework.key import ECKey
from test_framework.messages import (
@@ -30,22 +32,46 @@ from test_framework.util import (
)
+class MiniWalletMode(Enum):
+ """Determines the transaction type the MiniWallet is creating and spending.
+
+ For most purposes, the default mode ADDRESS_OP_TRUE should be sufficient;
+ it simply uses a fixed bech32 P2WSH address whose coins are spent with a
+ witness stack of OP_TRUE, i.e. following an anyone-can-spend policy.
+ However, if the transactions need to be modified by the user (e.g. prepending
+ scriptSig for testing opcodes that are activated by a soft-fork), or the txs
+ should contain an actual signature, the raw modes RAW_OP_TRUE and RAW_P2PK
+ can be useful. Summary of modes:
+
+ | output | | tx is | can modify | needs
+ mode | description | address | standard | scriptSig | signing
+ ----------------+-------------------+-----------+----------+------------+----------
+ ADDRESS_OP_TRUE | anyone-can-spend | bech32 | yes | no | no
+ RAW_OP_TRUE | anyone-can-spend | - (raw) | no | yes | no
+ RAW_P2PK | pay-to-public-key | - (raw) | yes | yes | yes
+ """
+ ADDRESS_OP_TRUE = 1
+ RAW_OP_TRUE = 2
+ RAW_P2PK = 3
+
+
class MiniWallet:
- def __init__(self, test_node, *, raw_script=False, use_p2pk=False):
+ def __init__(self, test_node, *, mode=MiniWalletMode.ADDRESS_OP_TRUE):
self._test_node = test_node
self._utxos = []
self._priv_key = None
self._address = None
- if raw_script:
+ assert isinstance(mode, MiniWalletMode)
+ if mode == MiniWalletMode.RAW_OP_TRUE:
self._scriptPubKey = bytes(CScript([OP_TRUE]))
- elif use_p2pk:
+ elif mode == MiniWalletMode.RAW_P2PK:
# use simple deterministic private key (k=1)
self._priv_key = ECKey()
self._priv_key.set((1).to_bytes(32, 'big'), True)
pub_key = self._priv_key.get_pubkey()
self._scriptPubKey = bytes(CScript([pub_key.get_bytes(), OP_CHECKSIG]))
- else:
+ elif mode == MiniWalletMode.ADDRESS_OP_TRUE:
self._address = ADDRESS_BCRT1_P2WSH_OP_TRUE
self._scriptPubKey = hex_str_to_bytes(self._test_node.validateaddress(self._address)['scriptPubKey'])
@@ -62,12 +88,20 @@ class MiniWallet:
if out['scriptPubKey']['hex'] == self._scriptPubKey.hex():
self._utxos.append({'txid': tx['txid'], 'vout': out['n'], 'value': out['value']})
- def sign_tx(self, tx):
+ def sign_tx(self, tx, fixed_length=True):
"""Sign tx that has been created by MiniWallet in P2PK mode"""
assert self._priv_key is not None
(sighash, err) = LegacySignatureHash(CScript(self._scriptPubKey), tx, 0, SIGHASH_ALL)
assert err is None
- tx.vin[0].scriptSig = CScript([self._priv_key.sign_ecdsa(sighash) + bytes(bytearray([SIGHASH_ALL]))])
+ # for exact fee calculation, create only signatures with fixed size by default (>49.89% probability):
+ # 65 bytes: high-R val (33 bytes) + low-S val (32 bytes)
+ # with the DER header/skeleton data of 6 bytes added, this leads to a target size of 71 bytes
+ der_sig = b''
+ while not len(der_sig) == 71:
+ der_sig = self._priv_key.sign_ecdsa(sighash)
+ if not fixed_length:
+ break
+ tx.vin[0].scriptSig = CScript([der_sig + bytes(bytearray([SIGHASH_ALL]))])
def generate(self, num_blocks):
"""Generate blocks with coinbase outputs to the internal address, and append the outputs to the internal list"""
@@ -80,12 +114,12 @@ class MiniWallet:
def get_address(self):
return self._address
- def get_utxo(self, *, txid='', mark_as_spent=True):
+ def get_utxo(self, *, txid: Optional[str]='', mark_as_spent=True):
"""
Returns a utxo and marks it as spent (pops it from the internal list)
Args:
- txid (string), optional: get the first utxo we find from a specific transaction
+ txid: get the first utxo we find from a specific transaction
Note: Can be used to get the change output immediately after a send_self_transfer
"""
@@ -98,24 +132,28 @@ class MiniWallet:
else:
return self._utxos[index]
- def send_self_transfer(self, *, fee_rate=Decimal("0.003"), from_node, utxo_to_spend=None):
+ def send_self_transfer(self, **kwargs):
"""Create and send a tx with the specified fee_rate. Fee may be exact or at most one satoshi higher than needed."""
- tx = self.create_self_transfer(fee_rate=fee_rate, from_node=from_node, utxo_to_spend=utxo_to_spend)
- self.sendrawtransaction(from_node=from_node, tx_hex=tx['hex'])
+ tx = self.create_self_transfer(**kwargs)
+ self.sendrawtransaction(from_node=kwargs['from_node'], tx_hex=tx['hex'])
return tx
- def create_self_transfer(self, *, fee_rate=Decimal("0.003"), from_node, utxo_to_spend=None, mempool_valid=True):
+ def create_self_transfer(self, *, fee_rate=Decimal("0.003"), from_node, 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."""
self._utxos = sorted(self._utxos, key=lambda k: k['value'])
utxo_to_spend = utxo_to_spend or self._utxos.pop() # Pick the largest utxo (if none provided) and hope it covers the fee
- vsize = Decimal(96)
+ if self._priv_key is None:
+ vsize = Decimal(96) # anyone-can-spend
+ else:
+ vsize = Decimal(168) # P2PK (73 bytes scriptSig + 35 bytes scriptPubKey + 60 bytes other)
send_value = satoshi_round(utxo_to_spend['value'] - fee_rate * (vsize / 1000))
fee = utxo_to_spend['value'] - send_value
assert send_value > 0
tx = CTransaction()
- tx.vin = [CTxIn(COutPoint(int(utxo_to_spend['txid'], 16), utxo_to_spend['vout']))]
+ tx.vin = [CTxIn(COutPoint(int(utxo_to_spend['txid'], 16), utxo_to_spend['vout']), nSequence=sequence)]
tx.vout = [CTxOut(int(send_value * COIN), self._scriptPubKey)]
+ tx.nLockTime = locktime
if not self._address:
# raw script
if self._priv_key is not None:
@@ -132,10 +170,7 @@ class MiniWallet:
tx_info = from_node.testmempoolaccept([tx_hex])[0]
assert_equal(mempool_valid, tx_info['allowed'])
if mempool_valid:
- # TODO: for P2PK, vsize is not constant due to varying scriptSig length,
- # so only check this for anyone-can-spend outputs right now
- if self._priv_key is None:
- assert_equal(tx_info['vsize'], vsize)
+ assert_equal(tx_info['vsize'], vsize)
assert_equal(tx_info['fees']['base'], fee)
return {'txid': tx_info['txid'], 'wtxid': tx_info['wtxid'], 'hex': tx_hex, 'tx': tx}
diff --git a/test/functional/test_framework/wallet_util.py b/test/functional/test_framework/wallet_util.py
index b9c0fb6691..acbc040741 100755
--- a/test/functional/test_framework/wallet_util.py
+++ b/test/functional/test_framework/wallet_util.py
@@ -17,17 +17,15 @@ from test_framework.address import (
from test_framework.key import ECKey
from test_framework.script import (
CScript,
- OP_0,
OP_2,
OP_3,
OP_CHECKMULTISIG,
- OP_CHECKSIG,
- OP_DUP,
- OP_EQUAL,
- OP_EQUALVERIFY,
- OP_HASH160,
- hash160,
- sha256,
+)
+from test_framework.script_util import (
+ key_to_p2pkh_script,
+ key_to_p2wpkh_script,
+ script_to_p2sh_script,
+ script_to_p2wsh_script,
)
from test_framework.util import hex_str_to_bytes
@@ -57,15 +55,14 @@ def get_key(node):
Returns a named tuple of privkey, pubkey and all address and scripts."""
addr = node.getnewaddress()
pubkey = node.getaddressinfo(addr)['pubkey']
- pkh = hash160(hex_str_to_bytes(pubkey))
return Key(privkey=node.dumpprivkey(addr),
pubkey=pubkey,
- p2pkh_script=CScript([OP_DUP, OP_HASH160, pkh, OP_EQUALVERIFY, OP_CHECKSIG]).hex(),
+ p2pkh_script=key_to_p2pkh_script(pubkey).hex(),
p2pkh_addr=key_to_p2pkh(pubkey),
- p2wpkh_script=CScript([OP_0, pkh]).hex(),
+ p2wpkh_script=key_to_p2wpkh_script(pubkey).hex(),
p2wpkh_addr=key_to_p2wpkh(pubkey),
- p2sh_p2wpkh_script=CScript([OP_HASH160, hash160(CScript([OP_0, pkh])), OP_EQUAL]).hex(),
- p2sh_p2wpkh_redeem_script=CScript([OP_0, pkh]).hex(),
+ p2sh_p2wpkh_script=script_to_p2sh_script(key_to_p2wpkh_script(pubkey)).hex(),
+ p2sh_p2wpkh_redeem_script=key_to_p2wpkh_script(pubkey).hex(),
p2sh_p2wpkh_addr=key_to_p2sh_p2wpkh(pubkey))
def get_generate_key():
@@ -76,15 +73,14 @@ def get_generate_key():
eckey.generate()
privkey = bytes_to_wif(eckey.get_bytes())
pubkey = eckey.get_pubkey().get_bytes().hex()
- pkh = hash160(hex_str_to_bytes(pubkey))
return Key(privkey=privkey,
pubkey=pubkey,
- p2pkh_script=CScript([OP_DUP, OP_HASH160, pkh, OP_EQUALVERIFY, OP_CHECKSIG]).hex(),
+ p2pkh_script=key_to_p2pkh_script(pubkey).hex(),
p2pkh_addr=key_to_p2pkh(pubkey),
- p2wpkh_script=CScript([OP_0, pkh]).hex(),
+ p2wpkh_script=key_to_p2wpkh_script(pubkey).hex(),
p2wpkh_addr=key_to_p2wpkh(pubkey),
- p2sh_p2wpkh_script=CScript([OP_HASH160, hash160(CScript([OP_0, pkh])), OP_EQUAL]).hex(),
- p2sh_p2wpkh_redeem_script=CScript([OP_0, pkh]).hex(),
+ p2sh_p2wpkh_script=script_to_p2sh_script(key_to_p2wpkh_script(pubkey)).hex(),
+ p2sh_p2wpkh_redeem_script=key_to_p2wpkh_script(pubkey).hex(),
p2sh_p2wpkh_addr=key_to_p2sh_p2wpkh(pubkey))
def get_multisig(node):
@@ -98,15 +94,15 @@ def get_multisig(node):
addrs.append(addr['address'])
pubkeys.append(addr['pubkey'])
script_code = CScript([OP_2] + [hex_str_to_bytes(pubkey) for pubkey in pubkeys] + [OP_3, OP_CHECKMULTISIG])
- witness_script = CScript([OP_0, sha256(script_code)])
+ witness_script = script_to_p2wsh_script(script_code)
return Multisig(privkeys=[node.dumpprivkey(addr) for addr in addrs],
pubkeys=pubkeys,
- p2sh_script=CScript([OP_HASH160, hash160(script_code), OP_EQUAL]).hex(),
+ p2sh_script=script_to_p2sh_script(script_code).hex(),
p2sh_addr=script_to_p2sh(script_code),
redeem_script=script_code.hex(),
p2wsh_script=witness_script.hex(),
p2wsh_addr=script_to_p2wsh(script_code),
- p2sh_p2wsh_script=CScript([OP_HASH160, witness_script, OP_EQUAL]).hex(),
+ p2sh_p2wsh_script=script_to_p2sh_script(witness_script).hex(),
p2sh_p2wsh_addr=script_to_p2sh_p2wsh(script_code))
def test_address(node, address, **kwargs):
diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py
index 00527e78f1..0a73891f2a 100755
--- a/test/functional/test_runner.py
+++ b/test/functional/test_runner.py
@@ -19,6 +19,7 @@ import datetime
import os
import time
import shutil
+import signal
import subprocess
import sys
import tempfile
@@ -108,8 +109,7 @@ BASE_SCRIPTS = [
'p2p_tx_download.py',
'mempool_updatefromblock.py',
'wallet_dump.py --legacy-wallet',
- 'wallet_listtransactions.py --legacy-wallet',
- 'wallet_listtransactions.py --descriptors',
+ 'feature_taproot.py --previous_release',
'feature_taproot.py',
'rpc_signer.py',
'wallet_signer.py --descriptors',
@@ -123,8 +123,6 @@ BASE_SCRIPTS = [
'wallet_abandonconflict.py --legacy-wallet',
'wallet_abandonconflict.py --descriptors',
'feature_csv_activation.py',
- 'rpc_rawtransaction.py --legacy-wallet',
- 'rpc_rawtransaction.py --descriptors',
'wallet_address_types.py --legacy-wallet',
'wallet_address_types.py --descriptors',
'feature_bip68_sequence.py',
@@ -138,6 +136,7 @@ BASE_SCRIPTS = [
'interface_zmq.py',
'rpc_invalid_address_message.py',
'interface_bitcoin_cli.py',
+ 'feature_bind_extra.py',
'mempool_resurrect.py',
'wallet_txn_doublespend.py --mineblock',
'tool_wallet.py --legacy-wallet',
@@ -158,6 +157,8 @@ BASE_SCRIPTS = [
'wallet_createwallet.py --legacy-wallet',
'wallet_createwallet.py --usecli',
'wallet_createwallet.py --descriptors',
+ 'wallet_listtransactions.py --legacy-wallet',
+ 'wallet_listtransactions.py --descriptors',
'wallet_watchonly.py --legacy-wallet',
'wallet_watchonly.py --usecli --legacy-wallet',
'wallet_reorgsrestore.py',
@@ -170,9 +171,12 @@ BASE_SCRIPTS = [
'feature_proxy.py',
'rpc_signrawtransaction.py --legacy-wallet',
'rpc_signrawtransaction.py --descriptors',
+ 'rpc_rawtransaction.py --legacy-wallet',
+ 'rpc_rawtransaction.py --descriptors',
'wallet_groups.py --legacy-wallet',
'p2p_addrv2_relay.py',
'wallet_groups.py --descriptors',
+ 'p2p_compactblocks_hb.py',
'p2p_disconnect_ban.py',
'rpc_decodescript.py',
'rpc_blockchain.py',
@@ -182,6 +186,7 @@ BASE_SCRIPTS = [
'p2p_addr_relay.py',
'p2p_getaddr_caching.py',
'p2p_getdata.py',
+ 'p2p_addrfetch.py',
'rpc_net.py',
'wallet_keypool.py --legacy-wallet',
'wallet_keypool.py --descriptors',
@@ -211,6 +216,7 @@ BASE_SCRIPTS = [
'mempool_package_onemore.py',
'rpc_createmultisig.py --legacy-wallet',
'rpc_createmultisig.py --descriptors',
+ 'rpc_packages.py',
'feature_versionbits_warning.py',
'rpc_preciousblock.py',
'wallet_importprunedfunds.py --legacy-wallet',
@@ -260,6 +266,7 @@ BASE_SCRIPTS = [
'wallet_send.py --legacy-wallet',
'wallet_send.py --descriptors',
'wallet_create_tx.py --descriptors',
+ 'wallet_taproot.py',
'p2p_fingerprint.py',
'feature_uacomment.py',
'wallet_coinbase_category.py --legacy-wallet',
@@ -275,6 +282,7 @@ BASE_SCRIPTS = [
'feature_asmap.py',
'mempool_unbroadcast.py',
'mempool_compatibility.py',
+ 'mempool_accept_wtxid.py',
'rpc_deriveaddresses.py',
'rpc_deriveaddresses.py --usecli',
'p2p_ping.py',
@@ -282,11 +290,14 @@ BASE_SCRIPTS = [
'feature_logging.py',
'feature_anchors.py',
'feature_coinstatsindex.py',
+ 'wallet_orphanedreward.py',
'p2p_node_network_limited.py',
'p2p_permissions.py',
'feature_blocksdir.py',
'wallet_startup.py',
+ 'p2p_i2p_ports.py',
'feature_config_args.py',
+ 'feature_presegwit_node_upgrade.py',
'feature_settings.py',
'rpc_getdescriptorinfo.py',
'rpc_addresses_deprecation.py',
@@ -545,9 +556,11 @@ def run_tests(*, test_list, src_dir, build_dir, tmpdir, jobs=1, enable_coverage=
all_passed = all(map(lambda test_result: test_result.was_successful, test_results)) and coverage_passed
- # This will be a no-op unless failfast is True in which case there may be dangling
- # processes which need to be killed.
- job_queue.kill_and_join()
+ # Clean up dangling processes if any. This may only happen with --failfast option.
+ # Killing the process group will also terminate the current process but that is
+ # not an issue
+ if len(job_queue.jobs):
+ os.killpg(os.getpgid(0), signal.SIGKILL)
sys.exit(not all_passed)
@@ -644,16 +657,6 @@ class TestHandler:
print('.', end='', flush=True)
dot_count += 1
- def kill_and_join(self):
- """Send SIGKILL to all jobs and block until all have ended."""
- procs = [i[2] for i in self.jobs]
-
- for proc in procs:
- proc.kill()
-
- for proc in procs:
- proc.wait()
-
class TestResult():
def __init__(self, name, status, time):
diff --git a/test/functional/wallet_abandonconflict.py b/test/functional/wallet_abandonconflict.py
index 2e0edcfa38..d24cc802a4 100755
--- a/test/functional/wallet_abandonconflict.py
+++ b/test/functional/wallet_abandonconflict.py
@@ -12,6 +12,7 @@
"""
from decimal import Decimal
+from test_framework.blocktools import COINBASE_MATURITY
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
@@ -28,7 +29,7 @@ class AbandonConflictTest(BitcoinTestFramework):
self.skip_if_no_wallet()
def run_test(self):
- self.nodes[1].generate(100)
+ self.nodes[1].generate(COINBASE_MATURITY)
self.sync_blocks()
balance = self.nodes[0].getbalance()
txA = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), Decimal("10"))
diff --git a/test/functional/wallet_address_types.py b/test/functional/wallet_address_types.py
index b3bee1876d..9b97d08424 100755
--- a/test/functional/wallet_address_types.py
+++ b/test/functional/wallet_address_types.py
@@ -53,6 +53,7 @@ Test that the nodes generate the correct change address type:
from decimal import Decimal
import itertools
+from test_framework.blocktools import COINBASE_MATURITY
from test_framework.test_framework import BitcoinTestFramework
from test_framework.descriptors import (
descsum_create,
@@ -220,7 +221,7 @@ class AddressTypeTest(BitcoinTestFramework):
def run_test(self):
# Mine 101 blocks on node5 to bring nodes out of IBD and make sure that
# no coinbases are maturing for the nodes-under-test during the test
- self.nodes[5].generate(101)
+ self.nodes[5].generate(COINBASE_MATURITY + 1)
self.sync_blocks()
uncompressed_1 = "0496b538e853519c726a2c91e61ec11600ae1390813a627c66fb8be7947be63c52da7589379515d4e0a604f8141781e62294721166bf621e73a82cbf2342c858ee"
@@ -258,7 +259,7 @@ class AddressTypeTest(BitcoinTestFramework):
self.log.info("Sending from node {} ({}) with{} multisig using {}".format(from_node, self.extra_args[from_node], "" if multisig else "out", "default" if address_type is None else address_type))
old_balances = self.get_balances()
self.log.debug("Old balances are {}".format(old_balances))
- to_send = (old_balances[from_node] / 101).quantize(Decimal("0.00000001"))
+ to_send = (old_balances[from_node] / (COINBASE_MATURITY + 1)).quantize(Decimal("0.00000001"))
sends = {}
addresses = {}
@@ -372,5 +373,15 @@ class AddressTypeTest(BitcoinTestFramework):
self.test_address(4, self.nodes[4].getrawchangeaddress(), multisig=False, typ='p2sh-segwit')
self.test_address(4, self.nodes[4].getrawchangeaddress('bech32'), multisig=False, typ='bech32')
+ if self.options.descriptors:
+ self.log.info("Descriptor wallets do not have bech32m addresses by default yet")
+ # TODO: Remove this when they do
+ assert_raises_rpc_error(-12, "Error: No bech32m addresses available", self.nodes[0].getnewaddress, "", "bech32m")
+ assert_raises_rpc_error(-12, "Error: No bech32m addresses available", self.nodes[0].getrawchangeaddress, "bech32m")
+ else:
+ self.log.info("Legacy wallets cannot make bech32m addresses")
+ assert_raises_rpc_error(-8, "Legacy wallets cannot provide bech32m addresses", self.nodes[0].getnewaddress, "", "bech32m")
+ assert_raises_rpc_error(-8, "Legacy wallets cannot provide bech32m addresses", self.nodes[0].getrawchangeaddress, "bech32m")
+
if __name__ == '__main__':
AddressTypeTest().main()
diff --git a/test/functional/wallet_avoidreuse.py b/test/functional/wallet_avoidreuse.py
index 1d3736d9b1..c13d8de4b5 100755
--- a/test/functional/wallet_avoidreuse.py
+++ b/test/functional/wallet_avoidreuse.py
@@ -42,25 +42,25 @@ def count_unspent(node):
r["reused"]["supported"] = supports_reused
return r
-def assert_unspent(node, total_count=None, total_sum=None, reused_supported=None, reused_count=None, reused_sum=None):
+def assert_unspent(node, total_count=None, total_sum=None, reused_supported=None, reused_count=None, reused_sum=None, margin=0.001):
'''Make assertions about a node's unspent output statistics'''
stats = count_unspent(node)
if total_count is not None:
assert_equal(stats["total"]["count"], total_count)
if total_sum is not None:
- assert_approx(stats["total"]["sum"], total_sum, 0.001)
+ assert_approx(stats["total"]["sum"], total_sum, margin)
if reused_supported is not None:
assert_equal(stats["reused"]["supported"], reused_supported)
if reused_count is not None:
assert_equal(stats["reused"]["count"], reused_count)
if reused_sum is not None:
- assert_approx(stats["reused"]["sum"], reused_sum, 0.001)
+ assert_approx(stats["reused"]["sum"], reused_sum, margin)
-def assert_balances(node, mine):
+def assert_balances(node, mine, margin=0.001):
'''Make assertions about a node's getbalances output'''
got = node.getbalances()["mine"]
for k,v in mine.items():
- assert_approx(got[k], v, 0.001)
+ assert_approx(got[k], v, margin)
class AvoidReuseTest(BitcoinTestFramework):
@@ -299,7 +299,7 @@ class AvoidReuseTest(BitcoinTestFramework):
ret_addr = self.nodes[0].getnewaddress()
# send multiple transactions, reusing one address
- for _ in range(11):
+ for _ in range(101):
self.nodes[0].sendtoaddress(new_addr, 1)
self.nodes[0].generate(1)
@@ -311,14 +311,14 @@ class AvoidReuseTest(BitcoinTestFramework):
# getbalances and listunspent should show the remaining outputs
# in the reused address as used/reused
- assert_unspent(self.nodes[1], total_count=2, total_sum=6, reused_count=1, reused_sum=1)
- assert_balances(self.nodes[1], mine={"used": 1, "trusted": 5})
+ assert_unspent(self.nodes[1], total_count=2, total_sum=96, reused_count=1, reused_sum=1, margin=0.01)
+ assert_balances(self.nodes[1], mine={"used": 1, "trusted": 95}, margin=0.01)
def test_full_destination_group_is_preferred(self):
'''
- Test the case where [1] only has 11 outputs of 1 BTC in the same reused
+ Test the case where [1] only has 101 outputs of 1 BTC in the same reused
address and tries to send a small payment of 0.5 BTC. The wallet
- should use 10 outputs from the reused address as inputs and not a
+ should use 100 outputs from the reused address as inputs and not a
single 1 BTC input, in order to join several outputs from the reused
address.
'''
@@ -330,8 +330,8 @@ class AvoidReuseTest(BitcoinTestFramework):
new_addr = self.nodes[1].getnewaddress()
ret_addr = self.nodes[0].getnewaddress()
- # Send 11 outputs of 1 BTC to the same, reused address in the wallet
- for _ in range(11):
+ # Send 101 outputs of 1 BTC to the same, reused address in the wallet
+ for _ in range(101):
self.nodes[0].sendtoaddress(new_addr, 1)
self.nodes[0].generate(1)
@@ -342,14 +342,14 @@ class AvoidReuseTest(BitcoinTestFramework):
txid = self.nodes[1].sendtoaddress(address=ret_addr, amount=0.5)
inputs = self.nodes[1].getrawtransaction(txid, 1)["vin"]
- # The transaction should use 10 inputs exactly
- assert_equal(len(inputs), 10)
+ # The transaction should use 100 inputs exactly
+ assert_equal(len(inputs), 100)
def test_all_destination_groups_are_used(self):
'''
- Test the case where [1] only has 22 outputs of 1 BTC in the same reused
- address and tries to send a payment of 20.5 BTC. The wallet
- should use all 22 outputs from the reused address as inputs.
+ Test the case where [1] only has 202 outputs of 1 BTC in the same reused
+ address and tries to send a payment of 200.5 BTC. The wallet
+ should use all 202 outputs from the reused address as inputs.
'''
self.log.info("Test that all destination groups are used")
@@ -359,20 +359,20 @@ class AvoidReuseTest(BitcoinTestFramework):
new_addr = self.nodes[1].getnewaddress()
ret_addr = self.nodes[0].getnewaddress()
- # Send 22 outputs of 1 BTC to the same, reused address in the wallet
- for _ in range(22):
+ # Send 202 outputs of 1 BTC to the same, reused address in the wallet
+ for _ in range(202):
self.nodes[0].sendtoaddress(new_addr, 1)
self.nodes[0].generate(1)
self.sync_all()
# Sending a transaction that needs to use the full groups
- # of 10 inputs but also the incomplete group of 2 inputs.
- txid = self.nodes[1].sendtoaddress(address=ret_addr, amount=20.5)
+ # of 100 inputs but also the incomplete group of 2 inputs.
+ txid = self.nodes[1].sendtoaddress(address=ret_addr, amount=200.5)
inputs = self.nodes[1].getrawtransaction(txid, 1)["vin"]
- # The transaction should use 22 inputs exactly
- assert_equal(len(inputs), 22)
+ # The transaction should use 202 inputs exactly
+ assert_equal(len(inputs), 202)
if __name__ == '__main__':
diff --git a/test/functional/wallet_backup.py b/test/functional/wallet_backup.py
index f34c1345e0..05a0ef0ea1 100755
--- a/test/functional/wallet_backup.py
+++ b/test/functional/wallet_backup.py
@@ -35,6 +35,7 @@ import os
from random import randint
import shutil
+from test_framework.blocktools import COINBASE_MATURITY
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
@@ -123,7 +124,7 @@ class WalletBackupTest(BitcoinTestFramework):
self.sync_blocks()
self.nodes[2].generate(1)
self.sync_blocks()
- self.nodes[3].generate(100)
+ self.nodes[3].generate(COINBASE_MATURITY)
self.sync_blocks()
assert_equal(self.nodes[0].getbalance(), 50)
@@ -152,7 +153,7 @@ class WalletBackupTest(BitcoinTestFramework):
self.do_one_round()
# Generate 101 more blocks, so any fees paid mature
- self.nodes[3].generate(101)
+ self.nodes[3].generate(COINBASE_MATURITY + 1)
self.sync_all()
balance0 = self.nodes[0].getbalance()
diff --git a/test/functional/wallet_balance.py b/test/functional/wallet_balance.py
index 433b40faee..204a866c55 100755
--- a/test/functional/wallet_balance.py
+++ b/test/functional/wallet_balance.py
@@ -7,6 +7,7 @@ from decimal import Decimal
import struct
from test_framework.address import ADDRESS_BCRT1_UNSPENDABLE as ADDRESS_WATCHONLY
+from test_framework.blocktools import COINBASE_MATURITY
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
@@ -72,7 +73,7 @@ class WalletTest(BitcoinTestFramework):
self.nodes[0].generate(1)
self.sync_all()
self.nodes[1].generate(1)
- self.nodes[1].generatetoaddress(101, ADDRESS_WATCHONLY)
+ self.nodes[1].generatetoaddress(COINBASE_MATURITY + 1, ADDRESS_WATCHONLY)
self.sync_all()
if not self.options.descriptors:
diff --git a/test/functional/wallet_basic.py b/test/functional/wallet_basic.py
index 4a1d25bbc5..b5afc3785e 100755
--- a/test/functional/wallet_basic.py
+++ b/test/functional/wallet_basic.py
@@ -6,6 +6,7 @@
from decimal import Decimal
from itertools import product
+from test_framework.blocktools import COINBASE_MATURITY
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_array_result,
@@ -65,7 +66,7 @@ class WalletTest(BitcoinTestFramework):
assert_equal(walletinfo['balance'], 0)
self.sync_all(self.nodes[0:3])
- self.nodes[1].generate(101)
+ self.nodes[1].generate(COINBASE_MATURITY + 1)
self.sync_all(self.nodes[0:3])
assert_equal(self.nodes[0].getbalance(), 50)
@@ -158,7 +159,7 @@ class WalletTest(BitcoinTestFramework):
assert_equal(len(self.nodes[1].listlockunspent()), 0)
# Have node1 generate 100 blocks (so node0 can recover the fee)
- self.nodes[1].generate(100)
+ self.nodes[1].generate(COINBASE_MATURITY)
self.sync_all(self.nodes[0:3])
# node0 should end up with 100 btc in block rewards plus fees, but
@@ -419,6 +420,9 @@ class WalletTest(BitcoinTestFramework):
# This will raise an exception for importing an invalid pubkey
assert_raises_rpc_error(-5, "Pubkey is not a valid public key", self.nodes[0].importpubkey, "5361746f736869204e616b616d6f746f")
+ # Bech32m addresses cannot be imported into a legacy wallet
+ assert_raises_rpc_error(-5, "Bech32m addresses cannot be imported into legacy wallets", self.nodes[0].importaddress, "bcrt1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vqc8gma6")
+
# Import address and private key to check correct behavior of spendable unspents
# 1. Send some coins to generate new UTXO
address_to_import = self.nodes[2].getnewaddress()
diff --git a/test/functional/wallet_bumpfee.py b/test/functional/wallet_bumpfee.py
index ff5070c1fa..c04986038d 100755
--- a/test/functional/wallet_bumpfee.py
+++ b/test/functional/wallet_bumpfee.py
@@ -14,16 +14,23 @@ added in the future, they should try to follow the same convention and not
make assumptions about execution order.
"""
from decimal import Decimal
-import io
-from test_framework.blocktools import add_witness_commitment, create_block, create_coinbase, send_to_witness
-from test_framework.messages import BIP125_SEQUENCE_NUMBER, CTransaction
+from test_framework.blocktools import (
+ COINBASE_MATURITY,
+ add_witness_commitment,
+ create_block,
+ create_coinbase,
+ send_to_witness,
+)
+from test_framework.messages import (
+ BIP125_SEQUENCE_NUMBER,
+ tx_from_hex,
+)
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
assert_greater_than,
assert_raises_rpc_error,
- hex_str_to_bytes,
)
WALLET_PASSPHRASE = "test"
@@ -265,7 +272,7 @@ def test_small_output_with_feerate_succeeds(self, rbf_node, dest_address):
self.log.info('Testing small output with feerate bump succeeds')
# Make sure additional inputs exist
- rbf_node.generatetoaddress(101, rbf_node.getnewaddress())
+ rbf_node.generatetoaddress(COINBASE_MATURITY + 1, rbf_node.getnewaddress())
rbfid = spend_one_input(rbf_node, dest_address)
input_list = rbf_node.getrawtransaction(rbfid, 1)["vin"]
assert_equal(len(input_list), 1)
@@ -575,9 +582,7 @@ def spend_one_input(node, dest_address, change_size=Decimal("0.00049000")):
def submit_block_with_tx(node, tx):
- ctx = CTransaction()
- ctx.deserialize(io.BytesIO(hex_str_to_bytes(tx)))
-
+ ctx = tx_from_hex(tx)
tip = node.getbestblockhash()
height = node.getblockcount() + 1
block_time = node.getblockheader(tip)["mediantime"] + 1
diff --git a/test/functional/wallet_descriptor.py b/test/functional/wallet_descriptor.py
index 1e032bdd6c..c6f5d334f8 100755
--- a/test/functional/wallet_descriptor.py
+++ b/test/functional/wallet_descriptor.py
@@ -4,6 +4,7 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test descriptor wallet function."""
+from test_framework.blocktools import COINBASE_MATURITY
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
@@ -83,7 +84,7 @@ class WalletDescriptorTest(BitcoinTestFramework):
send_wrpc = self.nodes[0].get_wallet_rpc("desc1")
# Generate some coins
- send_wrpc.generatetoaddress(101, send_wrpc.getnewaddress())
+ send_wrpc.generatetoaddress(COINBASE_MATURITY + 1, send_wrpc.getnewaddress())
# Make transactions
self.log.info("Test sending and receiving")
diff --git a/test/functional/wallet_dump.py b/test/functional/wallet_dump.py
index eb54da99f5..91d6121679 100755
--- a/test/functional/wallet_dump.py
+++ b/test/functional/wallet_dump.py
@@ -209,6 +209,15 @@ class WalletDumpTest(BitcoinTestFramework):
with self.nodes[0].assert_debug_log(['Flushing wallet.dat'], timeout=20):
self.nodes[0].getnewaddress()
+ # Make sure that dumpwallet doesn't have a lock order issue when there is an unconfirmed tx and it is reloaded
+ # See https://github.com/bitcoin/bitcoin/issues/22489
+ self.nodes[0].createwallet("w3")
+ w3 = self.nodes[0].get_wallet_rpc("w3")
+ w3.importprivkey(privkey=self.nodes[0].get_deterministic_priv_key().key, label="coinbase_import")
+ w3.sendtoaddress(w3.getnewaddress(), 10)
+ w3.unloadwallet()
+ self.nodes[0].loadwallet("w3")
+ w3.dumpwallet(os.path.join(self.nodes[0].datadir, "w3.dump"))
if __name__ == '__main__':
WalletDumpTest().main()
diff --git a/test/functional/wallet_fallbackfee.py b/test/functional/wallet_fallbackfee.py
index 78eef4b790..b28f3ecebc 100755
--- a/test/functional/wallet_fallbackfee.py
+++ b/test/functional/wallet_fallbackfee.py
@@ -3,6 +3,8 @@
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test wallet replace-by-fee capabilities in conjunction with the fallbackfee."""
+
+from test_framework.blocktools import COINBASE_MATURITY
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_raises_rpc_error
@@ -15,7 +17,7 @@ class WalletRBFTest(BitcoinTestFramework):
self.skip_if_no_wallet()
def run_test(self):
- self.nodes[0].generate(101)
+ self.nodes[0].generate(COINBASE_MATURITY + 1)
# sending a transaction without fee estimations must be possible by default on regtest
self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 1)
diff --git a/test/functional/wallet_groups.py b/test/functional/wallet_groups.py
index c0b76d960f..d9d135a986 100755
--- a/test/functional/wallet_groups.py
+++ b/test/functional/wallet_groups.py
@@ -4,8 +4,11 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test wallet group functionality."""
+from test_framework.blocktools import COINBASE_MATURITY
from test_framework.test_framework import BitcoinTestFramework
-from test_framework.messages import CTransaction, FromHex, ToHex
+from test_framework.messages import (
+ tx_from_hex,
+)
from test_framework.util import (
assert_approx,
assert_equal,
@@ -31,7 +34,7 @@ class WalletGroupTest(BitcoinTestFramework):
def run_test(self):
self.log.info("Setting up")
# Mine some coins
- self.nodes[0].generate(101)
+ self.nodes[0].generate(COINBASE_MATURITY + 1)
# Get some addresses from the two nodes
addr1 = [self.nodes[1].getnewaddress() for _ in range(3)]
@@ -153,10 +156,10 @@ class WalletGroupTest(BitcoinTestFramework):
self.log.info("Fill a wallet with 10,000 outputs corresponding to the same scriptPubKey")
for _ in range(5):
raw_tx = self.nodes[0].createrawtransaction([{"txid":"0"*64, "vout":0}], [{addr2[0]: 0.05}])
- tx = FromHex(CTransaction(), raw_tx)
+ tx = tx_from_hex(raw_tx)
tx.vin = []
tx.vout = [tx.vout[0]] * 2000
- funded_tx = self.nodes[0].fundrawtransaction(ToHex(tx))
+ funded_tx = self.nodes[0].fundrawtransaction(tx.serialize().hex())
signed_tx = self.nodes[0].signrawtransactionwithwallet(funded_tx['hex'])
self.nodes[0].sendrawtransaction(signed_tx['hex'])
self.nodes[0].generate(1)
diff --git a/test/functional/wallet_hd.py b/test/functional/wallet_hd.py
index 23d132df41..d41a389197 100755
--- a/test/functional/wallet_hd.py
+++ b/test/functional/wallet_hd.py
@@ -7,6 +7,7 @@
import os
import shutil
+from test_framework.blocktools import COINBASE_MATURITY
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
@@ -48,7 +49,7 @@ class WalletHDTest(BitcoinTestFramework):
# Derive some HD addresses and remember the last
# Also send funds to each add
- self.nodes[0].generate(101)
+ self.nodes[0].generate(COINBASE_MATURITY + 1)
hd_add = None
NUM_HD_ADDS = 10
for i in range(1, NUM_HD_ADDS + 1):
diff --git a/test/functional/wallet_importdescriptors.py b/test/functional/wallet_importdescriptors.py
index 0a3dd56620..262175c789 100755
--- a/test/functional/wallet_importdescriptors.py
+++ b/test/functional/wallet_importdescriptors.py
@@ -16,6 +16,7 @@ variants.
and test the values returned."""
from test_framework.address import key_to_p2pkh
+from test_framework.blocktools import COINBASE_MATURITY
from test_framework.test_framework import BitcoinTestFramework
from test_framework.descriptors import descsum_create
from test_framework.util import (
@@ -73,12 +74,11 @@ class ImportDescriptorsTest(BitcoinTestFramework):
assert_equal(wpriv.getwalletinfo()['keypoolsize'], 0)
self.log.info('Mining coins')
- w0.generatetoaddress(101, w0.getnewaddress())
+ w0.generatetoaddress(COINBASE_MATURITY + 1, w0.getnewaddress())
# RPC importdescriptors -----------------------------------------------
# # Test import fails if no descriptor present
- key = get_generate_key()
self.log.info("Import should fail if a descriptor is not provided")
self.test_importdesc({"timestamp": "now"},
success=False,
@@ -88,10 +88,10 @@ class ImportDescriptorsTest(BitcoinTestFramework):
# # Test importing of a P2PKH descriptor
key = get_generate_key()
self.log.info("Should import a p2pkh descriptor")
- self.test_importdesc({"desc": descsum_create("pkh(" + key.pubkey + ")"),
- "timestamp": "now",
- "label": "Descriptor import test"},
- success=True)
+ import_request = {"desc": descsum_create("pkh(" + key.pubkey + ")"),
+ "timestamp": "now",
+ "label": "Descriptor import test"}
+ self.test_importdesc(import_request, success=True)
test_address(w1,
key.p2pkh_addr,
solvable=True,
@@ -99,11 +99,15 @@ class ImportDescriptorsTest(BitcoinTestFramework):
labels=["Descriptor import test"])
assert_equal(w1.getwalletinfo()['keypoolsize'], 0)
+ self.log.info("Test can import same descriptor with public key twice")
+ self.test_importdesc(import_request, success=True)
+
+ self.log.info("Test can update descriptor label")
+ self.test_importdesc({**import_request, "label": "Updated label"}, success=True)
+ test_address(w1, key.p2pkh_addr, solvable=True, ismine=True, labels=["Updated label"])
+
self.log.info("Internal addresses cannot have labels")
- self.test_importdesc({"desc": descsum_create("pkh(" + key.pubkey + ")"),
- "timestamp": "now",
- "internal": True,
- "label": "Descriptor import test"},
+ self.test_importdesc({**import_request, "internal": True},
success=False,
error_code=-8,
error_message="Internal addresses should not have a label")
@@ -251,6 +255,39 @@ class ImportDescriptorsTest(BitcoinTestFramework):
self.test_importdesc({"desc": descsum_create(desc), "timestamp": "now", "range": [0, 1000001]},
success=False, error_code=-8, error_message='Range is too large')
+ self.log.info("Verify we can only extend descriptor's range")
+ range_request = {"desc": descsum_create(desc), "timestamp": "now", "range": [5, 10], 'active': True}
+ self.test_importdesc(range_request, wallet=wpriv, success=True)
+ assert_equal(wpriv.getwalletinfo()['keypoolsize'], 6)
+ self.test_importdesc({**range_request, "range": [0, 10]}, wallet=wpriv, success=True)
+ assert_equal(wpriv.getwalletinfo()['keypoolsize'], 11)
+ self.test_importdesc({**range_request, "range": [0, 20]}, wallet=wpriv, success=True)
+ assert_equal(wpriv.getwalletinfo()['keypoolsize'], 21)
+ # Can keep range the same
+ self.test_importdesc({**range_request, "range": [0, 20]}, wallet=wpriv, success=True)
+ assert_equal(wpriv.getwalletinfo()['keypoolsize'], 21)
+
+ self.test_importdesc({**range_request, "range": [5, 10]}, wallet=wpriv, success=False,
+ error_code=-8, error_message='new range must include current range = [0,20]')
+ self.test_importdesc({**range_request, "range": [0, 10]}, wallet=wpriv, success=False,
+ error_code=-8, error_message='new range must include current range = [0,20]')
+ self.test_importdesc({**range_request, "range": [5, 20]}, wallet=wpriv, success=False,
+ error_code=-8, error_message='new range must include current range = [0,20]')
+ assert_equal(wpriv.getwalletinfo()['keypoolsize'], 21)
+
+ self.log.info("Check we can change descriptor internal flag")
+ self.test_importdesc({**range_request, "range": [0, 20], "internal": True}, wallet=wpriv, success=True)
+ assert_equal(wpriv.getwalletinfo()['keypoolsize'], 0)
+ assert_raises_rpc_error(-4, 'This wallet has no available keys', wpriv.getnewaddress, '', 'p2sh-segwit')
+ assert_equal(wpriv.getwalletinfo()['keypoolsize_hd_internal'], 21)
+ wpriv.getrawchangeaddress('p2sh-segwit')
+
+ self.test_importdesc({**range_request, "range": [0, 20], "internal": False}, wallet=wpriv, success=True)
+ assert_equal(wpriv.getwalletinfo()['keypoolsize'], 21)
+ wpriv.getnewaddress('', 'p2sh-segwit')
+ assert_equal(wpriv.getwalletinfo()['keypoolsize_hd_internal'], 0)
+ assert_raises_rpc_error(-4, 'This wallet has no available keys', wpriv.getrawchangeaddress, 'p2sh-segwit')
+
# Make sure ranged imports import keys in order
w1 = self.nodes[1].get_wallet_rpc('w1')
self.log.info('Key ranges should be imported in order')
@@ -302,9 +339,21 @@ class ImportDescriptorsTest(BitcoinTestFramework):
w1.keypoolrefill()
assert_equal(w1.getwalletinfo()['keypoolsize'], 5 * 3)
+ self.log.info("Check we can change next_index")
+ # go back and forth with next_index
+ for i in [4, 0, 2, 1, 3]:
+ self.test_importdesc({'desc': descsum_create('wpkh([80002067/0h/0h]' + xpub + '/*)'),
+ 'active': True,
+ 'range': [0, 9],
+ 'next_index': i,
+ 'timestamp': 'now'
+ },
+ success=True)
+ assert_equal(w1.getnewaddress('', 'bech32'), addresses[i])
+
# Check active=False default
self.log.info('Check imported descriptors are not active by default')
- self.test_importdesc({'desc': descsum_create('pkh([12345678/0h/0h]' + xpub + '/*)'),
+ self.test_importdesc({'desc': descsum_create('pkh([12345678/1h]' + xpub + '/*)'),
'range' : [0, 2],
'timestamp': 'now',
'internal': True
@@ -312,6 +361,32 @@ class ImportDescriptorsTest(BitcoinTestFramework):
success=True)
assert_raises_rpc_error(-4, 'This wallet has no available keys', w1.getrawchangeaddress, 'legacy')
+ self.log.info('Check can activate inactive descriptor')
+ self.test_importdesc({'desc': descsum_create('pkh([12345678]' + xpub + '/*)'),
+ 'range': [0, 5],
+ 'active': True,
+ 'timestamp': 'now',
+ 'internal': True
+ },
+ success=True)
+ address = w1.getrawchangeaddress('legacy')
+ assert_equal(address, "mpA2Wh9dvZT7yfELq1UnrUmAoc5qCkMetg")
+
+ self.log.info('Check can deactivate active descriptor')
+ self.test_importdesc({'desc': descsum_create('pkh([12345678]' + xpub + '/*)'),
+ 'range': [0, 5],
+ 'active': False,
+ 'timestamp': 'now',
+ 'internal': True
+ },
+ success=True)
+ assert_raises_rpc_error(-4, 'This wallet has no available keys', w1.getrawchangeaddress, 'legacy')
+
+ self.log.info('Verify activation state is persistent')
+ w1.unloadwallet()
+ self.nodes[1].loadwallet('w1')
+ assert_raises_rpc_error(-4, 'This wallet has no available keys', w1.getrawchangeaddress, 'legacy')
+
# # Test importing a descriptor containing a WIF private key
wif_priv = "cTe1f5rdT8A8DFgVWTjyPwACsDPJM9ff4QngFxUixCSvvbg1x6sh"
address = "2MuhcG52uHPknxDgmGPsV18jSHFBnnRgjPg"
@@ -321,6 +396,10 @@ class ImportDescriptorsTest(BitcoinTestFramework):
"timestamp": "now"},
success=True,
wallet=wpriv)
+
+ self.log.info('Test can import same descriptor with private key twice')
+ self.test_importdesc({"desc": descsum_create(desc), "timestamp": "now"}, success=True, wallet=wpriv)
+
test_address(wpriv,
address,
solvable=True,
@@ -338,14 +417,25 @@ class ImportDescriptorsTest(BitcoinTestFramework):
wmulti_priv = self.nodes[1].get_wallet_rpc("wmulti_priv")
assert_equal(wmulti_priv.getwalletinfo()['keypoolsize'], 0)
- self.test_importdesc({"desc":"wsh(multi(2,tprv8ZgxMBicQKsPevADjDCWsa6DfhkVXicu8NQUzfibwX2MexVwW4tCec5mXdCW8kJwkzBRRmAay1KZya4WsehVvjTGVW6JLqiqd8DdZ4xSg52/84h/0h/0h/*,tprv8ZgxMBicQKsPdSNWUhDiwTScDr6JfkZuLshTRwzvZGnMSnGikV6jxpmdDkC3YRc4T3GD6Nvg9uv6hQg73RVv1EiTXDZwxVbsLugVHU8B1aq/84h/0h/0h/*,tprv8ZgxMBicQKsPeonDt8Ka2mrQmHa61hQ5FQCsvWBTpSNzBFgM58cV2EuXNAHF14VawVpznnme3SuTbA62sGriwWyKifJmXntfNeK7zeqMCj1/84h/0h/0h/*))#m2sr93jn",
+ xprv1 = 'tprv8ZgxMBicQKsPevADjDCWsa6DfhkVXicu8NQUzfibwX2MexVwW4tCec5mXdCW8kJwkzBRRmAay1KZya4WsehVvjTGVW6JLqiqd8DdZ4xSg52'
+ acc_xpub1 = 'tpubDCJtdt5dgJpdhW4MtaVYDhG4T4tF6jcLR1PxL43q9pq1mxvXgMS9Mzw1HnXG15vxUGQJMMSqCQHMTy3F1eW5VkgVroWzchsPD5BUojrcWs8' # /84'/0'/0'
+ chg_xpub1 = 'tpubDCXqdwWZcszwqYJSnZp8eARkxGJfHAk23KDxbztV4BbschfaTfYLTcSkSJ3TN64dRqwa1rnFUScsYormKkGqNbbPwkorQimVevXjxzUV9Gf' # /84'/1'/0'
+ xprv2 = 'tprv8ZgxMBicQKsPdSNWUhDiwTScDr6JfkZuLshTRwzvZGnMSnGikV6jxpmdDkC3YRc4T3GD6Nvg9uv6hQg73RVv1EiTXDZwxVbsLugVHU8B1aq'
+ acc_xprv2 = 'tprv8gVCsmRAxVSxyUpsL13Y7ZEWBFPWbgS5E2MmFVNGuANrknvmmn2vWnmHvU8AwEFYzR2ji6EeZLSCLVacsYkvor3Pcb5JY5FGcevqTwYvdYx'
+ acc_xpub2 = 'tpubDDBF2BTR6s8drwrfDei8WxtckGuSm1cyoKxYY1QaKSBFbHBYQArWhHPA6eJrzZej6nfHGLSURYSLHr7GuYch8aY5n61tGqgn8b4cXrMuoPH'
+ chg_xpub2 = 'tpubDCYfZY2ceyHzYzMMVPt9MNeiqtQ2T7Uyp9QSFwYXh8Vi9iJFYXcuphJaGXfF3jUQJi5Y3GMNXvM11gaL4txzZgNGK22BFAwMXynnzv4z2Jh'
+ xprv3 = 'tprv8ZgxMBicQKsPeonDt8Ka2mrQmHa61hQ5FQCsvWBTpSNzBFgM58cV2EuXNAHF14VawVpznnme3SuTbA62sGriwWyKifJmXntfNeK7zeqMCj1'
+ acc_xpub3 = 'tpubDCsWoW1kuQB9kG5MXewHqkbjPtqPueRnXju7uM2NK7y3JYb2ajAZ9EiuZXNNuE4661RAfriBWhL8UsnAPpk8zrKKnZw1Ug7X4oHgMdZiU4E'
+ chg_xpub3 = 'tpubDC6UGqnsQStngYuGD4MKsMy7eD1Yg9NTJfPdvjdG2JE5oZ7EsSL3WHg4Gsw2pR5K39ZwJ46M1wZayhedVdQtMGaUhq5S23PH6fnENK3V1sb'
+
+ self.test_importdesc({"desc":"wsh(multi(2," + xprv1 + "/84h/0h/0h/*," + xprv2 + "/84h/0h/0h/*," + xprv3 + "/84h/0h/0h/*))#m2sr93jn",
"active": True,
"range": 1000,
"next_index": 0,
"timestamp": "now"},
success=True,
wallet=wmulti_priv)
- self.test_importdesc({"desc":"wsh(multi(2,tprv8ZgxMBicQKsPevADjDCWsa6DfhkVXicu8NQUzfibwX2MexVwW4tCec5mXdCW8kJwkzBRRmAay1KZya4WsehVvjTGVW6JLqiqd8DdZ4xSg52/84h/1h/0h/*,tprv8ZgxMBicQKsPdSNWUhDiwTScDr6JfkZuLshTRwzvZGnMSnGikV6jxpmdDkC3YRc4T3GD6Nvg9uv6hQg73RVv1EiTXDZwxVbsLugVHU8B1aq/84h/1h/0h/*,tprv8ZgxMBicQKsPeonDt8Ka2mrQmHa61hQ5FQCsvWBTpSNzBFgM58cV2EuXNAHF14VawVpznnme3SuTbA62sGriwWyKifJmXntfNeK7zeqMCj1/84h/1h/0h/*))#q3sztvx5",
+ self.test_importdesc({"desc":"wsh(multi(2," + xprv1 + "/84h/1h/0h/*," + xprv2 + "/84h/1h/0h/*," + xprv3 + "/84h/1h/0h/*))#q3sztvx5",
"active": True,
"internal" : True,
"range": 1000,
@@ -373,14 +463,14 @@ class ImportDescriptorsTest(BitcoinTestFramework):
wmulti_pub = self.nodes[1].get_wallet_rpc("wmulti_pub")
assert_equal(wmulti_pub.getwalletinfo()['keypoolsize'], 0)
- self.test_importdesc({"desc":"wsh(multi(2,[7b2d0242/84h/0h/0h]tpubDCJtdt5dgJpdhW4MtaVYDhG4T4tF6jcLR1PxL43q9pq1mxvXgMS9Mzw1HnXG15vxUGQJMMSqCQHMTy3F1eW5VkgVroWzchsPD5BUojrcWs8/*,[59b09cd6/84h/0h/0h]tpubDDBF2BTR6s8drwrfDei8WxtckGuSm1cyoKxYY1QaKSBFbHBYQArWhHPA6eJrzZej6nfHGLSURYSLHr7GuYch8aY5n61tGqgn8b4cXrMuoPH/*,[e81a0532/84h/0h/0h]tpubDCsWoW1kuQB9kG5MXewHqkbjPtqPueRnXju7uM2NK7y3JYb2ajAZ9EiuZXNNuE4661RAfriBWhL8UsnAPpk8zrKKnZw1Ug7X4oHgMdZiU4E/*))#tsry0s5e",
+ self.test_importdesc({"desc":"wsh(multi(2,[7b2d0242/84h/0h/0h]" + acc_xpub1 + "/*,[59b09cd6/84h/0h/0h]" + acc_xpub2 + "/*,[e81a0532/84h/0h/0h]" + acc_xpub3 +"/*))#tsry0s5e",
"active": True,
"range": 1000,
"next_index": 0,
"timestamp": "now"},
success=True,
wallet=wmulti_pub)
- self.test_importdesc({"desc":"wsh(multi(2,[7b2d0242/84h/1h/0h]tpubDCXqdwWZcszwqYJSnZp8eARkxGJfHAk23KDxbztV4BbschfaTfYLTcSkSJ3TN64dRqwa1rnFUScsYormKkGqNbbPwkorQimVevXjxzUV9Gf/*,[59b09cd6/84h/1h/0h]tpubDCYfZY2ceyHzYzMMVPt9MNeiqtQ2T7Uyp9QSFwYXh8Vi9iJFYXcuphJaGXfF3jUQJi5Y3GMNXvM11gaL4txzZgNGK22BFAwMXynnzv4z2Jh/*,[e81a0532/84h/1h/0h]tpubDC6UGqnsQStngYuGD4MKsMy7eD1Yg9NTJfPdvjdG2JE5oZ7EsSL3WHg4Gsw2pR5K39ZwJ46M1wZayhedVdQtMGaUhq5S23PH6fnENK3V1sb/*))#c08a2rzv",
+ self.test_importdesc({"desc":"wsh(multi(2,[7b2d0242/84h/1h/0h]" + chg_xpub1 + "/*,[59b09cd6/84h/1h/0h]" + chg_xpub2 + "/*,[e81a0532/84h/1h/0h]" + chg_xpub3 + "/*))#c08a2rzv",
"active": True,
"internal" : True,
"range": 1000,
@@ -395,8 +485,15 @@ class ImportDescriptorsTest(BitcoinTestFramework):
change_addr = wmulti_pub.getrawchangeaddress('bech32')
assert_equal(change_addr, 'bcrt1qt9uhe3a9hnq7vajl7a094z4s3crm9ttf8zw3f5v9gr2nyd7e3lnsy44n8e')
assert_equal(wmulti_pub.getwalletinfo()['keypoolsize'], 999)
+
+ # generate some utxos for next tests
txid = w0.sendtoaddress(addr, 10)
vout = find_vout_for_address(self.nodes[0], txid, addr)
+
+ addr2 = wmulti_pub.getnewaddress('', 'bech32')
+ txid2 = w0.sendtoaddress(addr2, 10)
+ vout2 = find_vout_for_address(self.nodes[0], txid2, addr2)
+
self.nodes[0].generate(6)
self.sync_all()
assert_equal(wmulti_pub.getbalance(), wmulti_priv.getbalance())
@@ -410,14 +507,14 @@ class ImportDescriptorsTest(BitcoinTestFramework):
wmulti_priv1 = self.nodes[1].get_wallet_rpc("wmulti_priv1")
res = wmulti_priv1.importdescriptors([
{
- "desc": descsum_create("wsh(multi(2,tprv8ZgxMBicQKsPevADjDCWsa6DfhkVXicu8NQUzfibwX2MexVwW4tCec5mXdCW8kJwkzBRRmAay1KZya4WsehVvjTGVW6JLqiqd8DdZ4xSg52/84h/0h/0h/*,[59b09cd6/84h/0h/0h]tpubDDBF2BTR6s8drwrfDei8WxtckGuSm1cyoKxYY1QaKSBFbHBYQArWhHPA6eJrzZej6nfHGLSURYSLHr7GuYch8aY5n61tGqgn8b4cXrMuoPH/*,[e81a0532/84h/0h/0h]tpubDCsWoW1kuQB9kG5MXewHqkbjPtqPueRnXju7uM2NK7y3JYb2ajAZ9EiuZXNNuE4661RAfriBWhL8UsnAPpk8zrKKnZw1Ug7X4oHgMdZiU4E/*))"),
+ "desc": descsum_create("wsh(multi(2," + xprv1 + "/84h/0h/0h/*,[59b09cd6/84h/0h/0h]" + acc_xpub2 + "/*,[e81a0532/84h/0h/0h]" + acc_xpub3 + "/*))"),
"active": True,
"range": 1000,
"next_index": 0,
"timestamp": "now"
},
{
- "desc": descsum_create("wsh(multi(2,tprv8ZgxMBicQKsPevADjDCWsa6DfhkVXicu8NQUzfibwX2MexVwW4tCec5mXdCW8kJwkzBRRmAay1KZya4WsehVvjTGVW6JLqiqd8DdZ4xSg52/84h/1h/0h/*,[59b09cd6/84h/1h/0h]tpubDCYfZY2ceyHzYzMMVPt9MNeiqtQ2T7Uyp9QSFwYXh8Vi9iJFYXcuphJaGXfF3jUQJi5Y3GMNXvM11gaL4txzZgNGK22BFAwMXynnzv4z2Jh/*,[e81a0532/84h/1h/0h]tpubDC6UGqnsQStngYuGD4MKsMy7eD1Yg9NTJfPdvjdG2JE5oZ7EsSL3WHg4Gsw2pR5K39ZwJ46M1wZayhedVdQtMGaUhq5S23PH6fnENK3V1sb/*))"),
+ "desc": descsum_create("wsh(multi(2," + xprv1 + "/84h/1h/0h/*,[59b09cd6/84h/1h/0h]" + chg_xpub2 + "/*,[e81a0532/84h/1h/0h]" + chg_xpub3 + "/*))"),
"active": True,
"internal" : True,
"range": 1000,
@@ -433,14 +530,14 @@ class ImportDescriptorsTest(BitcoinTestFramework):
wmulti_priv2 = self.nodes[1].get_wallet_rpc('wmulti_priv2')
res = wmulti_priv2.importdescriptors([
{
- "desc": descsum_create("wsh(multi(2,[7b2d0242/84h/0h/0h]tpubDCJtdt5dgJpdhW4MtaVYDhG4T4tF6jcLR1PxL43q9pq1mxvXgMS9Mzw1HnXG15vxUGQJMMSqCQHMTy3F1eW5VkgVroWzchsPD5BUojrcWs8/*,tprv8ZgxMBicQKsPdSNWUhDiwTScDr6JfkZuLshTRwzvZGnMSnGikV6jxpmdDkC3YRc4T3GD6Nvg9uv6hQg73RVv1EiTXDZwxVbsLugVHU8B1aq/84h/0h/0h/*,[e81a0532/84h/0h/0h]tpubDCsWoW1kuQB9kG5MXewHqkbjPtqPueRnXju7uM2NK7y3JYb2ajAZ9EiuZXNNuE4661RAfriBWhL8UsnAPpk8zrKKnZw1Ug7X4oHgMdZiU4E/*))"),
+ "desc": descsum_create("wsh(multi(2,[7b2d0242/84h/0h/0h]" + acc_xpub1 + "/*," + xprv2 + "/84h/0h/0h/*,[e81a0532/84h/0h/0h]" + acc_xpub3 + "/*))"),
"active": True,
"range": 1000,
"next_index": 0,
"timestamp": "now"
},
{
- "desc": descsum_create("wsh(multi(2,[7b2d0242/84h/1h/0h]tpubDCXqdwWZcszwqYJSnZp8eARkxGJfHAk23KDxbztV4BbschfaTfYLTcSkSJ3TN64dRqwa1rnFUScsYormKkGqNbbPwkorQimVevXjxzUV9Gf/*,tprv8ZgxMBicQKsPdSNWUhDiwTScDr6JfkZuLshTRwzvZGnMSnGikV6jxpmdDkC3YRc4T3GD6Nvg9uv6hQg73RVv1EiTXDZwxVbsLugVHU8B1aq/84h/1h/0h/*,[e81a0532/84h/1h/0h]tpubDC6UGqnsQStngYuGD4MKsMy7eD1Yg9NTJfPdvjdG2JE5oZ7EsSL3WHg4Gsw2pR5K39ZwJ46M1wZayhedVdQtMGaUhq5S23PH6fnENK3V1sb/*))"),
+ "desc": descsum_create("wsh(multi(2,[7b2d0242/84h/1h/0h]" + chg_xpub1 + "/*," + xprv2 + "/84h/1h/0h/*,[e81a0532/84h/1h/0h]" + chg_xpub3 + "/*))"),
"active": True,
"internal" : True,
"range": 1000,
@@ -530,6 +627,33 @@ class ImportDescriptorsTest(BitcoinTestFramework):
)
+ self.log.info("Amending multisig with new private keys")
+ self.nodes[1].createwallet(wallet_name="wmulti_priv3", descriptors=True)
+ wmulti_priv3 = self.nodes[1].get_wallet_rpc("wmulti_priv3")
+ res = wmulti_priv3.importdescriptors([
+ {
+ "desc": descsum_create("wsh(multi(2," + xprv1 + "/84h/0h/0h/*,[59b09cd6/84h/0h/0h]" + acc_xpub2 + "/*,[e81a0532/84h/0h/0h]" + acc_xpub3 + "/*))"),
+ "active": True,
+ "range": 1000,
+ "next_index": 0,
+ "timestamp": "now"
+ }])
+ assert_equal(res[0]['success'], True)
+ res = wmulti_priv3.importdescriptors([
+ {
+ "desc": descsum_create("wsh(multi(2," + xprv1 + "/84h/0h/0h/*,[59b09cd6/84h/0h/0h]" + acc_xprv2 + "/*,[e81a0532/84h/0h/0h]" + acc_xpub3 + "/*))"),
+ "active": True,
+ "range": 1000,
+ "next_index": 0,
+ "timestamp": "now"
+ }])
+ assert_equal(res[0]['success'], True)
+
+ rawtx = self.nodes[1].createrawtransaction([{'txid': txid2, 'vout': vout2}], {w0.getnewaddress(): 9.999})
+ tx = wmulti_priv3.signrawtransactionwithwallet(rawtx)
+ assert_equal(tx['complete'], True)
+ self.nodes[1].sendrawtransaction(tx['hex'])
+
self.log.info("Combo descriptors cannot be active")
self.test_importdesc({"desc": descsum_create("combo(tpubDCJtdt5dgJpdhW4MtaVYDhG4T4tF6jcLR1PxL43q9pq1mxvXgMS9Mzw1HnXG15vxUGQJMMSqCQHMTy3F1eW5VkgVroWzchsPD5BUojrcWs8/*)"),
"active": True,
diff --git a/test/functional/wallet_importmulti.py b/test/functional/wallet_importmulti.py
index 13186b9e1d..baeac655df 100755
--- a/test/functional/wallet_importmulti.py
+++ b/test/functional/wallet_importmulti.py
@@ -15,6 +15,7 @@ variants.
- `test_address()` is called to call getaddressinfo for an address on node1
and test the values returned."""
+from test_framework.blocktools import COINBASE_MATURITY
from test_framework.script import (
CScript,
OP_NOP,
@@ -255,7 +256,7 @@ class ImportMultiTest(BitcoinTestFramework):
# P2SH address
multisig = get_multisig(self.nodes[0])
- self.nodes[1].generate(100)
+ self.nodes[1].generate(COINBASE_MATURITY)
self.nodes[1].sendtoaddress(multisig.p2sh_addr, 10.00)
self.nodes[1].generate(1)
timestamp = self.nodes[1].getblock(self.nodes[1].getbestblockhash())['mediantime']
@@ -276,7 +277,7 @@ class ImportMultiTest(BitcoinTestFramework):
# P2SH + Redeem script
multisig = get_multisig(self.nodes[0])
- self.nodes[1].generate(100)
+ self.nodes[1].generate(COINBASE_MATURITY)
self.nodes[1].sendtoaddress(multisig.p2sh_addr, 10.00)
self.nodes[1].generate(1)
timestamp = self.nodes[1].getblock(self.nodes[1].getbestblockhash())['mediantime']
@@ -297,7 +298,7 @@ class ImportMultiTest(BitcoinTestFramework):
# P2SH + Redeem script + Private Keys + !Watchonly
multisig = get_multisig(self.nodes[0])
- self.nodes[1].generate(100)
+ self.nodes[1].generate(COINBASE_MATURITY)
self.nodes[1].sendtoaddress(multisig.p2sh_addr, 10.00)
self.nodes[1].generate(1)
timestamp = self.nodes[1].getblock(self.nodes[1].getbestblockhash())['mediantime']
@@ -323,7 +324,7 @@ class ImportMultiTest(BitcoinTestFramework):
# P2SH + Redeem script + Private Keys + Watchonly
multisig = get_multisig(self.nodes[0])
- self.nodes[1].generate(100)
+ self.nodes[1].generate(COINBASE_MATURITY)
self.nodes[1].sendtoaddress(multisig.p2sh_addr, 10.00)
self.nodes[1].generate(1)
timestamp = self.nodes[1].getblock(self.nodes[1].getbestblockhash())['mediantime']
@@ -745,6 +746,27 @@ class ImportMultiTest(BitcoinTestFramework):
assert 'hdmasterfingerprint' not in pub_import_info
assert 'hdkeypath' not in pub_import_info
+ # Bech32m addresses and descriptors cannot be imported
+ self.log.info("Bech32m addresses and descriptors cannot be imported")
+ self.test_importmulti(
+ {
+ "scriptPubKey": {"address": "bcrt1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vqc8gma6"},
+ "timestamp": "now",
+ },
+ success=False,
+ error_code=-5,
+ error_message="Bech32m addresses cannot be imported into legacy wallets",
+ )
+ self.test_importmulti(
+ {
+ "desc": descsum_create("tr({})".format(pub)),
+ "timestamp": "now",
+ },
+ success=False,
+ error_code=-5,
+ error_message="Bech32m descriptors cannot be imported into legacy wallets",
+ )
+
# Import some public keys to the keypool of a no privkey wallet
self.log.info("Adding pubkey to keypool of disableprivkey wallet")
self.nodes[1].createwallet(wallet_name="noprivkeys", disable_private_keys=True)
diff --git a/test/functional/wallet_importprunedfunds.py b/test/functional/wallet_importprunedfunds.py
index 7635ce2139..ded0e64b1d 100755
--- a/test/functional/wallet_importprunedfunds.py
+++ b/test/functional/wallet_importprunedfunds.py
@@ -5,6 +5,7 @@
"""Test the importprunedfunds and removeprunedfunds RPCs."""
from decimal import Decimal
+from test_framework.blocktools import COINBASE_MATURITY
from test_framework.address import key_to_p2wpkh
from test_framework.key import ECKey
from test_framework.test_framework import BitcoinTestFramework
@@ -24,7 +25,7 @@ class ImportPrunedFundsTest(BitcoinTestFramework):
def run_test(self):
self.log.info("Mining blocks...")
- self.nodes[0].generate(101)
+ self.nodes[0].generate(COINBASE_MATURITY + 1)
self.sync_all()
@@ -46,7 +47,7 @@ class ImportPrunedFundsTest(BitcoinTestFramework):
self.sync_all()
# Node 1 sync test
- assert_equal(self.nodes[1].getblockcount(), 101)
+ assert_equal(self.nodes[1].getblockcount(), COINBASE_MATURITY + 1)
# Address Test - before import
address_info = self.nodes[1].getaddressinfo(address1)
diff --git a/test/functional/wallet_keypool.py b/test/functional/wallet_keypool.py
index 3fe6adeebc..28bfc9116f 100755
--- a/test/functional/wallet_keypool.py
+++ b/test/functional/wallet_keypool.py
@@ -161,7 +161,7 @@ class KeyPoolTest(BitcoinTestFramework):
# Using a fee rate (10 sat / byte) well above the minimum relay rate
# creating a 5,000 sat transaction with change should not be possible
- assert_raises_rpc_error(-4, "Transaction needs a change address, but we can't generate it. Please call keypoolrefill first.", w2.walletcreatefundedpsbt, inputs=[], outputs=[{addr.pop(): 0.00005000}], options={"subtractFeeFromOutputs": [0], "feeRate": 0.00010})
+ assert_raises_rpc_error(-4, "Transaction needs a change address, but we can't generate it.", w2.walletcreatefundedpsbt, inputs=[], outputs=[{addr.pop(): 0.00005000}], options={"subtractFeeFromOutputs": [0], "feeRate": 0.00010})
# creating a 10,000 sat transaction without change, with a manual input, should still be possible
res = w2.walletcreatefundedpsbt(inputs=w2.listunspent(), outputs=[{destination: 0.00010000}], options={"subtractFeeFromOutputs": [0], "feeRate": 0.00010})
diff --git a/test/functional/wallet_keypool_topup.py b/test/functional/wallet_keypool_topup.py
index 5619d57947..1ecf08b9ac 100755
--- a/test/functional/wallet_keypool_topup.py
+++ b/test/functional/wallet_keypool_topup.py
@@ -13,6 +13,7 @@ Two nodes. Node1 is under test. Node0 is providing transactions and generating b
import os
import shutil
+from test_framework.blocktools import COINBASE_MATURITY
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
@@ -31,7 +32,7 @@ class KeypoolRestoreTest(BitcoinTestFramework):
def run_test(self):
wallet_path = os.path.join(self.nodes[1].datadir, self.chain, "wallets", self.default_wallet_name, self.wallet_data_filename)
wallet_backup_path = os.path.join(self.nodes[1].datadir, "wallet.bak")
- self.nodes[0].generate(101)
+ self.nodes[0].generate(COINBASE_MATURITY + 1)
self.log.info("Make backup of wallet")
self.stop_node(1)
diff --git a/test/functional/wallet_labels.py b/test/functional/wallet_labels.py
index 551eb72720..a571454acf 100755
--- a/test/functional/wallet_labels.py
+++ b/test/functional/wallet_labels.py
@@ -11,6 +11,7 @@ RPCs tested are:
"""
from collections import defaultdict
+from test_framework.blocktools import COINBASE_MATURITY
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_equal, assert_raises_rpc_error
from test_framework.wallet_util import test_address
@@ -32,7 +33,7 @@ class WalletLabelsTest(BitcoinTestFramework):
# Note each time we call generate, all generated coins go into
# the same address, so we call twice to get two addresses w/50 each
node.generatetoaddress(nblocks=1, address=node.getnewaddress(label='coinbase'))
- node.generatetoaddress(nblocks=101, address=node.getnewaddress(label='coinbase'))
+ node.generatetoaddress(nblocks=COINBASE_MATURITY + 1, address=node.getnewaddress(label='coinbase'))
assert_equal(node.getbalance(), 100)
# there should be 2 address groups
@@ -104,7 +105,7 @@ class WalletLabelsTest(BitcoinTestFramework):
label.verify(node)
assert_equal(node.getreceivedbylabel(label.name), 2)
label.verify(node)
- node.generate(101)
+ node.generate(COINBASE_MATURITY + 1)
# Check that setlabel can assign a label to a new unused address.
for label in labels:
@@ -124,7 +125,7 @@ class WalletLabelsTest(BitcoinTestFramework):
label.add_address(multisig_address)
label.purpose[multisig_address] = "send"
label.verify(node)
- node.generate(101)
+ node.generate(COINBASE_MATURITY + 1)
# Check that setlabel can change the label of an address from a
# different label.
@@ -134,31 +135,33 @@ class WalletLabelsTest(BitcoinTestFramework):
# in the label. This is a no-op.
change_label(node, labels[2].addresses[0], labels[2], labels[2])
- self.log.info('Check watchonly labels')
- node.createwallet(wallet_name='watch_only', disable_private_keys=True)
- wallet_watch_only = node.get_wallet_rpc('watch_only')
- BECH32_VALID = {
- '✔️_VER15_PROG40': 'bcrt10qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqxkg7fn',
- '✔️_VER16_PROG03': 'bcrt1sqqqqq8uhdgr',
- '✔️_VER16_PROB02': 'bcrt1sqqqq4wstyw',
- }
- BECH32_INVALID = {
- '❌_VER15_PROG41': 'bcrt1sqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqajlxj8',
- '❌_VER16_PROB01': 'bcrt1sqq5r4036',
- }
- for l in BECH32_VALID:
- ad = BECH32_VALID[l]
- wallet_watch_only.importaddress(label=l, rescan=False, address=ad)
- node.generatetoaddress(1, ad)
- assert_equal(wallet_watch_only.getaddressesbylabel(label=l), {ad: {'purpose': 'receive'}})
- assert_equal(wallet_watch_only.getreceivedbylabel(label=l), 0)
- for l in BECH32_INVALID:
- ad = BECH32_INVALID[l]
- assert_raises_rpc_error(
- -5,
- "Address is not valid" if self.options.descriptors else "Invalid Bitcoin address or script",
- lambda: wallet_watch_only.importaddress(label=l, rescan=False, address=ad),
- )
+ if self.options.descriptors:
+ # This is a descriptor wallet test because of segwit v1+ addresses
+ self.log.info('Check watchonly labels')
+ node.createwallet(wallet_name='watch_only', disable_private_keys=True)
+ wallet_watch_only = node.get_wallet_rpc('watch_only')
+ BECH32_VALID = {
+ '✔️_VER15_PROG40': 'bcrt10qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqxkg7fn',
+ '✔️_VER16_PROG03': 'bcrt1sqqqqq8uhdgr',
+ '✔️_VER16_PROB02': 'bcrt1sqqqq4wstyw',
+ }
+ BECH32_INVALID = {
+ '❌_VER15_PROG41': 'bcrt1sqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqajlxj8',
+ '❌_VER16_PROB01': 'bcrt1sqq5r4036',
+ }
+ for l in BECH32_VALID:
+ ad = BECH32_VALID[l]
+ wallet_watch_only.importaddress(label=l, rescan=False, address=ad)
+ node.generatetoaddress(1, ad)
+ assert_equal(wallet_watch_only.getaddressesbylabel(label=l), {ad: {'purpose': 'receive'}})
+ assert_equal(wallet_watch_only.getreceivedbylabel(label=l), 0)
+ for l in BECH32_INVALID:
+ ad = BECH32_INVALID[l]
+ assert_raises_rpc_error(
+ -5,
+ "Address is not valid" if self.options.descriptors else "Invalid Bitcoin address or script",
+ lambda: wallet_watch_only.importaddress(label=l, rescan=False, address=ad),
+ )
class Label:
diff --git a/test/functional/wallet_listdescriptors.py b/test/functional/wallet_listdescriptors.py
index c1444164ce..c2565d84f6 100755
--- a/test/functional/wallet_listdescriptors.py
+++ b/test/functional/wallet_listdescriptors.py
@@ -30,9 +30,10 @@ class ListDescriptorsTest(BitcoinTestFramework):
node = self.nodes[0]
assert_raises_rpc_error(-18, 'No wallet is loaded.', node.listdescriptors)
- self.log.info('Test that the command is not available for legacy wallets.')
- node.createwallet(wallet_name='w1', descriptors=False)
- assert_raises_rpc_error(-4, 'listdescriptors is not available for non-descriptor wallets', node.listdescriptors)
+ if self.is_bdb_compiled():
+ self.log.info('Test that the command is not available for legacy wallets.')
+ node.createwallet(wallet_name='w1', descriptors=False)
+ assert_raises_rpc_error(-4, 'listdescriptors is not available for non-descriptor wallets', node.listdescriptors)
self.log.info('Test the command for empty descriptors wallet.')
node.createwallet(wallet_name='w2', blank=True, descriptors=True)
@@ -72,6 +73,10 @@ class ListDescriptorsTest(BitcoinTestFramework):
}
assert_equal(expected, wallet.listdescriptors())
+ self.log.info("Test listdescriptors with encrypted wallet")
+ wallet.encryptwallet("pass")
+ assert_equal(expected, wallet.listdescriptors())
+
self.log.info('Test non-active non-range combo descriptor')
node.createwallet(wallet_name='w4', blank=True, descriptors=True)
wallet = node.get_wallet_rpc('w4')
diff --git a/test/functional/wallet_listsinceblock.py b/test/functional/wallet_listsinceblock.py
index 448720530c..3899971bd7 100755
--- a/test/functional/wallet_listsinceblock.py
+++ b/test/functional/wallet_listsinceblock.py
@@ -5,6 +5,7 @@
"""Test the listsinceblock RPC."""
from test_framework.address import key_to_p2wpkh
+from test_framework.blocktools import COINBASE_MATURITY
from test_framework.key import ECKey
from test_framework.test_framework import BitcoinTestFramework
from test_framework.messages import BIP125_SEQUENCE_NUMBER
@@ -29,7 +30,7 @@ class ListSinceBlockTest(BitcoinTestFramework):
# All nodes are in IBD from genesis, so they'll need the miner (node2) to be an outbound connection, or have
# only one connection. (See fPreferredDownload in net_processing)
self.connect_nodes(1, 2)
- self.nodes[2].generate(101)
+ self.nodes[2].generate(COINBASE_MATURITY + 1)
self.sync_all()
self.test_no_blockhash()
diff --git a/test/functional/wallet_listtransactions.py b/test/functional/wallet_listtransactions.py
index 71573964de..c0386f5d70 100755
--- a/test/functional/wallet_listtransactions.py
+++ b/test/functional/wallet_listtransactions.py
@@ -4,33 +4,29 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test the listtransactions API."""
from decimal import Decimal
-from io import BytesIO
-from test_framework.messages import COIN, CTransaction
+from test_framework.messages import (
+ COIN,
+ tx_from_hex,
+)
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_array_result,
assert_equal,
- hex_str_to_bytes,
)
-def tx_from_hex(hexstring):
- tx = CTransaction()
- f = BytesIO(hex_str_to_bytes(hexstring))
- tx.deserialize(f)
- return tx
-
class ListTransactionsTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 2
+ # This test isn't testing txn relay/timing, so set whitelist on the
+ # peers for instant txn relay. This speeds up the test run time 2-3x.
+ self.extra_args = [["-whitelist=noban@127.0.0.1"]] * self.num_nodes
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
def run_test(self):
- self.nodes[0].generate(1) # Get out of IBD
- self.sync_all()
- # Simple send, 0 to 1:
+ self.log.info("Test simple send from node0 to node1")
txid = self.nodes[0].sendtoaddress(self.nodes[1].getnewaddress(), 0.1)
self.sync_all()
assert_array_result(self.nodes[0].listtransactions(),
@@ -39,7 +35,7 @@ class ListTransactionsTest(BitcoinTestFramework):
assert_array_result(self.nodes[1].listtransactions(),
{"txid": txid},
{"category": "receive", "amount": Decimal("0.1"), "confirmations": 0})
- # mine a block, confirmations should change:
+ self.log.info("Test confirmations change after mining a block")
blockhash = self.nodes[0].generate(1)[0]
blockheight = self.nodes[0].getblockheader(blockhash)['height']
self.sync_all()
@@ -50,7 +46,7 @@ class ListTransactionsTest(BitcoinTestFramework):
{"txid": txid},
{"category": "receive", "amount": Decimal("0.1"), "confirmations": 1, "blockhash": blockhash, "blockheight": blockheight})
- # send-to-self:
+ self.log.info("Test send-to-self on node0")
txid = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 0.2)
assert_array_result(self.nodes[0].listtransactions(),
{"txid": txid, "category": "send"},
@@ -59,7 +55,7 @@ class ListTransactionsTest(BitcoinTestFramework):
{"txid": txid, "category": "receive"},
{"amount": Decimal("0.2")})
- # sendmany from node1: twice to self, twice to node2:
+ self.log.info("Test sendmany from node1: twice to self, twice to node0")
send_to = {self.nodes[0].getnewaddress(): 0.11,
self.nodes[1].getnewaddress(): 0.22,
self.nodes[0].getnewaddress(): 0.33,
@@ -93,6 +89,7 @@ class ListTransactionsTest(BitcoinTestFramework):
if not self.options.descriptors:
# include_watchonly is a legacy wallet feature, so don't test it for descriptor wallets
+ self.log.info("Test 'include_watchonly' feature (legacy wallet)")
pubkey = self.nodes[1].getaddressinfo(self.nodes[1].getnewaddress())['pubkey']
multisig = self.nodes[1].createmultisig(1, [pubkey])
self.nodes[0].importaddress(multisig["redeemScript"], "watchonly", False, True)
@@ -108,37 +105,38 @@ class ListTransactionsTest(BitcoinTestFramework):
self.run_rbf_opt_in_test()
- # Check that the opt-in-rbf flag works properly, for sent and received
- # transactions.
+
def run_rbf_opt_in_test(self):
- # Check whether a transaction signals opt-in RBF itself
+ """Test the opt-in-rbf flag for sent and received transactions."""
+
def is_opt_in(node, txid):
+ """Check whether a transaction signals opt-in RBF itself."""
rawtx = node.getrawtransaction(txid, 1)
for x in rawtx["vin"]:
if x["sequence"] < 0xfffffffe:
return True
return False
- # Find an unconfirmed output matching a certain txid
def get_unconfirmed_utxo_entry(node, txid_to_match):
+ """Find an unconfirmed output matching a certain txid."""
utxo = node.listunspent(0, 0)
for i in utxo:
if i["txid"] == txid_to_match:
return i
return None
- # 1. Chain a few transactions that don't opt-in.
+ self.log.info("Test txs w/o opt-in RBF (bip125-replaceable=no)")
+ # Chain a few transactions that don't opt in.
txid_1 = self.nodes[0].sendtoaddress(self.nodes[1].getnewaddress(), 1)
assert not is_opt_in(self.nodes[0], txid_1)
assert_array_result(self.nodes[0].listtransactions(), {"txid": txid_1}, {"bip125-replaceable": "no"})
self.sync_mempools()
assert_array_result(self.nodes[1].listtransactions(), {"txid": txid_1}, {"bip125-replaceable": "no"})
- # Tx2 will build off txid_1, still not opting in to RBF.
+ # Tx2 will build off tx1, still not opting in to RBF.
utxo_to_use = get_unconfirmed_utxo_entry(self.nodes[0], txid_1)
assert_equal(utxo_to_use["safe"], True)
utxo_to_use = get_unconfirmed_utxo_entry(self.nodes[1], txid_1)
- utxo_to_use = get_unconfirmed_utxo_entry(self.nodes[1], txid_1)
assert_equal(utxo_to_use["safe"], False)
# Create tx2 using createrawtransaction
@@ -154,6 +152,7 @@ class ListTransactionsTest(BitcoinTestFramework):
self.sync_mempools()
assert_array_result(self.nodes[0].listtransactions(), {"txid": txid_2}, {"bip125-replaceable": "no"})
+ self.log.info("Test txs with opt-in RBF (bip125-replaceable=yes)")
# Tx3 will opt-in to RBF
utxo_to_use = get_unconfirmed_utxo_entry(self.nodes[0], txid_2)
inputs = [{"txid": txid_2, "vout": utxo_to_use["vout"]}]
@@ -184,6 +183,7 @@ class ListTransactionsTest(BitcoinTestFramework):
self.sync_mempools()
assert_array_result(self.nodes[0].listtransactions(), {"txid": txid_4}, {"bip125-replaceable": "yes"})
+ self.log.info("Test tx with unknown RBF state (bip125-replaceable=unknown)")
# Replace tx3, and check that tx4 becomes unknown
tx3_b = tx3_modified
tx3_b.vout[0].nValue -= int(Decimal("0.004") * COIN) # bump the fee
@@ -196,7 +196,7 @@ class ListTransactionsTest(BitcoinTestFramework):
self.sync_mempools()
assert_array_result(self.nodes[1].listtransactions(), {"txid": txid_4}, {"bip125-replaceable": "unknown"})
- # Check gettransaction as well:
+ self.log.info("Test bip125-replaceable status with gettransaction RPC")
for n in self.nodes[0:2]:
assert_equal(n.gettransaction(txid_1)["bip125-replaceable"], "no")
assert_equal(n.gettransaction(txid_2)["bip125-replaceable"], "no")
@@ -204,7 +204,7 @@ class ListTransactionsTest(BitcoinTestFramework):
assert_equal(n.gettransaction(txid_3b)["bip125-replaceable"], "yes")
assert_equal(n.gettransaction(txid_4)["bip125-replaceable"], "unknown")
- # After mining a transaction, it's no longer BIP125-replaceable
+ self.log.info("Test mined transactions are no longer bip125-replaceable")
self.nodes[0].generate(1)
assert txid_3b not in self.nodes[0].getrawmempool()
assert_equal(self.nodes[0].gettransaction(txid_3b)["bip125-replaceable"], "no")
diff --git a/test/functional/wallet_multiwallet.py b/test/functional/wallet_multiwallet.py
index 71d1b96a95..00d2c9ffe4 100755
--- a/test/functional/wallet_multiwallet.py
+++ b/test/functional/wallet_multiwallet.py
@@ -14,6 +14,7 @@ import stat
import time
from test_framework.authproxy import JSONRPCException
+from test_framework.blocktools import COINBASE_MATURITY
from test_framework.test_framework import BitcoinTestFramework
from test_framework.test_node import ErrorMatch
from test_framework.util import (
@@ -229,7 +230,7 @@ class MultiWalletTest(BitcoinTestFramework):
assert_raises_rpc_error(-19, "Wallet file not specified", node.getwalletinfo)
w1, w2, w3, w4, *_ = wallets
- node.generatetoaddress(nblocks=101, address=w1.getnewaddress())
+ node.generatetoaddress(nblocks=COINBASE_MATURITY + 1, address=w1.getnewaddress())
assert_equal(w1.getbalance(), 100)
assert_equal(w2.getbalance(), 0)
assert_equal(w3.getbalance(), 0)
diff --git a/test/functional/wallet_orphanedreward.py b/test/functional/wallet_orphanedreward.py
new file mode 100755
index 0000000000..097df2cf41
--- /dev/null
+++ b/test/functional/wallet_orphanedreward.py
@@ -0,0 +1,62 @@
+#!/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 orphaned block rewards in the wallet."""
+
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import assert_equal
+
+class OrphanedBlockRewardTest(BitcoinTestFramework):
+ def set_test_params(self):
+ self.setup_clean_chain = True
+ self.num_nodes = 2
+
+ def skip_test_if_missing_module(self):
+ self.skip_if_no_wallet()
+
+ def run_test(self):
+ # Generate some blocks and obtain some coins on node 0. We send
+ # some balance to node 1, which will hold it as a single coin.
+ self.nodes[0].generate(150)
+ self.nodes[0].sendtoaddress(self.nodes[1].getnewaddress(), 10)
+ self.nodes[0].generate(1)
+
+ # Get a block reward with node 1 and remember the block so we can orphan
+ # it later.
+ self.sync_blocks()
+ blk = self.nodes[1].generate(1)[0]
+ self.sync_blocks()
+
+ # Let the block reward mature and send coins including both
+ # the existing balance and the block reward.
+ self.nodes[0].generate(150)
+ self.sync_blocks()
+ assert_equal(self.nodes[1].getbalance(), 10 + 25)
+ txid = self.nodes[1].sendtoaddress(self.nodes[0].getnewaddress(), 30)
+
+ # Orphan the block reward and make sure that the original coins
+ # from the wallet can still be spent.
+ self.nodes[0].invalidateblock(blk)
+ self.nodes[0].generate(152)
+ self.sync_blocks()
+ # Without the following abandontransaction call, the coins are
+ # not considered available yet.
+ assert_equal(self.nodes[1].getbalances()["mine"], {
+ "trusted": 0,
+ "untrusted_pending": 0,
+ "immature": 0,
+ })
+ # The following abandontransaction is necessary to make the later
+ # lines succeed, and probably should not be needed; see
+ # https://github.com/bitcoin/bitcoin/issues/14148.
+ self.nodes[1].abandontransaction(txid)
+ assert_equal(self.nodes[1].getbalances()["mine"], {
+ "trusted": 10,
+ "untrusted_pending": 0,
+ "immature": 0,
+ })
+ self.nodes[1].sendtoaddress(self.nodes[0].getnewaddress(), 9)
+
+if __name__ == '__main__':
+ OrphanedBlockRewardTest().main()
diff --git a/test/functional/wallet_resendwallettransactions.py b/test/functional/wallet_resendwallettransactions.py
index 78d88f8aa5..37dee219e7 100755
--- a/test/functional/wallet_resendwallettransactions.py
+++ b/test/functional/wallet_resendwallettransactions.py
@@ -5,8 +5,10 @@
"""Test that the wallet resends transactions periodically."""
import time
-from test_framework.blocktools import create_block, create_coinbase
-from test_framework.messages import ToHex
+from test_framework.blocktools import (
+ create_block,
+ create_coinbase,
+)
from test_framework.p2p import P2PTxInvStore
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_equal
@@ -48,7 +50,7 @@ class ResendWalletTransactionsTest(BitcoinTestFramework):
block = create_block(int(node.getbestblockhash(), 16), create_coinbase(node.getblockcount() + 1), block_time)
block.rehash()
block.solve()
- node.submitblock(ToHex(block))
+ node.submitblock(block.serialize().hex())
# Set correct m_best_block_time, which is used in ResendWalletTransactions
node.syncwithvalidationinterfacequeue()
diff --git a/test/functional/wallet_taproot.py b/test/functional/wallet_taproot.py
new file mode 100755
index 0000000000..9eb204bf37
--- /dev/null
+++ b/test/functional/wallet_taproot.py
@@ -0,0 +1,427 @@
+#!/usr/bin/env python3
+# Copyright (c) 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 generation and spending of P2TR addresses."""
+
+import random
+
+from decimal import Decimal
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import assert_equal
+from test_framework.descriptors import descsum_create
+from test_framework.script import (CScript, OP_CHECKSIG, taproot_construct)
+from test_framework.segwit_addr import encode_segwit_address
+
+# xprvs/xpubs, and m/* derived x-only pubkeys (created using independent implementation)
+KEYS = [
+ {
+ "xprv": "tprv8ZgxMBicQKsPeNLUGrbv3b7qhUk1LQJZAGMuk9gVuKh9sd4BWGp1eMsehUni6qGb8bjkdwBxCbgNGdh2bYGACK5C5dRTaif9KBKGVnSezxV",
+ "xpub": "tpubD6NzVbkrYhZ4XqNGAWGWSzmxGWFwVjVTjZxh2fioKbVYi7Jx8fdbprVWsdW7mHwqjchBVas8TLZG4Xwuz4RKU4iaCqiCvoSkFCzQptqk5Y1",
+ "pubs": [
+ "83d8ee77a0f3a32a5cea96fd1624d623b836c1e5d1ac2dcde46814b619320c18",
+ "a30253b018ea6fca966135bf7dd8026915427f24ccf10d4e03f7870f4128569b",
+ "a61e5749f2f3db9dc871d7b187e30bfd3297eea2557e9be99897ea8ff7a29a21",
+ "8110cf482f66dc37125e619d73075af932521724ffc7108309e88f361efe8c8a",
+ ]
+ },
+ {
+ "xprv": "tprv8ZgxMBicQKsPe98QUPieXy5KFPVjuZNpcC9JY7K7buJEm8nWvJogK4kTda7eLjK9U4PnMNbSjEkpjDJazeBZ4rhYNYD7N6GEdaysj1AYSb5",
+ "xpub": "tpubD6NzVbkrYhZ4XcACN3PEwNjRpR1g4tZjBVk5pdMR2B6dbd3HYhdGVZNKofAiFZd9okBserZvv58A6tBX4pE64UpXGNTSesfUW7PpW36HuKz",
+ "pubs": [
+ "f95886b02a84928c5c15bdca32784993105f73de27fa6ad8c1a60389b999267c",
+ "71522134160685eb779857033bfc84c7626f13556154653a51dd42619064e679",
+ "48957b4158b2c5c3f4c000f51fd2cf0fd5ff8868ebfb194256f5e9131fc74bd8",
+ "086dda8139b3a84944010648d2b674b70447be3ae59322c09a4907bc80be62c1",
+ ]
+ },
+ {
+ "xprv": "tprv8ZgxMBicQKsPe3ZJmcj9aJ2EPZJYYCh6Lp3v82p75wspgaXmtDZ2RBtkAtWcGnW2VQDzMHQPBkCKMoYTqh1RfJKjv4PcmWVR7KqTpjsdboN",
+ "xpub": "tpubD6NzVbkrYhZ4XWb6fGPjyhgLxapUhXszv7ehQYrQWDgDX4nYWcNcbgWcM2RhYo9s2mbZcfZJ8t5LzYcr24FK79zVybsw5Qj3Rtqug8jpJMy",
+ "pubs": [
+ "9fa5ffb68821cf559001caa0577eeea4978b29416def328a707b15e91701a2f7",
+ "8a104c54cd34acba60c97dd8f1f7abc89ba9587afd88dc928e91aca7b1c50d20",
+ "13ba6b252a4eb5ef31d39cb521724cdab19a698323f5c17093f28fb1821d052f",
+ "f6c2b4863fd5ba1ba09e3a890caed8b75ffbe013ebab31a06ab87cd6f72506af",
+ ]
+ },
+ {
+ "xprv": "tprv8ZgxMBicQKsPdKziibn63Rm6aNzp7dSjDnufZMStXr71Huz7iihCRpbZZZ6Voy5HyuHCWx6foHMipzMzUq4tZrtkZ24DJwz5EeNWdsuwX5h",
+ "xpub": "tpubD6NzVbkrYhZ4Wo2WcFSgSqRD9QWkGxddo6WSqsVBx7uQ8QEtM7WncKDRjhFEexK119NigyCsFygA4b7sAPQxqebyFGAZ9XVV1BtcgNzbCRR",
+ "pubs": [
+ "03a669ea926f381582ec4a000b9472ba8a17347f5fb159eddd4a07036a6718eb",
+ "bbf56b14b119bccafb686adec2e3d2a6b51b1626213590c3afa815d1fd36f85d",
+ "2994519e31bbc238a07d82f85c9832b831705d2ee4a2dbb477ecec8a3f570fe5",
+ "68991b5c139a4c479f8c89d6254d288c533aefc0c5b91fac6c89019c4de64988",
+ ]
+ },
+ {
+ "xprv": "tprv8ZgxMBicQKsPen4PGtDwURYnCtVMDejyE8vVwMGhQWfVqB2FBPdekhTacDW4vmsKTsgC1wsncVqXiZdX2YFGAnKoLXYf42M78fQJFzuDYFN",
+ "xpub": "tpubD6NzVbkrYhZ4YF6BAXtXsqCtmv1HNyvsoSXHDsJzpnTtffH1onTEwC5SnLzCHPKPebh2i7Gxvi9kJNADcpuSmH8oM3rCYcHVtdXHjpYoKnX",
+ "pubs": [
+ "aba457d16a8d59151c387f24d1eb887efbe24644c1ee64b261282e7baebdb247",
+ "c8558b7caf198e892032d91f1a48ee9bdc25462b83b4d0ac62bb7fb2a0df630e",
+ "8a4bcaba0e970685858d133a4d0079c8b55bbc755599e212285691eb779ce3dc",
+ "b0d68ada13e0d954b3921b88160d4453e9c151131c2b7c724e08f538a666ceb3",
+ ]
+ },
+ {
+ "xprv": "tprv8ZgxMBicQKsPd91vCgRmbzA13wyip2RimYeVEkAyZvsEN5pUSB3T43SEBxPsytkxb42d64W2EiRE9CewpJQkzR8HKHLV8Uhk4dMF5yRPaTv",
+ "xpub": "tpubD6NzVbkrYhZ4Wc3i6L6N1Pp7cyVeyMcdLrFGXGDGzCfdCa5F4Zs3EY46N72Ws8QDEUYBVwXfDfda2UKSseSdU1fsBegJBhGCZyxkf28bkQ6",
+ "pubs": [
+ "9b4d495b74887815a1ff623c055c6eac6b6b2e07d2a016d6526ebac71dd99744",
+ "8e971b781b7ce7ab742d80278f2dfe7dd330f3efd6d00047f4a2071f2e7553cb",
+ "b811d66739b9f07435ccda907ec5cd225355321c35e0a7c7791232f24cf10632",
+ "4cd27a5552c272bc80ba544e9cc6340bb906969f5e7a1510b6cef9592683fbc9",
+ ]
+ },
+ {
+ "xprv": "tprv8ZgxMBicQKsPdEhLRxxwzTv2t18j7ruoffPeqAwVA2qXJ2P66RaMZLUWQ85SjoA7xPxdSgCB9UZ72m65qbnaLPtFTfHVP3MEmkpZk1Bv8RT",
+ "xpub": "tpubD6NzVbkrYhZ4Whj8KcdYPsa9T2efHC6iExzS7gynaJdv8WdripPwjq6NaH5gQJGrLmvUwHY1smhiakUosXNDTEa6qfKUQdLKV6DJBre6XvQ",
+ "pubs": [
+ "d0c19def28bb1b39451c1a814737615983967780d223b79969ba692182c6006b",
+ "cb1d1b1dc62fec1894d4c3d9a1b6738e5ff9c273a64f74e9ab363095f45e9c47",
+ "245be588f41acfaeb9481aa132717db56ee1e23eb289729fe2b8bde8f9a00830",
+ "5bc4ad6d6187fa82728c85a073b428483295288f8aef5722e47305b5872f7169",
+ ]
+ },
+ {
+ "xprv": "tprv8ZgxMBicQKsPcxbqxzcMAwQpiCD8x6qaZEJTxdKxw4w9GuMzDACTD9yhEsHGfqQcfYX4LivosLDDngTykYEp9JnTdcqY7cHqU8PpeFFKyV3",
+ "xpub": "tpubD6NzVbkrYhZ4WRddreGwaM4wHDj57S2V8XuFF9NGMLjY7PckqZ23PebZR1wGA4w84uX2vZphdZVsnREjij1ibYjEBTaTVQCEZCLs4xUDapx",
+ "pubs": [
+ "065cc1b92bd99e5a3e626e8296a366b2d132688eb43aea19bc14fd8f43bf07fb",
+ "5b95633a7dda34578b6985e6bfd85d83ec38b7ded892a9b74a3d899c85890562",
+ "dc86d434b9a34495c8e845b969d51f80d19a8df03b400353ffe8036a0c22eb60",
+ "06c8ffde238745b29ae8a97ae533e1f3edf214bba6ec58b5e7b9451d1d61ec19",
+ ]
+ },
+ {
+ "xprv": "tprv8ZgxMBicQKsPe6zLoU8MTTXgsdJVNBErrYGpoGwHf5VGvwUzdNc7NHeCSzkJkniCxBhZWujXjmD4HZmBBrnr3URgJjM6GxRgMmEhLdqNTWG",
+ "xpub": "tpubD6NzVbkrYhZ4Xa28h7nwrsBoSepRXWRmRqsc5nyb5MHfmRjmFmRhYnG4d9dC7uxixN5AfsEv1Lz3mCAuWvERyvPgKozHUVjfo8EG6foJGy7",
+ "pubs": [
+ "d826a0a53abb6ffc60df25b9c152870578faef4b2eb5a09bdd672bbe32cdd79b",
+ "939365e0359ff6bc6f6404ee220714c5d4a0d1e36838b9e2081ede217674e2ba",
+ "4e8767edcf7d3d90258cfbbea01b784f4d2de813c4277b51279cf808bac410a2",
+ "d42a2c280940bfc6ede971ae72cde2e1df96c6da7dab06a132900c6751ade208",
+ ]
+ },
+ {
+ "xprv": "tprv8ZgxMBicQKsPeB5o5oCsN2dVxM2mtJiYERQEBRc4JNwC1DFGYaEdNkmh8jJYVPU76YhkFoRoWTdh1p3yQGykG8TfDW34dKgrgSx28gswUyL",
+ "xpub": "tpubD6NzVbkrYhZ4Xe7aySsTmSHcXNYi3duSoj11TweMiejaqhW3Ay4DZFPZJses4sfpk4b9VHRhn8v4cKTMjugMM3hqXcqSSmRdiW8QvASXjfY",
+ "pubs": [
+ "e360564b2e0e8d06681b6336a29d0750210e8f34afd9afb5e6fd5fe6dba26c81",
+ "76b4900f00a1dcce463b6d8e02b768518fce4f9ecd6679a13ad78ea1e4815ad3",
+ "5575556e263c8ed52e99ab02147cc05a738869afe0039911b5a60a780f4e43d2",
+ "593b00e2c8d4bd6dda0fd9e238888acf427bb4e128887fd5a40e0e9da78cbc01",
+ ]
+ },
+ {
+ "xprv": "tprv8ZgxMBicQKsPfEH6jHemkGDjZRnAaKFJVGH8pQU638E6SdbX9hxit1tK2sfFPfL6KS7v8FfUKxstbfEpzSymbdfBM9Y5UkrxErF9fJaKLK3",
+ "xpub": "tpubD6NzVbkrYhZ4YhJtcwKN9fsr8TJ6jeSD4Zsv6vWPTQ2VH7rHn6nK4WWBCzKK7FkdVVwm3iztCU1UmStY4hX6gRbBmp9UzK9C59dQEzeXS12",
+ "pubs": [
+ "7631cacec3343052d87ef4d0065f61dde82d7d2db0c1cc02ef61ef3c982ea763",
+ "c05e44a9e735d1b1bef62e2c0d886e6fb4923b2649b67828290f5cacc51c71b7",
+ "b33198b20701afe933226c92fd0e3d51d3f266f1113d864dbd026ae3166ef7f2",
+ "f99643ac3f4072ee4a949301e86963a9ca0ad57f2ef29f6b84fda037d7cac85b",
+ ]
+ },
+ {
+ "xprv": "tprv8ZgxMBicQKsPdNWU38dT6aGxtqJR4oYS5kPpLVBcuKiiu7gqTYqMMqhUG6DP7pPahzPQu36sWSmeLCP1C4AwqcR5FX2RyRoZfd4B8pAnSdX",
+ "xpub": "tpubD6NzVbkrYhZ4WqYFvnJ3Vyw5TrpME8jLf3zbd1DvKbX7jbwc5wewYLKLSFRzZWV6hZj7XhsXAy7fhE5jB25DiWyNM3ztXbsXHRVCrp5BiPY",
+ "pubs": [
+ "2258b1c3160be0864a541854eec9164a572f094f7562628281a8073bb89173a7",
+ "83df59d0a5c951cdd62b7ab225a62079f48d2a333a86e66c35420d101446e92e",
+ "2a654bf234d819055312f9ca03fad5836f9163b09cdd24d29678f694842b874a",
+ "aa0334ab910047387c912a21ec0dab806a47ffa38365060dbc5d47c18c6e66e7",
+ ]
+ },
+ {
+ "xprv": "tprv8mGPkMVz5mZuJDnC2NjjAv7E9Zqa5LCgX4zawbZu5nzTtLb5kGhPwycX4H1gtW1f5ZdTKTNtQJ61hk71F2TdcQ93EFDTpUcPBr98QRji615",
+ "xpub": "tpubDHxRtmYEE9FaBgoyv2QKaKmLibMWEfPb6NbNE7cCW4nripqrNfWz8UEPEPbHCrakwLvwFfsqoaf4pjX4gWStp4nECRf1QwBKPkLqnY8pHbj",
+ "pubs": [
+ "00a9da96087a72258f83b338ef7f0ea8cbbe05da5f18f091eb397d1ecbf7c3d3",
+ "b2749b74d51a78f5fe3ebb3a7c0ff266a468cade143dfa265c57e325177edf00",
+ "6b8747a6bbe4440d7386658476da51f6e49a220508a7ec77fe7bccc3e7baa916",
+ "4674bf4d9ebbe01bf0aceaca2472f63198655ecf2df810f8d69b38421972318e",
+ ]
+ }
+]
+
+CHANGE_XPRV = "tprv8ZgxMBicQKsPcyDrWwiecVnTtFmfRwbfFqEfR4ZGWvq5aTTwLBWmAm5zrbMcYtb9gQNFfhRfqhhrBG37U3nhmXxEgeEPBJGHAPrHCrAd1WX"
+CHANGE_XPUB = "tpubD6NzVbkrYhZ4WSFeQbPF1uSaTHHbbGnZq8qShabZwCdUQwihxaLMMFhs2kidGF2qrRKiQVqw8VoyuTHj1bZqmMXMeciaU1gBjWA1sim2zUB"
+
+# Point with no known discrete log.
+H_POINT = "50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0"
+
+
+def key(hex_key):
+ """Construct an x-only pubkey from its hex representation."""
+ return bytes.fromhex(hex_key)
+
+def pk(hex_key):
+ """Construct a script expression for taproot_construct for pk(hex_key)."""
+ return (None, CScript([bytes.fromhex(hex_key), OP_CHECKSIG]))
+
+def compute_taproot_address(pubkey, scripts):
+ """Compute the address for a taproot output with given inner key and scripts."""
+ tap = taproot_construct(pubkey, scripts)
+ assert tap.scriptPubKey[0] == 0x51
+ assert tap.scriptPubKey[1] == 0x20
+ return encode_segwit_address("bcrt", 1, tap.scriptPubKey[2:])
+
+class WalletTaprootTest(BitcoinTestFramework):
+ """Test generation and spending of P2TR address outputs."""
+
+ def set_test_params(self):
+ self.num_nodes = 3
+ self.setup_clean_chain = True
+ self.extra_args = [['-keypool=100'], ['-keypool=100'], ["-vbparams=taproot:1:1"]]
+ self.supports_cli = False
+
+ def skip_test_if_missing_module(self):
+ self.skip_if_no_wallet()
+ self.skip_if_no_sqlite()
+
+ def setup_network(self):
+ self.setup_nodes()
+
+ def init_wallet(self, i):
+ pass
+
+ @staticmethod
+ def rand_keys(n):
+ ret = []
+ idxes = set()
+ for _ in range(n):
+ while True:
+ i = random.randrange(len(KEYS))
+ if not i in idxes:
+ break
+ idxes.add(i)
+ ret.append(KEYS[i])
+ return ret
+
+ @staticmethod
+ def make_desc(pattern, privmap, keys, pub_only = False):
+ pat = pattern.replace("$H", H_POINT)
+ for i in range(len(privmap)):
+ if privmap[i] and not pub_only:
+ pat = pat.replace("$%i" % (i + 1), keys[i]['xprv'])
+ else:
+ pat = pat.replace("$%i" % (i + 1), keys[i]['xpub'])
+ return descsum_create(pat)
+
+ @staticmethod
+ def make_addr(treefn, keys, i):
+ args = []
+ for j in range(len(keys)):
+ args.append(keys[j]['pubs'][i])
+ return compute_taproot_address(*treefn(*args))
+
+ def do_test_addr(self, comment, pattern, privmap, treefn, keys):
+ self.log.info("Testing %s address derivation" % comment)
+ desc = self.make_desc(pattern, privmap, keys, False)
+ desc_pub = self.make_desc(pattern, privmap, keys, True)
+ assert_equal(self.nodes[0].getdescriptorinfo(desc)['descriptor'], desc_pub)
+ result = self.addr_gen.importdescriptors([{"desc": desc_pub, "active": True, "timestamp": "now"}])
+ assert(result[0]['success'])
+ for i in range(4):
+ addr_g = self.addr_gen.getnewaddress(address_type='bech32m')
+ if treefn is not None:
+ addr_r = self.make_addr(treefn, keys, i)
+ assert_equal(addr_g, addr_r)
+ desc_a = self.addr_gen.getaddressinfo(addr_g)['desc']
+ if desc.startswith("tr("):
+ assert desc_a.startswith("tr(")
+ rederive = self.nodes[1].deriveaddresses(desc_a)
+ assert_equal(len(rederive), 1)
+ assert_equal(rederive[0], addr_g)
+
+ # tr descriptors cannot be imported when Taproot is not active
+ 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"])
+ if desc.startswith("tr"):
+ result = self.privs_tr_disabled.importdescriptors([{"desc": desc, "timestamp": "now"}])
+ assert(not result[0]["success"])
+ assert_equal(result[0]["error"]["code"], -4)
+ assert_equal(result[0]["error"]["message"], "Cannot import tr() descriptor when Taproot is not active")
+ result = self.pubs_tr_disabled.importdescriptors([{"desc": desc_pub, "timestamp": "now"}])
+ assert(not result[0]["success"])
+ assert_equal(result[0]["error"]["code"], -4)
+ assert_equal(result[0]["error"]["message"], "Cannot import tr() descriptor when Taproot is not active")
+
+ def do_test_sendtoaddress(self, comment, pattern, privmap, treefn, keys_pay, keys_change):
+ self.log.info("Testing %s through sendtoaddress" % comment)
+ desc_pay = self.make_desc(pattern, privmap, keys_pay)
+ desc_change = self.make_desc(pattern, privmap, keys_change)
+ desc_pay_pub = self.make_desc(pattern, privmap, keys_pay, True)
+ desc_change_pub = self.make_desc(pattern, privmap, keys_change, True)
+ assert_equal(self.nodes[0].getdescriptorinfo(desc_pay)['descriptor'], desc_pay_pub)
+ assert_equal(self.nodes[0].getdescriptorinfo(desc_change)['descriptor'], desc_change_pub)
+ result = self.rpc_online.importdescriptors([{"desc": desc_pay, "active": True, "timestamp": "now"}])
+ assert(result[0]['success'])
+ result = self.rpc_online.importdescriptors([{"desc": desc_change, "active": True, "timestamp": "now", "internal": True}])
+ assert(result[0]['success'])
+ for i in range(4):
+ addr_g = self.rpc_online.getnewaddress(address_type='bech32m')
+ if treefn is not None:
+ addr_r = self.make_addr(treefn, keys_pay, i)
+ assert_equal(addr_g, addr_r)
+ boring_balance = int(self.boring.getbalance() * 100000000)
+ to_amnt = random.randrange(1000000, boring_balance)
+ self.boring.sendtoaddress(address=addr_g, amount=Decimal(to_amnt) / 100000000, subtractfeefromamount=True)
+ self.nodes[0].generatetoaddress(1, self.boring.getnewaddress())
+ test_balance = int(self.rpc_online.getbalance() * 100000000)
+ ret_amnt = random.randrange(100000, test_balance)
+ res = self.rpc_online.sendtoaddress(address=self.boring.getnewaddress(), amount=Decimal(ret_amnt) / 100000000, subtractfeefromamount=True)
+ self.nodes[0].generatetoaddress(1, self.boring.getnewaddress())
+ assert(self.rpc_online.gettransaction(res)["confirmations"] > 0)
+
+ def do_test_psbt(self, comment, pattern, privmap, treefn, keys_pay, keys_change):
+ self.log.info("Testing %s through PSBT" % comment)
+ desc_pay = self.make_desc(pattern, privmap, keys_pay, False)
+ desc_change = self.make_desc(pattern, privmap, keys_change, False)
+ desc_pay_pub = self.make_desc(pattern, privmap, keys_pay, True)
+ desc_change_pub = self.make_desc(pattern, privmap, keys_change, True)
+ assert_equal(self.nodes[0].getdescriptorinfo(desc_pay)['descriptor'], desc_pay_pub)
+ assert_equal(self.nodes[0].getdescriptorinfo(desc_change)['descriptor'], desc_change_pub)
+ result = self.psbt_online.importdescriptors([{"desc": desc_pay_pub, "active": True, "timestamp": "now"}])
+ assert(result[0]['success'])
+ result = self.psbt_online.importdescriptors([{"desc": desc_change_pub, "active": True, "timestamp": "now", "internal": True}])
+ assert(result[0]['success'])
+ result = self.psbt_offline.importdescriptors([{"desc": desc_pay, "active": True, "timestamp": "now"}])
+ assert(result[0]['success'])
+ result = self.psbt_offline.importdescriptors([{"desc": desc_change, "active": True, "timestamp": "now", "internal": True}])
+ assert(result[0]['success'])
+ for i in range(4):
+ addr_g = self.psbt_online.getnewaddress(address_type='bech32m')
+ if treefn is not None:
+ addr_r = self.make_addr(treefn, keys_pay, i)
+ assert_equal(addr_g, addr_r)
+ boring_balance = int(self.boring.getbalance() * 100000000)
+ to_amnt = random.randrange(1000000, boring_balance)
+ self.boring.sendtoaddress(address=addr_g, amount=Decimal(to_amnt) / 100000000, subtractfeefromamount=True)
+ self.nodes[0].generatetoaddress(1, self.boring.getnewaddress())
+ test_balance = int(self.psbt_online.getbalance() * 100000000)
+ ret_amnt = random.randrange(100000, test_balance)
+ psbt = self.psbt_online.walletcreatefundedpsbt([], [{self.boring.getnewaddress(): Decimal(ret_amnt) / 100000000}], None, {"subtractFeeFromOutputs":[0]})['psbt']
+ res = self.psbt_offline.walletprocesspsbt(psbt)
+ assert(res['complete'])
+ rawtx = self.nodes[0].finalizepsbt(res['psbt'])['hex']
+ txid = self.nodes[0].sendrawtransaction(rawtx)
+ self.nodes[0].generatetoaddress(1, self.boring.getnewaddress())
+ assert(self.psbt_online.gettransaction(txid)['confirmations'] > 0)
+
+ def do_test(self, comment, pattern, privmap, treefn, nkeys):
+ keys = self.rand_keys(nkeys * 4)
+ self.do_test_addr(comment, pattern, privmap, treefn, keys[0:nkeys])
+ self.do_test_sendtoaddress(comment, pattern, privmap, treefn, keys[0:nkeys], keys[nkeys:2*nkeys])
+ self.do_test_psbt(comment, pattern, privmap, treefn, keys[2*nkeys:3*nkeys], keys[3*nkeys:4*nkeys])
+
+ def run_test(self):
+ 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)
+ self.nodes[0].createwallet(wallet_name="psbt_online", descriptors=True, disable_private_keys=True, blank=True)
+ self.nodes[1].createwallet(wallet_name="psbt_offline", descriptors=True, blank=True)
+ self.boring = self.nodes[0].get_wallet_rpc("boring")
+ self.addr_gen = self.nodes[0].get_wallet_rpc("addr_gen")
+ self.rpc_online = self.nodes[0].get_wallet_rpc("rpc_online")
+ self.psbt_online = self.nodes[0].get_wallet_rpc("psbt_online")
+ self.psbt_offline = self.nodes[1].get_wallet_rpc("psbt_offline")
+
+ self.log.info("Mining blocks...")
+ gen_addr = self.boring.getnewaddress()
+ self.nodes[0].generatetoaddress(101, gen_addr)
+
+ self.do_test(
+ "tr(XPRV)",
+ "tr($1/*)",
+ [True],
+ lambda k1: (key(k1), []),
+ 1
+ )
+ self.do_test(
+ "tr(H,XPRV)",
+ "tr($H,pk($1/*))",
+ [True],
+ lambda k1: (key(H_POINT), [pk(k1)]),
+ 1
+ )
+ self.do_test(
+ "wpkh(XPRV)",
+ "wpkh($1/*)",
+ [True],
+ None,
+ 1
+ )
+ self.do_test(
+ "tr(XPRV,{H,{H,XPUB}})",
+ "tr($1/*,{pk($H),{pk($H),pk($2/*)}})",
+ [True, False],
+ lambda k1, k2: (key(k1), [pk(H_POINT), [pk(H_POINT), pk(k2)]]),
+ 2
+ )
+ self.do_test(
+ "wsh(multi(1,XPRV,XPUB))",
+ "wsh(multi(1,$1/*,$2/*))",
+ [True, False],
+ None,
+ 2
+ )
+ self.do_test(
+ "tr(XPRV,{XPUB,XPUB})",
+ "tr($1/*,{pk($2/*),pk($2/*)})",
+ [True, False],
+ lambda k1, k2: (key(k1), [pk(k2), pk(k2)]),
+ 2
+ )
+ self.do_test(
+ "tr(XPRV,{{XPUB,H},{H,XPUB}})",
+ "tr($1/*,{{pk($2/*),pk($H)},{pk($H),pk($2/*)}})",
+ [True, False],
+ lambda k1, k2: (key(k1), [[pk(k2), pk(H_POINT)], [pk(H_POINT), pk(k2)]]),
+ 2
+ )
+ self.do_test(
+ "tr(XPUB,{{H,{H,XPUB}},{H,{H,{H,XPRV}}}})",
+ "tr($1/*,{{pk($H),{pk($H),pk($2/*)}},{pk($H),{pk($H),{pk($H),pk($3/*)}}}})",
+ [False, False, True],
+ lambda k1, k2, k3: (key(k1), [[pk(H_POINT), [pk(H_POINT), pk(k2)]], [pk(H_POINT), [pk(H_POINT), [pk(H_POINT), pk(k3)]]]]),
+ 3
+ )
+ self.do_test(
+ "tr(XPRV,{XPUB,{{XPUB,{H,H}},{{H,H},XPUB}}})",
+ "tr($1/*,{pk($2/*),{{pk($2/*),{pk($H),pk($H)}},{{pk($H),pk($H)},pk($2/*)}}})",
+ [True, False],
+ lambda k1, k2: (key(k1), [pk(k2), [[pk(k2), [pk(H_POINT), pk(H_POINT)]], [[pk(H_POINT), pk(H_POINT)], pk(k2)]]]),
+ 2
+ )
+
+ self.log.info("Sending everything back...")
+
+ txid = self.rpc_online.sendtoaddress(address=self.boring.getnewaddress(), amount=self.rpc_online.getbalance(), subtractfeefromamount=True)
+ self.nodes[0].generatetoaddress(1, self.boring.getnewaddress())
+ assert(self.rpc_online.gettransaction(txid)["confirmations"] > 0)
+
+ psbt = self.psbt_online.walletcreatefundedpsbt([], [{self.boring.getnewaddress(): self.psbt_online.getbalance()}], None, {"subtractFeeFromOutputs": [0]})['psbt']
+ res = self.psbt_offline.walletprocesspsbt(psbt)
+ assert(res['complete'])
+ rawtx = self.nodes[0].finalizepsbt(res['psbt'])['hex']
+ txid = self.nodes[0].sendrawtransaction(rawtx)
+ self.nodes[0].generatetoaddress(1, self.boring.getnewaddress())
+ assert(self.psbt_online.gettransaction(txid)['confirmations'] > 0)
+
+if __name__ == '__main__':
+ WalletTaprootTest().main()
diff --git a/test/functional/wallet_txn_clone.py b/test/functional/wallet_txn_clone.py
index 84ff9ad772..76b39201e3 100755
--- a/test/functional/wallet_txn_clone.py
+++ b/test/functional/wallet_txn_clone.py
@@ -4,12 +4,14 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test the wallet accounts properly when there are cloned transactions with malleated scriptsigs."""
-import io
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
)
-from test_framework.messages import CTransaction, COIN
+from test_framework.messages import (
+ COIN,
+ tx_from_hex,
+)
class TxnMallTest(BitcoinTestFramework):
@@ -71,8 +73,7 @@ class TxnMallTest(BitcoinTestFramework):
clone_raw = self.nodes[0].createrawtransaction(clone_inputs, clone_outputs, clone_locktime)
# createrawtransaction randomizes the order of its outputs, so swap them if necessary.
- clone_tx = CTransaction()
- clone_tx.deserialize(io.BytesIO(bytes.fromhex(clone_raw)))
+ clone_tx = tx_from_hex(clone_raw)
if (rawtx1["vout"][0]["value"] == 40 and clone_tx.vout[0].nValue != 40*COIN or rawtx1["vout"][0]["value"] != 40 and clone_tx.vout[0].nValue == 40*COIN):
(clone_tx.vout[0], clone_tx.vout[1]) = (clone_tx.vout[1], clone_tx.vout[0])
diff --git a/test/functional/wallet_upgradewallet.py b/test/functional/wallet_upgradewallet.py
index fbc0f995d2..4d34670ea9 100755
--- a/test/functional/wallet_upgradewallet.py
+++ b/test/functional/wallet_upgradewallet.py
@@ -6,9 +6,8 @@
Test upgradewallet RPC. Download node binaries:
-test/get_previous_releases.py -b v0.19.1 v0.18.1 v0.17.2 v0.16.3 v0.15.2
-
-Only v0.15.2 and v0.16.3 are required by this test. The others are used in feature_backwards_compatibility.py
+Requires previous releases binaries, see test/README.md.
+Only v0.15.2 and v0.16.3 are required by this test.
"""
import os
@@ -17,6 +16,7 @@ import struct
from io import BytesIO
+from test_framework.blocktools import COINBASE_MATURITY
from test_framework.bdb import dump_bdb_kv
from test_framework.messages import deser_compact_size, deser_string
from test_framework.test_framework import BitcoinTestFramework
@@ -94,10 +94,11 @@ class UpgradeWalletTest(BitcoinTestFramework):
def test_upgradewallet(self, wallet, previous_version, requested_version=None, expected_version=None):
unchanged = expected_version == previous_version
new_version = previous_version if unchanged else expected_version if expected_version else requested_version
- assert_equal(wallet.getwalletinfo()["walletversion"], previous_version)
+ old_wallet_info = wallet.getwalletinfo()
+ assert_equal(old_wallet_info["walletversion"], previous_version)
assert_equal(wallet.upgradewallet(requested_version),
{
- "wallet_name": "",
+ "wallet_name": old_wallet_info["walletname"],
"previous_version": previous_version,
"current_version": new_version,
"result": "Already at latest version. Wallet version unchanged." if unchanged else "Wallet upgraded successfully from version {} to version {}.".format(previous_version, new_version),
@@ -118,11 +119,11 @@ class UpgradeWalletTest(BitcoinTestFramework):
assert_equal(wallet.getwalletinfo()["walletversion"], previous_version)
def run_test(self):
- self.nodes[0].generatetoaddress(101, self.nodes[0].getnewaddress())
+ self.nodes[0].generatetoaddress(COINBASE_MATURITY + 1, self.nodes[0].getnewaddress())
self.dumb_sync_blocks()
# # Sanity check the test framework:
res = self.nodes[0].getblockchaininfo()
- assert_equal(res['blocks'], 101)
+ assert_equal(res['blocks'], COINBASE_MATURITY + 1)
node_master = self.nodes[0]
v16_3_node = self.nodes[1]
v15_2_node = self.nodes[2]
@@ -130,7 +131,7 @@ class UpgradeWalletTest(BitcoinTestFramework):
# Send coins to old wallets for later conversion checks.
v16_3_wallet = v16_3_node.get_wallet_rpc('wallet.dat')
v16_3_address = v16_3_wallet.getnewaddress()
- node_master.generatetoaddress(101, v16_3_address)
+ node_master.generatetoaddress(COINBASE_MATURITY + 1, v16_3_address)
self.dumb_sync_blocks()
v16_3_balance = v16_3_wallet.getbalance()
@@ -352,6 +353,11 @@ class UpgradeWalletTest(BitcoinTestFramework):
v16_3_kvs = dump_bdb_kv(v16_3_wallet)
assert b'\x0adefaultkey' not in v16_3_kvs
+ if self.is_sqlite_compiled():
+ self.log.info("Checking that descriptor wallets do nothing, successfully")
+ self.nodes[0].createwallet(wallet_name="desc_upgrade", descriptors=True)
+ desc_wallet = self.nodes[0].get_wallet_rpc("desc_upgrade")
+ self.test_upgradewallet(desc_wallet, previous_version=169900, expected_version=169900)
if __name__ == '__main__':
UpgradeWalletTest().main()
diff --git a/test/functional/wallet_watchonly.py b/test/functional/wallet_watchonly.py
index c345c382d0..6743c4a49b 100755
--- a/test/functional/wallet_watchonly.py
+++ b/test/functional/wallet_watchonly.py
@@ -5,6 +5,7 @@
"""Test createwallet watchonly arguments.
"""
+from test_framework.blocktools import COINBASE_MATURITY
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
@@ -36,7 +37,7 @@ class CreateWalletWatchonlyTest(BitcoinTestFramework):
wo_wallet.importpubkey(pubkey=def_wallet.getaddressinfo(wo_change)['pubkey'])
# generate some btc for testing
- node.generatetoaddress(101, a1)
+ node.generatetoaddress(COINBASE_MATURITY + 1, a1)
# send 1 btc to our watch-only address
txid = def_wallet.sendtoaddress(wo_addr, 1)
diff --git a/test/get_previous_releases.py b/test/get_previous_releases.py
index b177fbb4b2..e92bb402b5 100755
--- a/test/get_previous_releases.py
+++ b/test/get_previous_releases.py
@@ -21,39 +21,47 @@ import hashlib
SHA256_SUMS = {
-"d40f18b4e43c6e6370ef7db9131f584fbb137276ec2e3dba67a4b267f81cb644": "bitcoin-0.15.2-aarch64-linux-gnu.tar.gz",
-"54fb877a148a6ad189a1e1ab1ff8b11181e58ff2aaf430da55b3fd46ae549a6b": "bitcoin-0.15.2-arm-linux-gnueabihf.tar.gz",
-"2b843506c3f1af0eeca5854a920264f9a829f02d0d50328005950ddcbe88874d": "bitcoin-0.15.2-i686-pc-linux-gnu.tar.gz",
-"87e9340ff3d382d543b2b69112376077f0c8b4f7450d372e83b68f5a1e22b2df": "bitcoin-0.15.2-osx64.tar.gz",
-"566be44190fd76daa01f13d428939dadfb8e3daacefc8fa17f433cad28f73bd5": "bitcoin-0.15.2-x86_64-linux-gnu.tar.gz",
-
-"0768c6c15caffbaca6524824c9563b42c24f70633c681c2744649158aa3fd484": "bitcoin-0.16.3-aarch64-linux-gnu.tar.gz",
-"fb2818069854a6ad20ea03b28b55dbd35d8b1f7d453e90b83eace5d0098a2a87": "bitcoin-0.16.3-arm-linux-gnueabihf.tar.gz",
-"75a537844313b0a84bdb61ffcdc5c4ce19a738f7ddf71007cd2edf664efd7c37": "bitcoin-0.16.3-i686-pc-linux-gnu.tar.gz",
-"78c3bff3b619a19aed575961ea43cc9e142959218835cf51aede7f0b764fc25d": "bitcoin-0.16.3-osx64.tar.gz",
-"5d422a9d544742bc0df12427383f9c2517433ce7b58cf672b9a9b17c2ef51e4f": "bitcoin-0.16.3-x86_64-linux-gnu.tar.gz",
-
-"5a6b35d1a348a402f2d2d6ab5aed653a1a1f13bc63aaaf51605e3501b0733b7a": "bitcoin-0.17.2-aarch64-linux-gnu.tar.gz",
-"d1913a5d19c8e8da4a67d1bd5205d03c8614dfd2e02bba2fe3087476643a729e": "bitcoin-0.17.2-arm-linux-gnueabihf.tar.gz",
-"d295fc93f39bbf0fd937b730a93184899a2eb6c3a6d53f3d857cbe77ef89b98c": "bitcoin-0.17.2-i686-pc-linux-gnu.tar.gz",
-"a783ba20706dbfd5b47fbedf42165fce70fbbc7d78003305d964f6b3da14887f": "bitcoin-0.17.2-osx64.tar.gz",
-"943f9362b9f11130177839116f48f809d83478b4c28591d486ee9a7e35179da6": "bitcoin-0.17.2-x86_64-linux-gnu.tar.gz",
-
-"88f343af72803b851c7da13874cc5525026b0b55e63e1b5e1298390c4688adc6": "bitcoin-0.18.1-aarch64-linux-gnu.tar.gz",
-"cc7d483e4b20c5dabd4dcaf304965214cf4934bcc029ca99cbc9af00d3771a1f": "bitcoin-0.18.1-arm-linux-gnueabihf.tar.gz",
-"989e847b3e95fc9fedc0b109cae1b4fa43348f2f712e187a118461876af9bd16": "bitcoin-0.18.1-i686-pc-linux-gnu.tar.gz",
-"b7bbcee7a7540f711b171d6981f939ca8482005fde22689bc016596d80548bb1": "bitcoin-0.18.1-osx64.tar.gz",
-"425ee5ec631ae8da71ebc1c3f5c0269c627cf459379b9b030f047107a28e3ef8": "bitcoin-0.18.1-riscv64-linux-gnu.tar.gz",
-"600d1db5e751fa85903e935a01a74f5cc57e1e7473c15fd3e17ed21e202cfe5a": "bitcoin-0.18.1-x86_64-linux-gnu.tar.gz",
-
-"3a80431717842672df682bdb619e66523b59541483297772a7969413be3502ff": "bitcoin-0.19.1-aarch64-linux-gnu.tar.gz",
-"657f28213823d240dd3324d14829702f9ad6f0710f8bdd1c379cb3c447197f48": "bitcoin-0.19.1-arm-linux-gnueabihf.tar.gz",
-"10d1e53208aa7603022f4acc084a046299ab4ccf25fe01e81b3fb6f856772589": "bitcoin-0.19.1-i686-pc-linux-gnu.tar.gz",
-"1ae1b87de26487075cd2fd22e0d4ead87d969bd55c44f2f1d873ecdc6147ebb3": "bitcoin-0.19.1-osx64.tar.gz",
-"aa7a9563b48aa79252c8e7b6a41c07a5441bd9f14c5e4562cc72720ea6cb0ee5": "bitcoin-0.19.1-riscv64-linux-gnu.tar.gz",
-"5fcac9416e486d4960e1a946145566350ca670f9aaba99de6542080851122e4c": "bitcoin-0.19.1-x86_64-linux-gnu.tar.gz"
+ "d40f18b4e43c6e6370ef7db9131f584fbb137276ec2e3dba67a4b267f81cb644": "bitcoin-0.15.2-aarch64-linux-gnu.tar.gz",
+ "54fb877a148a6ad189a1e1ab1ff8b11181e58ff2aaf430da55b3fd46ae549a6b": "bitcoin-0.15.2-arm-linux-gnueabihf.tar.gz",
+ "2b843506c3f1af0eeca5854a920264f9a829f02d0d50328005950ddcbe88874d": "bitcoin-0.15.2-i686-pc-linux-gnu.tar.gz",
+ "87e9340ff3d382d543b2b69112376077f0c8b4f7450d372e83b68f5a1e22b2df": "bitcoin-0.15.2-osx64.tar.gz",
+ "566be44190fd76daa01f13d428939dadfb8e3daacefc8fa17f433cad28f73bd5": "bitcoin-0.15.2-x86_64-linux-gnu.tar.gz",
+ #
+ "0768c6c15caffbaca6524824c9563b42c24f70633c681c2744649158aa3fd484": "bitcoin-0.16.3-aarch64-linux-gnu.tar.gz",
+ "fb2818069854a6ad20ea03b28b55dbd35d8b1f7d453e90b83eace5d0098a2a87": "bitcoin-0.16.3-arm-linux-gnueabihf.tar.gz",
+ "75a537844313b0a84bdb61ffcdc5c4ce19a738f7ddf71007cd2edf664efd7c37": "bitcoin-0.16.3-i686-pc-linux-gnu.tar.gz",
+ "78c3bff3b619a19aed575961ea43cc9e142959218835cf51aede7f0b764fc25d": "bitcoin-0.16.3-osx64.tar.gz",
+ "5d422a9d544742bc0df12427383f9c2517433ce7b58cf672b9a9b17c2ef51e4f": "bitcoin-0.16.3-x86_64-linux-gnu.tar.gz",
+ #
+ "5a6b35d1a348a402f2d2d6ab5aed653a1a1f13bc63aaaf51605e3501b0733b7a": "bitcoin-0.17.2-aarch64-linux-gnu.tar.gz",
+ "d1913a5d19c8e8da4a67d1bd5205d03c8614dfd2e02bba2fe3087476643a729e": "bitcoin-0.17.2-arm-linux-gnueabihf.tar.gz",
+ "d295fc93f39bbf0fd937b730a93184899a2eb6c3a6d53f3d857cbe77ef89b98c": "bitcoin-0.17.2-i686-pc-linux-gnu.tar.gz",
+ "a783ba20706dbfd5b47fbedf42165fce70fbbc7d78003305d964f6b3da14887f": "bitcoin-0.17.2-osx64.tar.gz",
+ "943f9362b9f11130177839116f48f809d83478b4c28591d486ee9a7e35179da6": "bitcoin-0.17.2-x86_64-linux-gnu.tar.gz",
+ #
+ "88f343af72803b851c7da13874cc5525026b0b55e63e1b5e1298390c4688adc6": "bitcoin-0.18.1-aarch64-linux-gnu.tar.gz",
+ "cc7d483e4b20c5dabd4dcaf304965214cf4934bcc029ca99cbc9af00d3771a1f": "bitcoin-0.18.1-arm-linux-gnueabihf.tar.gz",
+ "989e847b3e95fc9fedc0b109cae1b4fa43348f2f712e187a118461876af9bd16": "bitcoin-0.18.1-i686-pc-linux-gnu.tar.gz",
+ "b7bbcee7a7540f711b171d6981f939ca8482005fde22689bc016596d80548bb1": "bitcoin-0.18.1-osx64.tar.gz",
+ "425ee5ec631ae8da71ebc1c3f5c0269c627cf459379b9b030f047107a28e3ef8": "bitcoin-0.18.1-riscv64-linux-gnu.tar.gz",
+ "600d1db5e751fa85903e935a01a74f5cc57e1e7473c15fd3e17ed21e202cfe5a": "bitcoin-0.18.1-x86_64-linux-gnu.tar.gz",
+ #
+ "3a80431717842672df682bdb619e66523b59541483297772a7969413be3502ff": "bitcoin-0.19.1-aarch64-linux-gnu.tar.gz",
+ "657f28213823d240dd3324d14829702f9ad6f0710f8bdd1c379cb3c447197f48": "bitcoin-0.19.1-arm-linux-gnueabihf.tar.gz",
+ "10d1e53208aa7603022f4acc084a046299ab4ccf25fe01e81b3fb6f856772589": "bitcoin-0.19.1-i686-pc-linux-gnu.tar.gz",
+ "1ae1b87de26487075cd2fd22e0d4ead87d969bd55c44f2f1d873ecdc6147ebb3": "bitcoin-0.19.1-osx64.tar.gz",
+ "aa7a9563b48aa79252c8e7b6a41c07a5441bd9f14c5e4562cc72720ea6cb0ee5": "bitcoin-0.19.1-riscv64-linux-gnu.tar.gz",
+ "5fcac9416e486d4960e1a946145566350ca670f9aaba99de6542080851122e4c": "bitcoin-0.19.1-x86_64-linux-gnu.tar.gz",
+ #
+ "60c93e3462c303eb080be7cf623f1a7684b37fd47a018ad3848bc23e13c84e1c": "bitcoin-0.20.1-aarch64-linux-gnu.tar.gz",
+ "55b577e0fb306fb429d4be6c9316607753e8543e5946b542d75d876a2f08654c": "bitcoin-0.20.1-arm-linux-gnueabihf.tar.gz",
+ "b9024dde373ea7dad707363e07ec7e265383204127539ae0c234bff3a61da0d1": "bitcoin-0.20.1-osx64.tar.gz",
+ "c378d4e21109f09e8829f3591e015c66632dff2925a60b64d259be05a334c30b": "bitcoin-0.20.1-osx.dmg",
+ "fa71cb52ee5e0459cbf5248cdec72df27995840c796f58b304607a1ed4c165af": "bitcoin-0.20.1-riscv64-linux-gnu.tar.gz",
+ "376194f06596ecfa40331167c39bc70c355f960280bd2a645fdbf18f66527397": "bitcoin-0.20.1-x86_64-linux-gnu.tar.gz",
}
+
@contextlib.contextmanager
def pushd(new_dir) -> None:
previous_dir = os.getcwd()
@@ -104,7 +112,11 @@ def download_binary(tag, args) -> int:
tarballHash = hasher.hexdigest()
if tarballHash not in SHA256_SUMS or SHA256_SUMS[tarballHash] != tarball:
- print("Checksum did not match")
+ if tarball in SHA256_SUMS.values():
+ print("Checksum did not match")
+ return 1
+
+ print("Checksum for given version doesn't exist")
return 1
print("Checksum matched")
diff --git a/test/lint/lint-circular-dependencies.sh b/test/lint/lint-circular-dependencies.sh
index 354e14f361..f8f24bb1ff 100755
--- a/test/lint/lint-circular-dependencies.sh
+++ b/test/lint/lint-circular-dependencies.sh
@@ -17,7 +17,6 @@ EXPECTED_CIRCULAR_DEPENDENCIES=(
"index/coinstatsindex -> node/coinstats -> index/coinstatsindex"
"policy/fees -> txmempool -> policy/fees"
"qt/addresstablemodel -> qt/walletmodel -> qt/addresstablemodel"
- "qt/bitcoingui -> qt/walletframe -> qt/bitcoingui"
"qt/recentrequeststablemodel -> qt/walletmodel -> qt/recentrequeststablemodel"
"qt/sendcoinsdialog -> qt/walletmodel -> qt/sendcoinsdialog"
"qt/transactiontablemodel -> qt/walletmodel -> qt/transactiontablemodel"
@@ -25,6 +24,10 @@ EXPECTED_CIRCULAR_DEPENDENCIES=(
"wallet/fees -> wallet/wallet -> wallet/fees"
"wallet/wallet -> wallet/walletdb -> wallet/wallet"
"node/coinstats -> validation -> node/coinstats"
+ # Temporary circular dependencies that allow wallet.h/wallet.cpp to be
+ # split up in a MOVEONLY commit. These are removed in #21206.
+ "wallet/receive -> wallet/wallet -> wallet/receive"
+ "wallet/spend -> wallet/wallet -> wallet/spend"
)
EXIT_CODE=0
diff --git a/test/lint/lint-python.sh b/test/lint/lint-python.sh
index 51815963f6..c448fa6f9a 100755
--- a/test/lint/lint-python.sh
+++ b/test/lint/lint-python.sh
@@ -102,7 +102,7 @@ if ! PYTHONWARNINGS="ignore" flake8 --ignore=B,C,E,F,I,N,W --select=$(IFS=","; e
EXIT_CODE=1
fi
-if ! mypy --ignore-missing-imports $(git ls-files "test/functional/*.py" "contrib/devtools/*.py"); then
+if ! mypy --ignore-missing-imports --show-error-codes $(git ls-files "test/functional/*.py" "contrib/devtools/*.py"); then
EXIT_CODE=1
fi
diff --git a/test/lint/lint-spelling.ignore-words.txt b/test/lint/lint-spelling.ignore-words.txt
index 78ffe4def3..9906b15e9a 100644
--- a/test/lint/lint-spelling.ignore-words.txt
+++ b/test/lint/lint-spelling.ignore-words.txt
@@ -6,6 +6,8 @@ fpr
hights
hist
inout
+invokable
+keypair
mor
nin
ser
diff --git a/test/lint/lint-spelling.sh b/test/lint/lint-spelling.sh
index fbdf3c59c1..111091b7f8 100755
--- a/test/lint/lint-spelling.sh
+++ b/test/lint/lint-spelling.sh
@@ -15,6 +15,6 @@ if ! command -v codespell > /dev/null; then
fi
IGNORE_WORDS_FILE=test/lint/lint-spelling.ignore-words.txt
-if ! codespell --check-filenames --disable-colors --quiet-level=7 --ignore-words=${IGNORE_WORDS_FILE} $(git ls-files -- ":(exclude)build-aux/m4/" ":(exclude)contrib/seeds/*.txt" ":(exclude)depends/" ":(exclude)doc/release-notes/" ":(exclude)src/leveldb/" ":(exclude)src/crc32c/" ":(exclude)src/qt/locale/" ":(exclude)src/qt/*.qrc" ":(exclude)src/secp256k1/" ":(exclude)src/univalue/" ":(exclude)contrib/gitian-keys/keys.txt"); then
+if ! codespell --check-filenames --disable-colors --quiet-level=7 --ignore-words=${IGNORE_WORDS_FILE} $(git ls-files -- ":(exclude)build-aux/m4/" ":(exclude)contrib/seeds/*.txt" ":(exclude)depends/" ":(exclude)doc/release-notes/" ":(exclude)src/leveldb/" ":(exclude)src/crc32c/" ":(exclude)src/qt/locale/" ":(exclude)src/qt/*.qrc" ":(exclude)src/secp256k1/" ":(exclude)src/univalue/" ":(exclude)contrib/builder-keys/keys.txt" ":(exclude)contrib/guix/patches"); then
echo "^ Warning: codespell identified likely spelling errors. Any false positives? Add them to the list of ignored words in ${IGNORE_WORDS_FILE}"
fi
diff --git a/test/sanitizer_suppressions/ubsan b/test/sanitizer_suppressions/ubsan
index 4f6f92bd3c..2850cfcea5 100644
--- a/test/sanitizer_suppressions/ubsan
+++ b/test/sanitizer_suppressions/ubsan
@@ -98,7 +98,6 @@ implicit-unsigned-integer-truncation:crypto/
implicit-unsigned-integer-truncation:leveldb/
# std::variant warning fixed in https://github.com/gcc-mirror/gcc/commit/074436cf8cdd2a9ce75cadd36deb8301f00e55b9
implicit-unsigned-integer-truncation:std::__detail::__variant::_Variant_storage
-shift-base:nanobench.h
shift-base:*/include/c++/
shift-base:arith_uint256.cpp
shift-base:crypto/
diff --git a/test/util/data/bitcoin-util-test.json b/test/util/data/bitcoin-util-test.json
index 0a9846b4be..a648c0287a 100644
--- a/test/util/data/bitcoin-util-test.json
+++ b/test/util/data/bitcoin-util-test.json
@@ -1,4 +1,34 @@
[
+ { "exec": "./bitcoin-util",
+ "args": ["foo"],
+ "return_code": 1,
+ "error_txt": "Error parsing command line arguments: Invalid command 'foo'",
+ "description": ""
+ },
+ { "exec": "./bitcoin-util",
+ "args": ["help"],
+ "return_code": 1,
+ "error_txt": "Error parsing command line arguments: Invalid command 'help'",
+ "description": "`help` raises an error. Use `-help`"
+ },
+ { "exec": "./bitcoin-util",
+ "args": ["grind"],
+ "return_code": 1,
+ "error_txt": "Must specify block header to grind",
+ "description": ""
+ },
+ { "exec": "./bitcoin-util",
+ "args": ["grind", "1", "2"],
+ "return_code": 1,
+ "error_txt": "Must specify block header to grind",
+ "description": ""
+ },
+ { "exec": "./bitcoin-util",
+ "args": ["grind", "aa"],
+ "return_code": 1,
+ "error_txt": "Could not decode block header",
+ "description": ""
+ },
{ "exec": "./bitcoin-tx",
"args": ["-create", "nversion=1"],
"output_cmp": "blanktxv1.hex",