aboutsummaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rw-r--r--test/functional/data/invalid_txs.py4
-rwxr-xr-xtest/functional/feature_assumeutxo.py22
-rwxr-xr-xtest/functional/feature_block.py18
-rwxr-xr-xtest/functional/feature_fee_estimation.py54
-rwxr-xr-xtest/functional/feature_framework_miniwallet.py18
-rwxr-xr-xtest/functional/feature_proxy.py11
-rwxr-xr-xtest/functional/feature_taproot.py2
-rwxr-xr-xtest/functional/interface_rest.py2
-rwxr-xr-xtest/functional/mempool_accept.py60
-rwxr-xr-xtest/functional/mempool_reorg.py2
-rwxr-xr-xtest/functional/p2p_permissions.py2
-rwxr-xr-xtest/functional/rpc_decodescript.py10
-rwxr-xr-xtest/functional/rpc_scantxoutset.py10
-rwxr-xr-xtest/functional/rpc_signrawtransactionwithkey.py14
-rwxr-xr-xtest/functional/rpc_validateaddress.py5
-rw-r--r--test/functional/test_framework/address.py10
-rw-r--r--test/functional/test_framework/blocktools.py6
-rwxr-xr-xtest/functional/test_framework/messages.py7
-rw-r--r--test/functional/test_framework/script.py10
-rwxr-xr-xtest/functional/test_framework/script_util.py3
-rw-r--r--test/functional/test_framework/wallet.py12
21 files changed, 227 insertions, 55 deletions
diff --git a/test/functional/data/invalid_txs.py b/test/functional/data/invalid_txs.py
index 33054fd517..2e4ca83bf0 100644
--- a/test/functional/data/invalid_txs.py
+++ b/test/functional/data/invalid_txs.py
@@ -192,7 +192,7 @@ class SpendTooMuch(BadTxTemplate):
def get_tx(self):
return create_tx_with_script(
- self.spend_tx, 0, script_pub_key=basic_p2sh, amount=(self.spend_avail + 1))
+ self.spend_tx, 0, output_script=basic_p2sh, amount=(self.spend_avail + 1))
class CreateNegative(BadTxTemplate):
@@ -242,7 +242,7 @@ class TooManySigops(BadTxTemplate):
lotsa_checksigs = CScript([OP_CHECKSIG] * (MAX_BLOCK_SIGOPS))
return create_tx_with_script(
self.spend_tx, 0,
- script_pub_key=lotsa_checksigs,
+ output_script=lotsa_checksigs,
amount=1)
def getDisabledOpcodeTemplate(opcode):
diff --git a/test/functional/feature_assumeutxo.py b/test/functional/feature_assumeutxo.py
index 943862f8cf..da7cabf87c 100755
--- a/test/functional/feature_assumeutxo.py
+++ b/test/functional/feature_assumeutxo.py
@@ -8,20 +8,6 @@ to a hash that has been compiled into bitcoind.
The assumeutxo value generated and used here is committed to in
`CRegTestParams::m_assumeutxo_data` in `src/kernel/chainparams.cpp`.
-
-## Possible test improvements
-
-Interesting test cases could be loading an assumeutxo snapshot file with:
-
-- TODO: Valid snapshot file, but referencing a snapshot block that turns out to be
- invalid, or has an invalid parent
-
-Interesting starting states could be loading a snapshot when the current chain tip is:
-
-- TODO: An ancestor of snapshot block
-- TODO: The snapshot block
-- TODO: A descendant of the snapshot block
-
"""
from shutil import rmtree
@@ -202,7 +188,6 @@ class AssumeutxoTest(BitcoinTestFramework):
def test_snapshot_with_less_work(self, dump_output_path):
self.log.info("Test bitcoind should fail when snapshot has less accumulated work than this node.")
node = self.nodes[0]
- assert_equal(node.getblockcount(), FINAL_HEIGHT)
with node.assert_debug_log(expected_msgs=["[snapshot] activation failed - work does not exceed active chainstate"]):
assert_raises_rpc_error(-32603, "Unable to load UTXO snapshot", node.loadtxoutset, dump_output_path)
@@ -316,6 +301,11 @@ class AssumeutxoTest(BitcoinTestFramework):
self.log.info(f"Creating a UTXO snapshot at height {SNAPSHOT_BASE_HEIGHT}")
dump_output = n0.dumptxoutset('utxos.dat')
+ self.log.info("Test loading snapshot when the node tip is on the same block as the snapshot")
+ assert_equal(n0.getblockcount(), SNAPSHOT_BASE_HEIGHT)
+ assert_equal(n0.getblockchaininfo()["blocks"], SNAPSHOT_BASE_HEIGHT)
+ self.test_snapshot_with_less_work(dump_output['path'])
+
self.log.info("Test loading snapshot when headers are not synced")
self.test_headers_not_synced(dump_output['path'])
@@ -358,6 +348,8 @@ class AssumeutxoTest(BitcoinTestFramework):
self.test_snapshot_not_on_most_work_chain(dump_output['path'])
self.log.info(f"Loading snapshot into second node from {dump_output['path']}")
+ # This node's tip is on an ancestor block of the snapshot, which should
+ # be the normal case
loaded = n1.loadtxoutset(dump_output['path'])
assert_equal(loaded['coins_loaded'], SNAPSHOT_BASE_HEIGHT)
assert_equal(loaded['base_height'], SNAPSHOT_BASE_HEIGHT)
diff --git a/test/functional/feature_block.py b/test/functional/feature_block.py
index 172af27848..384ca311c7 100755
--- a/test/functional/feature_block.py
+++ b/test/functional/feature_block.py
@@ -1326,8 +1326,10 @@ class FullBlockTest(BitcoinTestFramework):
block.vtx.extend(tx_list)
# this is a little handier to use than the version in blocktools.py
- def create_tx(self, spend_tx, n, value, script=CScript([OP_TRUE, OP_DROP] * 15 + [OP_TRUE])):
- return create_tx_with_script(spend_tx, n, amount=value, script_pub_key=script)
+ def create_tx(self, spend_tx, n, value, output_script=None):
+ if output_script is None:
+ output_script = CScript([OP_TRUE, OP_DROP] * 15 + [OP_TRUE])
+ return create_tx_with_script(spend_tx, n, amount=value, output_script=output_script)
# sign a transaction, using the key we know about
# this signs input 0 in tx, which is assumed to be spending output 0 in spend_tx
@@ -1338,13 +1340,17 @@ class FullBlockTest(BitcoinTestFramework):
return
sign_input_legacy(tx, 0, spend_tx.vout[0].scriptPubKey, self.coinbase_key)
- def create_and_sign_transaction(self, spend_tx, value, script=CScript([OP_TRUE])):
- tx = self.create_tx(spend_tx, 0, value, script)
+ def create_and_sign_transaction(self, spend_tx, value, output_script=None):
+ if output_script is None:
+ output_script = CScript([OP_TRUE])
+ tx = self.create_tx(spend_tx, 0, value, output_script=output_script)
self.sign_tx(tx, spend_tx)
tx.rehash()
return tx
- def next_block(self, number, spend=None, additional_coinbase_value=0, script=CScript([OP_TRUE]), *, version=4):
+ def next_block(self, number, spend=None, additional_coinbase_value=0, *, script=None, version=4):
+ if script is None:
+ script = CScript([OP_TRUE])
if self.tip is None:
base_block_hash = self.genesis_hash
block_time = int(time.time()) + 1
@@ -1361,7 +1367,7 @@ class FullBlockTest(BitcoinTestFramework):
else:
coinbase.vout[0].nValue += spend.vout[0].nValue - 1 # all but one satoshi to fees
coinbase.rehash()
- tx = self.create_tx(spend, 0, 1, script) # spend 1 satoshi
+ tx = self.create_tx(spend, 0, 1, output_script=script) # spend 1 satoshi
self.sign_tx(tx, spend)
tx.rehash()
block = create_block(base_block_hash, coinbase, block_time, version=version, txlist=[tx])
diff --git a/test/functional/feature_fee_estimation.py b/test/functional/feature_fee_estimation.py
index 34fb3c5573..a3dcb7afda 100755
--- a/test/functional/feature_fee_estimation.py
+++ b/test/functional/feature_fee_estimation.py
@@ -128,6 +128,14 @@ def make_tx(wallet, utxo, feerate):
fee_rate=Decimal(feerate * 1000) / COIN,
)
+def check_fee_estimates_btw_modes(node, expected_conservative, expected_economical):
+ fee_est_conservative = node.estimatesmartfee(1, estimate_mode="conservative")['feerate']
+ fee_est_economical = node.estimatesmartfee(1, estimate_mode="economical")['feerate']
+ fee_est_default = node.estimatesmartfee(1)['feerate']
+ assert_equal(fee_est_conservative, expected_conservative)
+ assert_equal(fee_est_economical, expected_economical)
+ assert_equal(fee_est_default, expected_economical)
+
class EstimateFeeTest(BitcoinTestFramework):
def set_test_params(self):
@@ -382,6 +390,40 @@ class EstimateFeeTest(BitcoinTestFramework):
self.start_node(0,extra_args=["-acceptstalefeeestimates"])
assert_equal(self.nodes[0].estimatesmartfee(1)["feerate"], fee_rate)
+ def clear_estimates(self):
+ self.log.info("Restarting node with fresh estimation")
+ self.stop_node(0)
+ fee_dat = self.nodes[0].chain_path / "fee_estimates.dat"
+ os.remove(fee_dat)
+ self.start_node(0)
+ self.connect_nodes(0, 1)
+ self.connect_nodes(0, 2)
+ assert_equal(self.nodes[0].estimatesmartfee(1)["errors"], ["Insufficient data or no feerate found"])
+
+ def broadcast_and_mine(self, broadcaster, miner, feerate, count):
+ """Broadcast and mine some number of transactions with a specified fee rate."""
+ for _ in range(count):
+ self.wallet.send_self_transfer(from_node=broadcaster, fee_rate=feerate)
+ self.sync_mempools()
+ self.generate(miner, 1)
+
+ def test_estimation_modes(self):
+ low_feerate = Decimal("0.001")
+ high_feerate = Decimal("0.005")
+ tx_count = 24
+ # Broadcast and mine high fee transactions for the first 12 blocks.
+ for _ in range(12):
+ self.broadcast_and_mine(self.nodes[1], self.nodes[2], high_feerate, tx_count)
+ check_fee_estimates_btw_modes(self.nodes[0], high_feerate, high_feerate)
+
+ # We now track 12 blocks; short horizon stats will start decaying.
+ # Broadcast and mine low fee transactions for the next 4 blocks.
+ for _ in range(4):
+ self.broadcast_and_mine(self.nodes[1], self.nodes[2], low_feerate, tx_count)
+ # conservative mode will consider longer time horizons while economical mode does not
+ # Check the fee estimates for both modes after mining low fee transactions.
+ check_fee_estimates_btw_modes(self.nodes[0], high_feerate, low_feerate)
+
def run_test(self):
self.log.info("This test is time consuming, please be patient")
@@ -420,17 +462,15 @@ class EstimateFeeTest(BitcoinTestFramework):
self.log.info("Test reading old fee_estimates.dat")
self.test_old_fee_estimate_file()
- self.log.info("Restarting node with fresh estimation")
- self.stop_node(0)
- fee_dat = os.path.join(self.nodes[0].chain_path, "fee_estimates.dat")
- os.remove(fee_dat)
- self.start_node(0)
- self.connect_nodes(0, 1)
- self.connect_nodes(0, 2)
+ self.clear_estimates()
self.log.info("Testing estimates with RBF.")
self.sanity_check_rbf_estimates(self.confutxo + self.memutxo)
+ self.clear_estimates()
+ self.log.info("Test estimatesmartfee modes")
+ self.test_estimation_modes()
+
self.log.info("Testing that fee estimation is disabled in blocksonly.")
self.restart_node(0, ["-blocksonly"])
assert_raises_rpc_error(
diff --git a/test/functional/feature_framework_miniwallet.py b/test/functional/feature_framework_miniwallet.py
index 1f381df236..d1aa24e7cd 100755
--- a/test/functional/feature_framework_miniwallet.py
+++ b/test/functional/feature_framework_miniwallet.py
@@ -3,6 +3,9 @@
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test MiniWallet."""
+import random
+import string
+
from test_framework.blocktools import COINBASE_MATURITY
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
@@ -31,6 +34,20 @@ class FeatureFrameworkMiniWalletTest(BitcoinTestFramework):
assert_greater_than_or_equal(tx.get_weight(), target_weight)
assert_greater_than_or_equal(target_weight + 3, tx.get_weight())
+ def test_wallet_tagging(self):
+ """Verify that tagged wallet instances are able to send funds."""
+ self.log.info(f"Test tagged wallet instances...")
+ node = self.nodes[0]
+ untagged_wallet = self.wallets[0][1]
+ for i in range(10):
+ tag = ''.join(random.choice(string.ascii_letters) for _ in range(20))
+ self.log.debug(f"-> ({i}) tag name: {tag}")
+ tagged_wallet = MiniWallet(node, tag_name=tag)
+ untagged_wallet.send_to(from_node=node, scriptPubKey=tagged_wallet.get_scriptPubKey(), amount=100000)
+ tagged_wallet.rescan_utxos()
+ tagged_wallet.send_self_transfer(from_node=node)
+ self.generate(node, 1) # clear mempool
+
def run_test(self):
node = self.nodes[0]
self.wallets = [
@@ -43,6 +60,7 @@ class FeatureFrameworkMiniWalletTest(BitcoinTestFramework):
self.generate(wallet, COINBASE_MATURITY)
self.test_tx_padding()
+ self.test_wallet_tagging()
if __name__ == '__main__':
diff --git a/test/functional/feature_proxy.py b/test/functional/feature_proxy.py
index 2201821fda..644ee5cc7f 100755
--- a/test/functional/feature_proxy.py
+++ b/test/functional/feature_proxy.py
@@ -148,7 +148,8 @@ class ProxyTest(BitcoinTestFramework):
rv = []
addr = "15.61.23.23:1234"
self.log.debug(f"Test: outgoing IPv4 connection through node {node.index} for address {addr}")
- node.addnode(addr, "onetry")
+ # v2transport=False is used to avoid reconnections with v1 being scheduled. These could interfere with later checks.
+ node.addnode(addr, "onetry", v2transport=False)
cmd = proxies[0].queue.get()
assert isinstance(cmd, Socks5Command)
# Note: bitcoind's SOCKS5 implementation only sends atyp DOMAINNAME, even if connecting directly to IPv4/IPv6
@@ -164,7 +165,7 @@ class ProxyTest(BitcoinTestFramework):
if self.have_ipv6:
addr = "[1233:3432:2434:2343:3234:2345:6546:4534]:5443"
self.log.debug(f"Test: outgoing IPv6 connection through node {node.index} for address {addr}")
- node.addnode(addr, "onetry")
+ node.addnode(addr, "onetry", v2transport=False)
cmd = proxies[1].queue.get()
assert isinstance(cmd, Socks5Command)
# Note: bitcoind's SOCKS5 implementation only sends atyp DOMAINNAME, even if connecting directly to IPv4/IPv6
@@ -180,7 +181,7 @@ class ProxyTest(BitcoinTestFramework):
if test_onion:
addr = "pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion:8333"
self.log.debug(f"Test: outgoing onion connection through node {node.index} for address {addr}")
- node.addnode(addr, "onetry")
+ node.addnode(addr, "onetry", v2transport=False)
cmd = proxies[2].queue.get()
assert isinstance(cmd, Socks5Command)
assert_equal(cmd.atyp, AddressType.DOMAINNAME)
@@ -195,7 +196,7 @@ class ProxyTest(BitcoinTestFramework):
if test_cjdns:
addr = "[fc00:1:2:3:4:5:6:7]:8888"
self.log.debug(f"Test: outgoing CJDNS connection through node {node.index} for address {addr}")
- node.addnode(addr, "onetry")
+ node.addnode(addr, "onetry", v2transport=False)
cmd = proxies[1].queue.get()
assert isinstance(cmd, Socks5Command)
assert_equal(cmd.atyp, AddressType.DOMAINNAME)
@@ -209,7 +210,7 @@ class ProxyTest(BitcoinTestFramework):
addr = "node.noumenon:8333"
self.log.debug(f"Test: outgoing DNS name connection through node {node.index} for address {addr}")
- node.addnode(addr, "onetry")
+ node.addnode(addr, "onetry", v2transport=False)
cmd = proxies[3].queue.get()
assert isinstance(cmd, Socks5Command)
assert_equal(cmd.atyp, AddressType.DOMAINNAME)
diff --git a/test/functional/feature_taproot.py b/test/functional/feature_taproot.py
index b2e030adc7..443c1c33da 100755
--- a/test/functional/feature_taproot.py
+++ b/test/functional/feature_taproot.py
@@ -231,7 +231,7 @@ def default_sigmsg(ctx):
codeseppos = get(ctx, "codeseppos")
leaf_ver = get(ctx, "leafversion")
script = get(ctx, "script_taproot")
- return TaprootSignatureMsg(tx, utxos, hashtype, idx, scriptpath=True, script=script, leaf_ver=leaf_ver, codeseparator_pos=codeseppos, annex=annex)
+ return TaprootSignatureMsg(tx, utxos, hashtype, idx, scriptpath=True, leaf_script=script, leaf_ver=leaf_ver, codeseparator_pos=codeseppos, annex=annex)
else:
return TaprootSignatureMsg(tx, utxos, hashtype, idx, scriptpath=False, annex=annex)
elif mode == "witv0":
diff --git a/test/functional/interface_rest.py b/test/functional/interface_rest.py
index d547da9cf2..ba6e960476 100755
--- a/test/functional/interface_rest.py
+++ b/test/functional/interface_rest.py
@@ -208,6 +208,8 @@ class RESTTest (BitcoinTestFramework):
self.test_rest_request(f"/getutxos/{spending[0]}_+1", ret_type=RetType.OBJ, status=400)
self.test_rest_request(f"/getutxos/{spending[0]}-+1", ret_type=RetType.OBJ, status=400)
self.test_rest_request(f"/getutxos/{spending[0]}--1", ret_type=RetType.OBJ, status=400)
+ self.test_rest_request(f"/getutxos/{spending[0]}aa-1234", ret_type=RetType.OBJ, status=400)
+ self.test_rest_request(f"/getutxos/aa-1234", ret_type=RetType.OBJ, status=400)
# Test limits
long_uri = '/'.join([f"{txid}-{n_}" for n_ in range(20)])
diff --git a/test/functional/mempool_accept.py b/test/functional/mempool_accept.py
index 2b841f3791..44a36697c6 100755
--- a/test/functional/mempool_accept.py
+++ b/test/functional/mempool_accept.py
@@ -37,6 +37,7 @@ from test_framework.script_util import (
keys_to_multisig_script,
MIN_PADDING,
MIN_STANDARD_TX_NONWITNESS_SIZE,
+ PAY_TO_ANCHOR,
script_to_p2sh_script,
script_to_p2wsh_script,
)
@@ -389,6 +390,65 @@ class MempoolAcceptanceTest(BitcoinTestFramework):
maxfeerate=0,
)
+ self.log.info('OP_1 <0x4e73> is able to be created and spent')
+ anchor_value = 10000
+ create_anchor_tx = self.wallet.send_to(from_node=node, scriptPubKey=PAY_TO_ANCHOR, amount=anchor_value)
+ self.generate(node, 1)
+
+ # First spend has non-empty witness, will be rejected to prevent third party wtxid malleability
+ anchor_nonempty_wit_spend = CTransaction()
+ anchor_nonempty_wit_spend.vin.append(CTxIn(COutPoint(int(create_anchor_tx["txid"], 16), create_anchor_tx["sent_vout"]), b""))
+ anchor_nonempty_wit_spend.vout.append(CTxOut(anchor_value - int(fee*COIN), script_to_p2wsh_script(CScript([OP_TRUE]))))
+ anchor_nonempty_wit_spend.wit.vtxinwit.append(CTxInWitness())
+ anchor_nonempty_wit_spend.wit.vtxinwit[0].scriptWitness.stack.append(b"f")
+ anchor_nonempty_wit_spend.rehash()
+
+ self.check_mempool_result(
+ result_expected=[{'txid': anchor_nonempty_wit_spend.rehash(), 'allowed': False, 'reject-reason': 'bad-witness-nonstandard'}],
+ rawtxs=[anchor_nonempty_wit_spend.serialize().hex()],
+ maxfeerate=0,
+ )
+
+ # but is consensus-legal
+ self.generateblock(node, self.wallet.get_address(), [anchor_nonempty_wit_spend.serialize().hex()])
+
+ # Without witness elements it is standard
+ create_anchor_tx = self.wallet.send_to(from_node=node, scriptPubKey=PAY_TO_ANCHOR, amount=anchor_value)
+ self.generate(node, 1)
+
+ anchor_spend = CTransaction()
+ anchor_spend.vin.append(CTxIn(COutPoint(int(create_anchor_tx["txid"], 16), create_anchor_tx["sent_vout"]), b""))
+ anchor_spend.vout.append(CTxOut(anchor_value - int(fee*COIN), script_to_p2wsh_script(CScript([OP_TRUE]))))
+ anchor_spend.wit.vtxinwit.append(CTxInWitness())
+ # It's "segwit" but txid == wtxid since there is no witness data
+ assert_equal(anchor_spend.rehash(), anchor_spend.getwtxid())
+
+ self.check_mempool_result(
+ result_expected=[{'txid': anchor_spend.rehash(), 'allowed': True, 'vsize': anchor_spend.get_vsize(), 'fees': { 'base': Decimal('0.00000700')}}],
+ rawtxs=[anchor_spend.serialize().hex()],
+ maxfeerate=0,
+ )
+
+ self.log.info('But cannot be spent if nested sh()')
+ nested_anchor_tx = self.wallet.create_self_transfer(sequence=SEQUENCE_FINAL)['tx']
+ nested_anchor_tx.vout[0].scriptPubKey = script_to_p2sh_script(PAY_TO_ANCHOR)
+ nested_anchor_tx.rehash()
+ self.generateblock(node, self.wallet.get_address(), [nested_anchor_tx.serialize().hex()])
+
+ nested_anchor_spend = CTransaction()
+ nested_anchor_spend.vin.append(CTxIn(COutPoint(nested_anchor_tx.sha256, 0), b""))
+ nested_anchor_spend.vin[0].scriptSig = CScript([bytes(PAY_TO_ANCHOR)])
+ nested_anchor_spend.vout.append(CTxOut(nested_anchor_tx.vout[0].nValue - int(fee*COIN), script_to_p2wsh_script(CScript([OP_TRUE]))))
+ nested_anchor_spend.rehash()
+
+ self.check_mempool_result(
+ result_expected=[{'txid': nested_anchor_spend.rehash(), 'allowed': False, 'reject-reason': 'non-mandatory-script-verify-flag (Witness version reserved for soft-fork upgrades)'}],
+ rawtxs=[nested_anchor_spend.serialize().hex()],
+ maxfeerate=0,
+ )
+ # but is consensus-legal
+ self.generateblock(node, self.wallet.get_address(), [nested_anchor_spend.serialize().hex()])
+
self.log.info('Spending a confirmed bare multisig is okay')
address = self.wallet.get_address()
tx = tx_from_hex(raw_tx_reference)
diff --git a/test/functional/mempool_reorg.py b/test/functional/mempool_reorg.py
index e27942760c..74a353ab01 100755
--- a/test/functional/mempool_reorg.py
+++ b/test/functional/mempool_reorg.py
@@ -162,7 +162,7 @@ class MempoolCoinbaseTest(BitcoinTestFramework):
self.log.info("Generate a block")
last_block = self.generate(self.nodes[0], 1)
# generate() implicitly syncs blocks, so that peer 1 gets the block before timelock_tx
- # Otherwise, peer 1 would put the timelock_tx in m_recent_rejects
+ # Otherwise, peer 1 would put the timelock_tx in m_lazy_recent_rejects
self.log.info("The time-locked transaction can now be spent")
timelock_tx_id = self.nodes[0].sendrawtransaction(timelock_tx)
diff --git a/test/functional/p2p_permissions.py b/test/functional/p2p_permissions.py
index 5ca5101613..a9b164b078 100755
--- a/test/functional/p2p_permissions.py
+++ b/test/functional/p2p_permissions.py
@@ -121,7 +121,7 @@ class P2PPermissionsTests(BitcoinTestFramework):
tx.vout[0].nValue += 1
txid = tx.rehash()
# Send the transaction twice. The first time, it'll be rejected by ATMP because it conflicts
- # with a mempool transaction. The second time, it'll be in the m_recent_rejects filter.
+ # with a mempool transaction. The second time, it'll be in the m_lazy_recent_rejects filter.
p2p_rebroadcast_wallet.send_txs_and_test(
[tx],
self.nodes[1],
diff --git a/test/functional/rpc_decodescript.py b/test/functional/rpc_decodescript.py
index 66dbd78573..4f127fa8b7 100755
--- a/test/functional/rpc_decodescript.py
+++ b/test/functional/rpc_decodescript.py
@@ -187,6 +187,16 @@ class DecodeScriptTest(BitcoinTestFramework):
assert_equal('1 ' + xonly_public_key, rpc_result['asm'])
assert 'segwit' not in rpc_result
+ self.log.info("- P2A (anchor)")
+ # 1 <4e73>
+ witprog_hex = '4e73'
+ rpc_result = self.nodes[0].decodescript('5102' + witprog_hex)
+ assert_equal('anchor', rpc_result['type'])
+ # in the disassembly, the witness program is shown as single decimal due to its small size
+ witprog_as_decimal = int.from_bytes(bytes.fromhex(witprog_hex), 'little')
+ assert_equal(f'1 {witprog_as_decimal}', rpc_result['asm'])
+ assert_equal('bcrt1pfeesnyr2tx', rpc_result['address'])
+
def decoderawtransaction_asm_sighashtype(self):
"""Test decoding scripts via RPC command "decoderawtransaction".
diff --git a/test/functional/rpc_scantxoutset.py b/test/functional/rpc_scantxoutset.py
index 078a24d577..e53e2fa910 100755
--- a/test/functional/rpc_scantxoutset.py
+++ b/test/functional/rpc_scantxoutset.py
@@ -120,7 +120,15 @@ class ScantxoutsetTest(BitcoinTestFramework):
assert_equal(self.nodes[0].scantxoutset("status"), None)
assert_equal(self.nodes[0].scantxoutset("abort"), False)
- # check that first arg is needed
+ # Check that the blockhash and confirmations fields are correct
+ self.generate(self.nodes[0], 2)
+ unspent = self.nodes[0].scantxoutset("start", ["addr(mpQ8rokAhp1TAtJQR6F6TaUmjAWkAWYYBq)"])["unspents"][0]
+ blockhash = self.nodes[0].getblockhash(info["height"])
+ assert_equal(unspent["height"], info["height"])
+ assert_equal(unspent["blockhash"], blockhash)
+ assert_equal(unspent["confirmations"], 3)
+
+ # Check that first arg is needed
assert_raises_rpc_error(-1, "scantxoutset \"action\" ( [scanobjects,...] )", self.nodes[0].scantxoutset)
# Check that second arg is needed for start
diff --git a/test/functional/rpc_signrawtransactionwithkey.py b/test/functional/rpc_signrawtransactionwithkey.py
index 9dae0ba994..be96742aa8 100755
--- a/test/functional/rpc_signrawtransactionwithkey.py
+++ b/test/functional/rpc_signrawtransactionwithkey.py
@@ -9,6 +9,7 @@ from test_framework.blocktools import (
)
from test_framework.address import (
address_to_scriptpubkey,
+ p2a,
script_to_p2sh,
)
from test_framework.test_framework import BitcoinTestFramework
@@ -100,6 +101,18 @@ class SignRawTransactionWithKeyTest(BitcoinTestFramework):
for tx_type in ['P2PKH', 'P2PK']: # these tests are order-independent
self.verify_txn_with_witness_script(tx_type)
+ def keyless_signing_test(self):
+ self.log.info("Test that keyless 'signing' of pay-to-anchor input succeeds")
+ funding_txid = self.send_to_address(p2a(), 49.999)
+ spending_tx = self.nodes[0].createrawtransaction(
+ [{"txid": funding_txid, "vout": 0}],
+ [{getnewdestination()[2]: Decimal("49.998")}])
+ spending_tx_signed = self.nodes[0].signrawtransactionwithkey(spending_tx, [], [])
+ self.assert_signing_completed_successfully(spending_tx_signed)
+ assert self.nodes[0].testmempoolaccept([spending_tx_signed["hex"]])[0]["allowed"]
+ # 'signing' a P2A prevout is a no-op, so signed and unsigned txs shouldn't differ
+ assert_equal(spending_tx, spending_tx_signed["hex"])
+
def verify_txn_with_witness_script(self, tx_type):
self.log.info("Test with a {} script as the witnessScript".format(tx_type))
embedded_privkey, embedded_pubkey = generate_keypair(wif=True)
@@ -138,6 +151,7 @@ class SignRawTransactionWithKeyTest(BitcoinTestFramework):
def run_test(self):
self.successful_signing_test()
self.witness_script_test()
+ self.keyless_signing_test()
self.invalid_sighashtype_test()
self.invalid_private_key_and_tx()
diff --git a/test/functional/rpc_validateaddress.py b/test/functional/rpc_validateaddress.py
index dde07e7ead..bf094a7df8 100755
--- a/test/functional/rpc_validateaddress.py
+++ b/test/functional/rpc_validateaddress.py
@@ -166,6 +166,11 @@ VALID_DATA = [
"bc1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vqzk5jj0",
"512079be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
),
+ # PayToAnchor(P2A)
+ (
+ "bc1pfeessrawgf",
+ "51024e73",
+ ),
]
diff --git a/test/functional/test_framework/address.py b/test/functional/test_framework/address.py
index bcb38b21cd..2c754e35aa 100644
--- a/test/functional/test_framework/address.py
+++ b/test/functional/test_framework/address.py
@@ -53,13 +53,14 @@ def create_deterministic_address_bcrt1_p2tr_op_true(explicit_internal_key=None):
can be spent with a witness stack of OP_TRUE and the control block
with internal public key (script-path spending).
- Returns a tuple with the generated address and the internal key.
+ Returns a tuple with the generated address and the TaprootInfo object.
"""
internal_key = explicit_internal_key or (1).to_bytes(32, 'big')
- address = output_key_to_p2tr(taproot_construct(internal_key, [(None, CScript([OP_TRUE]))]).output_pubkey)
+ taproot_info = taproot_construct(internal_key, [("only-path", CScript([OP_TRUE]))])
+ address = output_key_to_p2tr(taproot_info.output_pubkey)
if explicit_internal_key is None:
assert_equal(address, 'bcrt1p9yfmy5h72durp7zrhlw9lf7jpwjgvwdg0jr0lqmmjtgg83266lqsekaqka')
- return (address, internal_key)
+ return (address, taproot_info)
def byte_to_base58(b, version):
@@ -154,6 +155,9 @@ def output_key_to_p2tr(key, main=False):
assert len(key) == 32
return program_to_witness(1, key, main)
+def p2a(main=False):
+ return program_to_witness(1, "4e73", main)
+
def check_key(key):
if (type(key) is str):
key = bytes.fromhex(key) # Assuming this is hex string
diff --git a/test/functional/test_framework/blocktools.py b/test/functional/test_framework/blocktools.py
index 338b7fa3b0..5c2fa28a31 100644
--- a/test/functional/test_framework/blocktools.py
+++ b/test/functional/test_framework/blocktools.py
@@ -154,16 +154,18 @@ def create_coinbase(height, pubkey=None, *, script_pubkey=None, extra_output_scr
coinbase.calc_sha256()
return coinbase
-def create_tx_with_script(prevtx, n, script_sig=b"", *, amount, script_pub_key=CScript()):
+def create_tx_with_script(prevtx, n, script_sig=b"", *, amount, output_script=None):
"""Return one-input, one-output transaction object
spending the prevtx's n-th output with the given amount.
Can optionally pass scriptPubKey and scriptSig, default is anyone-can-spend output.
"""
+ if output_script is None:
+ output_script = CScript()
tx = CTransaction()
assert n < len(prevtx.vout)
tx.vin.append(CTxIn(COutPoint(prevtx.sha256, n), script_sig, SEQUENCE_FINAL))
- tx.vout.append(CTxOut(amount, script_pub_key))
+ tx.vout.append(CTxOut(amount, output_script))
tx.calc_sha256()
return tx
diff --git a/test/functional/test_framework/messages.py b/test/functional/test_framework/messages.py
index 005f7546a8..1f566a1348 100755
--- a/test/functional/test_framework/messages.py
+++ b/test/functional/test_framework/messages.py
@@ -1294,8 +1294,11 @@ class msg_tx:
__slots__ = ("tx",)
msgtype = b"tx"
- def __init__(self, tx=CTransaction()):
- self.tx = tx
+ def __init__(self, tx=None):
+ if tx is None:
+ self.tx = CTransaction()
+ else:
+ self.tx = tx
def deserialize(self, f):
self.tx.deserialize(f)
diff --git a/test/functional/test_framework/script.py b/test/functional/test_framework/script.py
index 97d62f957b..d510cf9b1c 100644
--- a/test/functional/test_framework/script.py
+++ b/test/functional/test_framework/script.py
@@ -810,7 +810,7 @@ def BIP341_sha_sequences(txTo):
def BIP341_sha_outputs(txTo):
return sha256(b"".join(o.serialize() for o in txTo.vout))
-def TaprootSignatureMsg(txTo, spent_utxos, hash_type, input_index = 0, scriptpath = False, script = CScript(), codeseparator_pos = -1, annex = None, leaf_ver = LEAF_VERSION_TAPSCRIPT):
+def TaprootSignatureMsg(txTo, spent_utxos, hash_type, input_index=0, *, scriptpath=False, leaf_script=None, codeseparator_pos=-1, annex=None, leaf_ver=LEAF_VERSION_TAPSCRIPT):
assert (len(txTo.vin) == len(spent_utxos))
assert (input_index < len(txTo.vin))
out_type = SIGHASH_ALL if hash_type == 0 else hash_type & 3
@@ -829,7 +829,7 @@ def TaprootSignatureMsg(txTo, spent_utxos, hash_type, input_index = 0, scriptpat
spend_type = 0
if annex is not None:
spend_type |= 1
- if (scriptpath):
+ if scriptpath:
spend_type |= 2
ss += bytes([spend_type])
if in_type == SIGHASH_ANYONECANPAY:
@@ -846,11 +846,11 @@ def TaprootSignatureMsg(txTo, spent_utxos, hash_type, input_index = 0, scriptpat
ss += sha256(txTo.vout[input_index].serialize())
else:
ss += bytes(0 for _ in range(32))
- if (scriptpath):
- ss += TaggedHash("TapLeaf", bytes([leaf_ver]) + ser_string(script))
+ if scriptpath:
+ ss += TaggedHash("TapLeaf", bytes([leaf_ver]) + ser_string(leaf_script))
ss += bytes([0])
ss += codeseparator_pos.to_bytes(4, "little", signed=True)
- assert len(ss) == 175 - (in_type == SIGHASH_ANYONECANPAY) * 49 - (out_type != SIGHASH_ALL and out_type != SIGHASH_SINGLE) * 32 + (annex is not None) * 32 + scriptpath * 37
+ assert len(ss) == 175 - (in_type == SIGHASH_ANYONECANPAY) * 49 - (out_type != SIGHASH_ALL and out_type != SIGHASH_SINGLE) * 32 + (annex is not None) * 32 + scriptpath * 37
return ss
def TaprootSignatureHash(*args, **kwargs):
diff --git a/test/functional/test_framework/script_util.py b/test/functional/test_framework/script_util.py
index 855f3b8cf5..938183ece4 100755
--- a/test/functional/test_framework/script_util.py
+++ b/test/functional/test_framework/script_util.py
@@ -8,6 +8,7 @@ import unittest
from test_framework.script import (
CScript,
OP_0,
+ OP_1,
OP_15,
OP_16,
OP_CHECKMULTISIG,
@@ -42,6 +43,8 @@ assert MIN_PADDING == 5
DUMMY_MIN_OP_RETURN_SCRIPT = CScript([OP_RETURN] + ([OP_0] * (MIN_PADDING - 1)))
assert len(DUMMY_MIN_OP_RETURN_SCRIPT) == MIN_PADDING
+PAY_TO_ANCHOR = CScript([OP_1, bytes.fromhex("4e73")])
+
def key_to_p2pk_script(key):
key = check_key(key)
return CScript([key, OP_CHECKSIG])
diff --git a/test/functional/test_framework/wallet.py b/test/functional/test_framework/wallet.py
index cb0d291361..f3713f297e 100644
--- a/test/functional/test_framework/wallet.py
+++ b/test/functional/test_framework/wallet.py
@@ -39,7 +39,6 @@ from test_framework.messages import (
)
from test_framework.script import (
CScript,
- LEAF_VERSION_TAPSCRIPT,
OP_1,
OP_NOP,
OP_RETURN,
@@ -106,8 +105,8 @@ class MiniWallet:
pub_key = self._priv_key.get_pubkey()
self._scriptPubKey = key_to_p2pk_script(pub_key.get_bytes())
elif mode == MiniWalletMode.ADDRESS_OP_TRUE:
- internal_key = None if tag_name is None else hash256(tag_name.encode())
- self._address, self._internal_key = create_deterministic_address_bcrt1_p2tr_op_true(internal_key)
+ internal_key = None if tag_name is None else compute_xonly_pubkey(hash256(tag_name.encode()))[0]
+ self._address, self._taproot_info = create_deterministic_address_bcrt1_p2tr_op_true(internal_key)
self._scriptPubKey = address_to_scriptpubkey(self._address)
# When the pre-mined test framework chain is used, it contains coinbase
@@ -195,7 +194,12 @@ class MiniWallet:
elif self._mode == MiniWalletMode.ADDRESS_OP_TRUE:
tx.wit.vtxinwit = [CTxInWitness()] * len(tx.vin)
for i in tx.wit.vtxinwit:
- i.scriptWitness.stack = [CScript([OP_TRUE]), bytes([LEAF_VERSION_TAPSCRIPT]) + self._internal_key]
+ assert_equal(len(self._taproot_info.leaves), 1)
+ leaf_info = list(self._taproot_info.leaves.values())[0]
+ i.scriptWitness.stack = [
+ leaf_info.script,
+ bytes([leaf_info.version | self._taproot_info.negflag]) + self._taproot_info.internal_pubkey,
+ ]
else:
assert False