aboutsummaryrefslogtreecommitdiff
path: root/test/functional
diff options
context:
space:
mode:
Diffstat (limited to 'test/functional')
-rwxr-xr-xtest/functional/feature_dbcrash.py8
-rwxr-xr-xtest/functional/feature_maxuploadtarget.py2
-rwxr-xr-xtest/functional/feature_minchainwork.py2
-rwxr-xr-xtest/functional/feature_nulldummy.py65
-rwxr-xr-xtest/functional/feature_rbf.py388
-rwxr-xr-xtest/functional/mempool_limit.py2
-rwxr-xr-xtest/functional/mempool_updatefromblock.py19
-rwxr-xr-xtest/functional/mining_prioritisetransaction.py2
-rwxr-xr-xtest/functional/p2p_invalid_tx.py68
-rwxr-xr-xtest/functional/p2p_segwit.py4
-rwxr-xr-xtest/functional/p2p_timeouts.py5
-rwxr-xr-xtest/functional/rpc_packages.py133
-rw-r--r--test/functional/test_framework/blocktools.py24
-rw-r--r--test/functional/test_framework/util.py19
-rw-r--r--test/functional/test_framework/wallet.py24
-rwxr-xr-xtest/functional/test_runner.py5
-rwxr-xr-xtest/functional/wallet_basic.py2
17 files changed, 477 insertions, 295 deletions
diff --git a/test/functional/feature_dbcrash.py b/test/functional/feature_dbcrash.py
index 62e9bec663..f606f26e70 100755
--- a/test/functional/feature_dbcrash.py
+++ b/test/functional/feature_dbcrash.py
@@ -62,8 +62,8 @@ class ChainstateWriteCrashTest(BitcoinTestFramework):
self.node2_args = ["-dbcrashratio=24", "-dbcache=16"] + self.base_args
# Node3 is a normal node with default args, except will mine full blocks
- # and non-standard txs (e.g. txs with "dust" outputs)
- self.node3_args = ["-blockmaxweight=4000000", "-acceptnonstdtxn"]
+ # and txs with "dust" outputs
+ self.node3_args = ["-blockmaxweight=4000000", "-dustrelayfee=0"]
self.extra_args = [self.node0_args, self.node1_args, self.node2_args, self.node3_args]
def setup_network(self):
@@ -211,7 +211,9 @@ class ChainstateWriteCrashTest(BitcoinTestFramework):
self.crashed_on_restart = 0 # Track count of crashes during recovery
# Start by creating a lot of utxos on node3
- utxo_list = self.wallet.send_self_transfer_multi(from_node=self.nodes[3], num_outputs=5000)['new_utxos']
+ utxo_list = []
+ for _ in range(5):
+ utxo_list.extend(self.wallet.send_self_transfer_multi(from_node=self.nodes[3], num_outputs=1000)['new_utxos'])
self.generate(self.nodes[3], 1, sync_fun=self.no_op)
assert_equal(len(self.nodes[3].getrawmempool()), 0)
self.log.info(f"Prepped {len(utxo_list)} utxo entries")
diff --git a/test/functional/feature_maxuploadtarget.py b/test/functional/feature_maxuploadtarget.py
index 0b9d651226..3ea412002a 100755
--- a/test/functional/feature_maxuploadtarget.py
+++ b/test/functional/feature_maxuploadtarget.py
@@ -46,7 +46,7 @@ class MaxUploadTest(BitcoinTestFramework):
self.num_nodes = 1
self.extra_args = [[
"-maxuploadtarget=800M",
- "-acceptnonstdtxn=1",
+ "-datacarriersize=100000",
]]
self.supports_cli = False
diff --git a/test/functional/feature_minchainwork.py b/test/functional/feature_minchainwork.py
index fa10855a98..9d0ad5ef9d 100755
--- a/test/functional/feature_minchainwork.py
+++ b/test/functional/feature_minchainwork.py
@@ -82,7 +82,7 @@ class MinimumChainWorkTest(BitcoinTestFramework):
msg.hashstop = 0
peer.send_and_ping(msg)
time.sleep(5)
- assert "headers" not in peer.last_message
+ assert ("headers" not in peer.last_message or len(peer.last_message["headers"].headers) == 0)
self.log.info("Generating one more block")
self.generate(self.nodes[0], 1)
diff --git a/test/functional/feature_nulldummy.py b/test/functional/feature_nulldummy.py
index 7a84098a83..9bfb79057e 100755
--- a/test/functional/feature_nulldummy.py
+++ b/test/functional/feature_nulldummy.py
@@ -19,9 +19,11 @@ from test_framework.blocktools import (
NORMAL_GBT_REQUEST_PARAMS,
add_witness_commitment,
create_block,
- create_transaction,
)
-from test_framework.messages import CTransaction
+from test_framework.messages import (
+ CTransaction,
+ tx_from_hex,
+)
from test_framework.script import (
OP_0,
OP_TRUE,
@@ -31,6 +33,9 @@ from test_framework.util import (
assert_equal,
assert_raises_rpc_error,
)
+from test_framework.wallet import getnewdestination
+from test_framework.key import ECKey
+from test_framework.wallet_util import bytes_to_wif
NULLDUMMY_ERROR = "non-mandatory-script-verify-flag (Dummy CHECKMULTISIG argument must be zero)"
@@ -55,22 +60,26 @@ class NULLDUMMYTest(BitcoinTestFramework):
'-par=1', # Use only one script thread to get the exact reject reason for testing
]]
- def skip_test_if_missing_module(self):
- self.skip_if_no_wallet()
+ def create_transaction(self, *, txid, input_details=None, addr, amount, privkey):
+ input = {"txid": txid, "vout": 0}
+ output = {addr: amount}
+ rawtx = self.nodes[0].createrawtransaction([input], output)
+ # Details only needed for scripthash or witness spends
+ input = None if not input_details else [{**input, **input_details}]
+ signedtx = self.nodes[0].signrawtransactionwithkey(rawtx, [privkey], input)
+ return tx_from_hex(signedtx["hex"])
def run_test(self):
- self.nodes[0].createwallet(wallet_name='wmulti', disable_private_keys=True)
- wmulti = self.nodes[0].get_wallet_rpc('wmulti')
- w0 = self.nodes[0].get_wallet_rpc(self.default_wallet_name)
- self.address = w0.getnewaddress()
- self.pubkey = w0.getaddressinfo(self.address)['pubkey']
- self.ms_address = wmulti.addmultisigaddress(1, [self.pubkey])['address']
- self.wit_address = w0.getnewaddress(address_type='p2sh-segwit')
- self.wit_ms_address = wmulti.addmultisigaddress(1, [self.pubkey], '', 'p2sh-segwit')['address']
- if not self.options.descriptors:
- # Legacy wallets need to import these so that they are watched by the wallet. This is unnecessary (and does not need to be tested) for descriptor wallets
- wmulti.importaddress(self.ms_address)
- wmulti.importaddress(self.wit_ms_address)
+ eckey = ECKey()
+ eckey.generate()
+ self.privkey = bytes_to_wif(eckey.get_bytes())
+ self.pubkey = eckey.get_pubkey().get_bytes().hex()
+ cms = self.nodes[0].createmultisig(1, [self.pubkey])
+ wms = self.nodes[0].createmultisig(1, [self.pubkey], 'p2sh-segwit')
+ self.ms_address = cms["address"]
+ ms_unlock_details = {"scriptPubKey": self.nodes[0].validateaddress(self.ms_address)["scriptPubKey"],
+ "redeemScript": cms["redeemScript"]}
+ self.wit_ms_address = wms['address']
self.coinbase_blocks = self.generate(self.nodes[0], 2) # block height = 2
coinbase_txid = []
@@ -82,16 +91,23 @@ class NULLDUMMYTest(BitcoinTestFramework):
self.lastblocktime = int(time.time()) + self.lastblockheight
self.log.info(f"Test 1: NULLDUMMY compliant base transactions should be accepted to mempool and mined before activation [{COINBASE_MATURITY + 3}]")
- test1txs = [create_transaction(self.nodes[0], coinbase_txid[0], self.ms_address, amount=49)]
+ test1txs = [self.create_transaction(txid=coinbase_txid[0], addr=self.ms_address, amount=49,
+ privkey=self.nodes[0].get_deterministic_priv_key().key)]
txid1 = self.nodes[0].sendrawtransaction(test1txs[0].serialize_with_witness().hex(), 0)
- test1txs.append(create_transaction(self.nodes[0], txid1, self.ms_address, amount=48))
+ test1txs.append(self.create_transaction(txid=txid1, input_details=ms_unlock_details,
+ addr=self.ms_address, amount=48,
+ privkey=self.privkey))
txid2 = self.nodes[0].sendrawtransaction(test1txs[1].serialize_with_witness().hex(), 0)
- test1txs.append(create_transaction(self.nodes[0], coinbase_txid[1], self.wit_ms_address, amount=49))
+ test1txs.append(self.create_transaction(txid=coinbase_txid[1],
+ addr=self.wit_ms_address, amount=49,
+ privkey=self.nodes[0].get_deterministic_priv_key().key))
txid3 = self.nodes[0].sendrawtransaction(test1txs[2].serialize_with_witness().hex(), 0)
self.block_submit(self.nodes[0], test1txs, accept=True)
self.log.info("Test 2: Non-NULLDUMMY base multisig transaction should not be accepted to mempool before activation")
- test2tx = create_transaction(self.nodes[0], txid2, self.ms_address, amount=47)
+ test2tx = self.create_transaction(txid=txid2, input_details=ms_unlock_details,
+ addr=self.ms_address, amount=47,
+ privkey=self.privkey)
invalidate_nulldummy_tx(test2tx)
assert_raises_rpc_error(-26, NULLDUMMY_ERROR, self.nodes[0].sendrawtransaction, test2tx.serialize_with_witness().hex(), 0)
@@ -99,14 +115,19 @@ class NULLDUMMYTest(BitcoinTestFramework):
self.block_submit(self.nodes[0], [test2tx], accept=True)
self.log.info("Test 4: Non-NULLDUMMY base multisig transaction is invalid after activation")
- test4tx = create_transaction(self.nodes[0], test2tx.hash, self.address, amount=46)
+ test4tx = self.create_transaction(txid=test2tx.hash, input_details=ms_unlock_details,
+ addr=getnewdestination()[2], amount=46,
+ privkey=self.privkey)
test6txs = [CTransaction(test4tx)]
invalidate_nulldummy_tx(test4tx)
assert_raises_rpc_error(-26, NULLDUMMY_ERROR, self.nodes[0].sendrawtransaction, test4tx.serialize_with_witness().hex(), 0)
self.block_submit(self.nodes[0], [test4tx], accept=False)
self.log.info("Test 5: Non-NULLDUMMY P2WSH multisig transaction invalid after activation")
- test5tx = create_transaction(self.nodes[0], txid3, self.wit_address, amount=48)
+ test5tx = self.create_transaction(txid=txid3, input_details={"scriptPubKey": test1txs[2].vout[0].scriptPubKey.hex(),
+ "amount": 49, "witnessScript": wms["redeemScript"]},
+ addr=getnewdestination(address_type='p2sh-segwit')[2], amount=48,
+ privkey=self.privkey)
test6txs.append(CTransaction(test5tx))
test5tx.wit.vtxinwit[0].scriptWitness.stack[0] = b'\x01'
assert_raises_rpc_error(-26, NULLDUMMY_ERROR, self.nodes[0].sendrawtransaction, test5tx.serialize_with_witness().hex(), 0)
diff --git a/test/functional/feature_rbf.py b/test/functional/feature_rbf.py
index 5558c76138..8e5cbba01a 100755
--- a/test/functional/feature_rbf.py
+++ b/test/functional/feature_rbf.py
@@ -4,28 +4,18 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test the RBF code."""
-from copy import deepcopy
from decimal import Decimal
from test_framework.messages import (
BIP125_SEQUENCE_NUMBER,
COIN,
- COutPoint,
- CTransaction,
- CTxIn,
- CTxOut,
SEQUENCE_FINAL,
)
-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,
)
-from test_framework.script_util import (
- DUMMY_P2WPKH_SCRIPT,
- DUMMY_2_P2WPKH_SCRIPT,
-)
from test_framework.wallet import MiniWallet
from test_framework.address import ADDRESS_BCRT1_UNSPENDABLE
@@ -35,7 +25,6 @@ class ReplaceByFeeTest(BitcoinTestFramework):
self.num_nodes = 2
self.extra_args = [
[
- "-acceptnonstdtxn=1",
"-maxorphantx=1000",
"-limitancestorcount=50",
"-limitancestorsize=101",
@@ -99,15 +88,14 @@ class ReplaceByFeeTest(BitcoinTestFramework):
self.log.info("Passed")
- def make_utxo(self, node, amount, confirmed=True, scriptPubKey=DUMMY_P2WPKH_SCRIPT):
+ def make_utxo(self, node, amount, *, confirmed=True, scriptPubKey=None):
"""Create a txout with a given amount and scriptPubKey
- confirmed - txouts created will be confirmed in the blockchain;
+ confirmed - txout created will be confirmed in the blockchain;
unconfirmed otherwise.
"""
- txid, n = self.wallet.send_to(from_node=node, scriptPubKey=scriptPubKey, amount=amount)
+ txid, n = self.wallet.send_to(from_node=node, scriptPubKey=scriptPubKey or self.wallet.get_scriptPubKey(), amount=amount)
- # If requested, ensure txouts are confirmed.
if confirmed:
mempool_size = len(node.getrawmempool())
while mempool_size > 0:
@@ -118,30 +106,24 @@ class ReplaceByFeeTest(BitcoinTestFramework):
assert new_size < mempool_size
mempool_size = new_size
- return COutPoint(int(txid, 16), n)
+ return self.wallet.get_utxo(txid=txid, vout=n)
def test_simple_doublespend(self):
"""Simple doublespend"""
# we use MiniWallet to create a transaction template with inputs correctly set,
# and modify the output (amount, scriptPubKey) according to our needs
- tx_template = self.wallet.create_self_transfer()['tx']
-
- tx1a = deepcopy(tx_template)
- tx1a.vout = [CTxOut(1 * COIN, DUMMY_P2WPKH_SCRIPT)]
- tx1a_hex = tx1a.serialize().hex()
- tx1a_txid = self.nodes[0].sendrawtransaction(tx1a_hex, 0)
+ tx = self.wallet.create_self_transfer()["tx"]
+ tx1a_txid = self.nodes[0].sendrawtransaction(tx.serialize().hex())
# Should fail because we haven't changed the fee
- tx1b = deepcopy(tx_template)
- tx1b.vout = [CTxOut(1 * COIN, DUMMY_2_P2WPKH_SCRIPT)]
- tx1b_hex = tx1b.serialize().hex()
+ tx.vout[0].scriptPubKey[-1] ^= 1
# This will raise an exception due to insufficient fee
- assert_raises_rpc_error(-26, "insufficient fee", self.nodes[0].sendrawtransaction, tx1b_hex, 0)
+ assert_raises_rpc_error(-26, "insufficient fee", self.nodes[0].sendrawtransaction, tx.serialize().hex(), 0)
# Extra 0.1 BTC fee
- tx1b.vout[0].nValue -= int(0.1 * COIN)
- tx1b_hex = tx1b.serialize().hex()
+ tx.vout[0].nValue -= int(0.1 * COIN)
+ tx1b_hex = tx.serialize().hex()
# Works when enabled
tx1b_txid = self.nodes[0].sendrawtransaction(tx1b_hex, 0)
@@ -163,28 +145,28 @@ class ReplaceByFeeTest(BitcoinTestFramework):
chain_txids = []
while remaining_value > 1 * COIN:
remaining_value -= int(0.1 * COIN)
- tx = CTransaction()
- tx.vin = [CTxIn(prevout, nSequence=0)]
- tx.vout = [CTxOut(remaining_value, CScript([1, OP_DROP] * 15 + [1]))]
- tx_hex = tx.serialize().hex()
- txid = self.nodes[0].sendrawtransaction(tx_hex, 0)
- chain_txids.append(txid)
- prevout = COutPoint(int(txid, 16), 0)
+ prevout = self.wallet.send_self_transfer(
+ from_node=self.nodes[0],
+ utxo_to_spend=prevout,
+ sequence=0,
+ fee=Decimal("0.1"),
+ )["new_utxo"]
+ chain_txids.append(prevout["txid"])
# Whether the double-spend is allowed is evaluated by including all
# child fees - 4 BTC - so this attempt is rejected.
- dbl_tx = CTransaction()
- dbl_tx.vin = [CTxIn(tx0_outpoint, nSequence=0)]
- dbl_tx.vout = [CTxOut(initial_nValue - 3 * COIN, DUMMY_P2WPKH_SCRIPT)]
+ dbl_tx = self.wallet.create_self_transfer(
+ utxo_to_spend=tx0_outpoint,
+ sequence=0,
+ fee=Decimal("3"),
+ )["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)
# Accepted with sufficient fee
- dbl_tx = CTransaction()
- dbl_tx.vin = [CTxIn(tx0_outpoint, nSequence=0)]
- dbl_tx.vout = [CTxOut(int(0.1 * COIN), DUMMY_P2WPKH_SCRIPT)]
+ dbl_tx.vout[0].nValue = int(0.1 * COIN)
dbl_tx_hex = dbl_tx.serialize().hex()
self.nodes[0].sendrawtransaction(dbl_tx_hex, 0)
@@ -208,22 +190,19 @@ class ReplaceByFeeTest(BitcoinTestFramework):
if txout_value < fee:
return
- vout = [CTxOut(txout_value, CScript([i+1]))
- for i in range(tree_width)]
- tx = CTransaction()
- tx.vin = [CTxIn(prevout, nSequence=0)]
- tx.vout = vout
- tx_hex = tx.serialize().hex()
+ tx = self.wallet.send_self_transfer_multi(
+ utxos_to_spend=[prevout],
+ from_node=self.nodes[0],
+ sequence=0,
+ num_outputs=tree_width,
+ amount_per_output=txout_value,
+ )
- assert len(tx.serialize()) < 100000
- txid = self.nodes[0].sendrawtransaction(tx_hex, 0)
- yield tx
+ yield tx["txid"]
_total_txs[0] += 1
- txid = int(txid, 16)
-
- for i, txout in enumerate(tx.vout):
- for x in branch(COutPoint(txid, i), txout_value,
+ for utxo in tx["new_utxos"]:
+ for x in branch(utxo, txout_value,
max_txs,
tree_width=tree_width, fee=fee,
_total_txs=_total_txs):
@@ -235,25 +214,26 @@ class ReplaceByFeeTest(BitcoinTestFramework):
assert_equal(len(tree_txs), n)
# Attempt double-spend, will fail because too little fee paid
- 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 = dbl_tx.serialize().hex()
+ dbl_tx_hex = self.wallet.create_self_transfer(
+ utxo_to_spend=tx0_outpoint,
+ sequence=0,
+ fee=(Decimal(fee) / COIN) * n,
+ )["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)
# 0.1 BTC fee is enough
- dbl_tx = CTransaction()
- dbl_tx.vin = [CTxIn(tx0_outpoint, nSequence=0)]
- dbl_tx.vout = [CTxOut(initial_nValue - fee * n - int(0.1 * COIN), DUMMY_P2WPKH_SCRIPT)]
- dbl_tx_hex = dbl_tx.serialize().hex()
+ dbl_tx_hex = self.wallet.create_self_transfer(
+ utxo_to_spend=tx0_outpoint,
+ sequence=0,
+ fee=(Decimal(fee) / COIN) * n + Decimal("0.1"),
+ )["hex"]
self.nodes[0].sendrawtransaction(dbl_tx_hex, 0)
mempool = self.nodes[0].getrawmempool()
- for tx in tree_txs:
- tx.rehash()
- assert tx.hash not in mempool
+ for txid in tree_txs:
+ assert txid not in mempool
# Try again, but with more total transactions than the "max txs
# double-spent at once" anti-DoS limit.
@@ -263,33 +243,36 @@ class ReplaceByFeeTest(BitcoinTestFramework):
tree_txs = list(branch(tx0_outpoint, initial_nValue, n, fee=fee))
assert_equal(len(tree_txs), n)
- 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 = dbl_tx.serialize().hex()
+ dbl_tx_hex = self.wallet.create_self_transfer(
+ utxo_to_spend=tx0_outpoint,
+ sequence=0,
+ fee=2 * (Decimal(fee) / COIN) * n,
+ )["hex"]
# This will raise an exception
assert_raises_rpc_error(-26, "too many potential replacements", self.nodes[0].sendrawtransaction, dbl_tx_hex, 0)
- for tx in tree_txs:
- tx.rehash()
- self.nodes[0].getrawtransaction(tx.hash)
+ for txid in tree_txs:
+ self.nodes[0].getrawtransaction(txid)
def test_replacement_feeperkb(self):
"""Replacement requires fee-per-KB to be higher"""
tx0_outpoint = self.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 = tx1a.serialize().hex()
- self.nodes[0].sendrawtransaction(tx1a_hex, 0)
+ self.wallet.send_self_transfer(
+ from_node=self.nodes[0],
+ utxo_to_spend=tx0_outpoint,
+ sequence=0,
+ fee=Decimal("0.1"),
+ )
# 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 = tx1b.serialize().hex()
+ tx1b_hex = self.wallet.create_self_transfer_multi(
+ utxos_to_spend=[tx0_outpoint],
+ sequence=0,
+ num_outputs=100,
+ amount_per_output=1000,
+ )["hex"]
# This will raise an exception due to insufficient fee
assert_raises_rpc_error(-26, "insufficient fee", self.nodes[0].sendrawtransaction, tx1b_hex, 0)
@@ -299,37 +282,36 @@ class ReplaceByFeeTest(BitcoinTestFramework):
utxo1 = self.make_utxo(self.nodes[0], int(1.2 * COIN))
utxo2 = self.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 = tx1a.serialize().hex()
- tx1a_txid = self.nodes[0].sendrawtransaction(tx1a_hex, 0)
-
- tx1a_txid = int(tx1a_txid, 16)
+ tx1a_utxo = self.wallet.send_self_transfer(
+ from_node=self.nodes[0],
+ utxo_to_spend=utxo1,
+ sequence=0,
+ fee=Decimal("0.1"),
+ )["new_utxo"]
# Direct spend an output of the transaction we're replacing.
- tx2 = CTransaction()
- 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 = tx2.serialize().hex()
+ tx2_hex = self.wallet.create_self_transfer_multi(
+ utxos_to_spend=[utxo1, utxo2, tx1a_utxo],
+ sequence=0,
+ amount_per_output=int(COIN * tx1a_utxo["value"]),
+ )["hex"]
# This will raise an exception
assert_raises_rpc_error(-26, "bad-txns-spends-conflicting-tx", self.nodes[0].sendrawtransaction, tx2_hex, 0)
# Spend tx1a's output to test the indirect case.
- tx1b = CTransaction()
- tx1b.vin = [CTxIn(COutPoint(tx1a_txid, 0), nSequence=0)]
- tx1b.vout = [CTxOut(1 * COIN, DUMMY_P2WPKH_SCRIPT)]
- tx1b_hex = tx1b.serialize().hex()
- tx1b_txid = self.nodes[0].sendrawtransaction(tx1b_hex, 0)
- tx1b_txid = int(tx1b_txid, 16)
+ tx1b_utxo = self.wallet.send_self_transfer(
+ from_node=self.nodes[0],
+ utxo_to_spend=tx1a_utxo,
+ sequence=0,
+ fee=Decimal("0.1"),
+ )["new_utxo"]
- tx2 = CTransaction()
- tx2.vin = [CTxIn(utxo1, nSequence=0), CTxIn(utxo2, nSequence=0),
- CTxIn(COutPoint(tx1b_txid, 0))]
- tx2.vout = tx1a.vout
- tx2_hex = tx2.serialize().hex()
+ tx2_hex = self.wallet.create_self_transfer_multi(
+ utxos_to_spend=[utxo1, utxo2, tx1b_utxo],
+ sequence=0,
+ amount_per_output=int(COIN * tx1a_utxo["value"]),
+ )["hex"]
# This will raise an exception
assert_raises_rpc_error(-26, "bad-txns-spends-conflicting-tx", self.nodes[0].sendrawtransaction, tx2_hex, 0)
@@ -337,18 +319,20 @@ class ReplaceByFeeTest(BitcoinTestFramework):
def test_new_unconfirmed_inputs(self):
"""Replacements that add new unconfirmed inputs are rejected"""
confirmed_utxo = self.make_utxo(self.nodes[0], int(1.1 * COIN))
- unconfirmed_utxo = self.make_utxo(self.nodes[0], int(0.1 * COIN), False)
+ unconfirmed_utxo = self.make_utxo(self.nodes[0], int(0.1 * COIN), confirmed=False)
- tx1 = CTransaction()
- tx1.vin = [CTxIn(confirmed_utxo)]
- tx1.vout = [CTxOut(1 * COIN, DUMMY_P2WPKH_SCRIPT)]
- tx1_hex = tx1.serialize().hex()
- self.nodes[0].sendrawtransaction(tx1_hex, 0)
+ self.wallet.send_self_transfer(
+ from_node=self.nodes[0],
+ utxo_to_spend=confirmed_utxo,
+ sequence=0,
+ fee=Decimal("0.1"),
+ )
- tx2 = CTransaction()
- tx2.vin = [CTxIn(confirmed_utxo), CTxIn(unconfirmed_utxo)]
- tx2.vout = tx1.vout
- tx2_hex = tx2.serialize().hex()
+ tx2_hex = self.wallet.create_self_transfer_multi(
+ utxos_to_spend=[confirmed_utxo, unconfirmed_utxo],
+ sequence=0,
+ amount_per_output=1 * COIN,
+ )["hex"]
# This will raise an exception
assert_raises_rpc_error(-26, "replacement-adds-unconfirmed", self.nodes[0].sendrawtransaction, tx2_hex, 0)
@@ -364,45 +348,39 @@ class ReplaceByFeeTest(BitcoinTestFramework):
fee = int(0.0001 * COIN)
split_value = int((initial_nValue - fee) / (MAX_REPLACEMENT_LIMIT + 1))
- outputs = []
- 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 = splitting_tx.serialize().hex()
-
- txid = self.nodes[0].sendrawtransaction(splitting_tx_hex, 0)
- txid = int(txid, 16)
+ splitting_tx_utxos = self.wallet.send_self_transfer_multi(
+ from_node=self.nodes[0],
+ utxos_to_spend=[utxo],
+ sequence=0,
+ num_outputs=MAX_REPLACEMENT_LIMIT + 1,
+ amount_per_output=split_value,
+ )["new_utxos"]
# Now spend each of those outputs individually
- 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 = tx_i.serialize().hex()
- self.nodes[0].sendrawtransaction(tx_i_hex, 0)
+ for utxo in splitting_tx_utxos:
+ self.wallet.send_self_transfer(
+ from_node=self.nodes[0],
+ utxo_to_spend=utxo,
+ sequence=0,
+ fee=Decimal(fee) / COIN,
+ )
# 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)
- inputs = []
- 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 = self.wallet.create_self_transfer_multi(
+ utxos_to_spend=splitting_tx_utxos,
+ sequence=0,
+ amount_per_output=double_spend_value,
+ )["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)
# If we remove an input, it should pass
- double_tx = CTransaction()
- double_tx.vin = inputs[0:-1]
- double_tx.vout = [CTxOut(double_spend_value, CScript([b'a']))]
+ double_tx.vin.pop()
double_tx_hex = double_tx.serialize().hex()
self.nodes[0].sendrawtransaction(double_tx_hex, 0)
@@ -497,20 +475,22 @@ class ReplaceByFeeTest(BitcoinTestFramework):
tx0_outpoint = self.make_utxo(self.nodes[0], int(1.1 * COIN))
# Create a non-opting in transaction
- tx1a = CTransaction()
- tx1a.vin = [CTxIn(tx0_outpoint, nSequence=SEQUENCE_FINAL)]
- tx1a.vout = [CTxOut(1 * COIN, DUMMY_P2WPKH_SCRIPT)]
- tx1a_hex = tx1a.serialize().hex()
- tx1a_txid = self.nodes[0].sendrawtransaction(tx1a_hex, 0)
+ tx1a_utxo = self.wallet.send_self_transfer(
+ from_node=self.nodes[0],
+ utxo_to_spend=tx0_outpoint,
+ sequence=SEQUENCE_FINAL,
+ fee=Decimal("0.1"),
+ )["new_utxo"]
# This transaction isn't shown as replaceable
- assert_equal(self.nodes[0].getmempoolentry(tx1a_txid)['bip125-replaceable'], False)
+ assert_equal(self.nodes[0].getmempoolentry(tx1a_utxo["txid"])['bip125-replaceable'], False)
# Shouldn't be able to double-spend
- tx1b = CTransaction()
- tx1b.vin = [CTxIn(tx0_outpoint, nSequence=0)]
- tx1b.vout = [CTxOut(int(0.9 * COIN), DUMMY_P2WPKH_SCRIPT)]
- tx1b_hex = tx1b.serialize().hex()
+ tx1b_hex = self.wallet.create_self_transfer(
+ utxo_to_spend=tx0_outpoint,
+ sequence=0,
+ fee=Decimal("0.2"),
+ )["hex"]
# This will raise an exception
assert_raises_rpc_error(-26, "txn-mempool-conflict", self.nodes[0].sendrawtransaction, tx1b_hex, 0)
@@ -518,17 +498,19 @@ class ReplaceByFeeTest(BitcoinTestFramework):
tx1_outpoint = self.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 = tx2a.serialize().hex()
- tx2a_txid = self.nodes[0].sendrawtransaction(tx2a_hex, 0)
+ tx2a_utxo = self.wallet.send_self_transfer(
+ from_node=self.nodes[0],
+ utxo_to_spend=tx1_outpoint,
+ sequence=0xfffffffe,
+ fee=Decimal("0.1"),
+ )["new_utxo"]
# 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 = tx2b.serialize().hex()
+ tx2b_hex = self.wallet.create_self_transfer(
+ utxo_to_spend=tx1_outpoint,
+ sequence=0,
+ fee=Decimal("0.2"),
+ )["hex"]
# This will raise an exception
assert_raises_rpc_error(-26, "txn-mempool-conflict", self.nodes[0].sendrawtransaction, tx2b_hex, 0)
@@ -537,34 +519,31 @@ class ReplaceByFeeTest(BitcoinTestFramework):
# opt-in on one of the inputs
# Transaction should be replaceable on either input
- tx1a_txid = int(tx1a_txid, 16)
- tx2a_txid = int(tx2a_txid, 16)
-
- tx3a = CTransaction()
- tx3a.vin = [CTxIn(COutPoint(tx1a_txid, 0), nSequence=SEQUENCE_FINAL),
- 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 = tx3a.serialize().hex()
-
- tx3a_txid = self.nodes[0].sendrawtransaction(tx3a_hex, 0)
+ tx3a_txid = self.wallet.send_self_transfer_multi(
+ from_node=self.nodes[0],
+ utxos_to_spend=[tx1a_utxo, tx2a_utxo],
+ sequence=[SEQUENCE_FINAL, 0xfffffffd],
+ fee_per_output=int(0.1 * COIN),
+ )["txid"]
# This transaction is shown as replaceable
assert_equal(self.nodes[0].getmempoolentry(tx3a_txid)['bip125-replaceable'], True)
- tx3b = CTransaction()
- tx3b.vin = [CTxIn(COutPoint(tx1a_txid, 0), nSequence=0)]
- tx3b.vout = [CTxOut(int(0.5 * COIN), DUMMY_P2WPKH_SCRIPT)]
- 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 = tx3c.serialize().hex()
+ self.wallet.send_self_transfer(
+ from_node=self.nodes[0],
+ utxo_to_spend=tx1a_utxo,
+ sequence=0,
+ fee=Decimal("0.4"),
+ )
- self.nodes[0].sendrawtransaction(tx3b_hex, 0)
# If tx3b was accepted, tx3c won't look like a replacement,
# but make sure it is accepted anyway
- self.nodes[0].sendrawtransaction(tx3c_hex, 0)
+ self.wallet.send_self_transfer(
+ from_node=self.nodes[0],
+ utxo_to_spend=tx2a_utxo,
+ sequence=0,
+ fee=Decimal("0.4"),
+ )
def test_prioritised_transactions(self):
# Ensure that fee deltas used via prioritisetransaction are
@@ -573,17 +552,20 @@ class ReplaceByFeeTest(BitcoinTestFramework):
# 1. Check that feeperkb uses modified fees
tx0_outpoint = self.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 = tx1a.serialize().hex()
- tx1a_txid = self.nodes[0].sendrawtransaction(tx1a_hex, 0)
+ tx1a_txid = self.wallet.send_self_transfer(
+ from_node=self.nodes[0],
+ utxo_to_spend=tx0_outpoint,
+ sequence=0,
+ fee=Decimal("0.1"),
+ )["txid"]
# 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 = tx1b.serialize().hex()
+ tx1b_hex = self.wallet.create_self_transfer_multi(
+ utxos_to_spend=[tx0_outpoint],
+ sequence=0,
+ num_outputs=100,
+ amount_per_output=int(0.00001 * COIN),
+ )["hex"]
# Verify tx1b cannot replace tx1a.
assert_raises_rpc_error(-26, "insufficient fee", self.nodes[0].sendrawtransaction, tx1b_hex, 0)
@@ -599,27 +581,29 @@ class ReplaceByFeeTest(BitcoinTestFramework):
# 2. Check that absolute fee checks use modified fee.
tx1_outpoint = self.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 = tx2a.serialize().hex()
- self.nodes[0].sendrawtransaction(tx2a_hex, 0)
+ # tx2a
+ self.wallet.send_self_transfer(
+ from_node=self.nodes[0],
+ utxo_to_spend=tx1_outpoint,
+ sequence=0,
+ fee=Decimal("0.1"),
+ )
# Lower fee, but we'll prioritise it
- tx2b = CTransaction()
- tx2b.vin = [CTxIn(tx1_outpoint, nSequence=0)]
- tx2b.vout = [CTxOut(int(1.01 * COIN), DUMMY_P2WPKH_SCRIPT)]
- tx2b.rehash()
- tx2b_hex = tx2b.serialize().hex()
+ tx2b = self.wallet.create_self_transfer(
+ utxo_to_spend=tx1_outpoint,
+ sequence=0,
+ fee=Decimal("0.09"),
+ )
# Verify tx2b cannot replace tx2a.
- assert_raises_rpc_error(-26, "insufficient fee", self.nodes[0].sendrawtransaction, tx2b_hex, 0)
+ 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["txid"], fee_delta=int(0.1 * COIN))
# tx2b should now be accepted
- tx2b_txid = self.nodes[0].sendrawtransaction(tx2b_hex, 0)
+ tx2b_txid = self.nodes[0].sendrawtransaction(tx2b["hex"], 0)
assert tx2b_txid in self.nodes[0].getrawmempool()
diff --git a/test/functional/mempool_limit.py b/test/functional/mempool_limit.py
index e92f73304b..7080662b49 100755
--- a/test/functional/mempool_limit.py
+++ b/test/functional/mempool_limit.py
@@ -23,7 +23,7 @@ class MempoolLimitTest(BitcoinTestFramework):
self.setup_clean_chain = True
self.num_nodes = 1
self.extra_args = [[
- "-acceptnonstdtxn=1",
+ "-datacarriersize=100000",
"-maxmempool=5",
"-spendzeroconfchange=0",
]]
diff --git a/test/functional/mempool_updatefromblock.py b/test/functional/mempool_updatefromblock.py
index 51de582ce0..f97c2223a6 100755
--- a/test/functional/mempool_updatefromblock.py
+++ b/test/functional/mempool_updatefromblock.py
@@ -12,6 +12,9 @@ import time
from decimal import Decimal
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_equal
+from test_framework.address import key_to_p2pkh
+from test_framework.wallet_util import bytes_to_wif
+from test_framework.key import ECKey
class MempoolUpdateFromBlockTest(BitcoinTestFramework):
@@ -19,8 +22,13 @@ class MempoolUpdateFromBlockTest(BitcoinTestFramework):
self.num_nodes = 1
self.extra_args = [['-limitdescendantsize=1000', '-limitancestorsize=1000', '-limitancestorcount=100']]
- def skip_test_if_missing_module(self):
- self.skip_if_no_wallet()
+ def get_new_address(self):
+ key = ECKey()
+ key.generate()
+ pubkey = key.get_pubkey().get_bytes()
+ address = key_to_p2pkh(pubkey)
+ self.priv_keys.append(bytes_to_wif(key.get_bytes()))
+ return address
def transaction_graph_test(self, size, n_tx_to_mine=None, start_input_txid='', end_address='', fee=Decimal(0.00100000)):
"""Create an acyclic tournament (a type of directed graph) of transactions and use it for testing.
@@ -38,11 +46,12 @@ class MempoolUpdateFromBlockTest(BitcoinTestFramework):
More details: https://en.wikipedia.org/wiki/Tournament_(graph_theory)
"""
+ self.priv_keys = [self.nodes[0].get_deterministic_priv_key().key]
if not start_input_txid:
start_input_txid = self.nodes[0].getblock(self.nodes[0].getblockhash(1))['tx'][0]
if not end_address:
- end_address = self.nodes[0].getnewaddress()
+ end_address = self.get_new_address()
first_block_hash = ''
tx_id = []
@@ -74,7 +83,7 @@ class MempoolUpdateFromBlockTest(BitcoinTestFramework):
output_value = ((inputs_value - fee) / Decimal(n_outputs)).quantize(Decimal('0.00000001'))
outputs = {}
for _ in range(n_outputs):
- outputs[self.nodes[0].getnewaddress()] = output_value
+ outputs[self.get_new_address()] = output_value
else:
output_value = (inputs_value - fee).quantize(Decimal('0.00000001'))
outputs = {end_address: output_value}
@@ -84,7 +93,7 @@ class MempoolUpdateFromBlockTest(BitcoinTestFramework):
# Create a new transaction.
unsigned_raw_tx = self.nodes[0].createrawtransaction(inputs, outputs)
- signed_raw_tx = self.nodes[0].signrawtransactionwithwallet(unsigned_raw_tx)
+ signed_raw_tx = self.nodes[0].signrawtransactionwithkey(unsigned_raw_tx, self.priv_keys)
tx_id.append(self.nodes[0].sendrawtransaction(signed_raw_tx['hex']))
tx_size.append(self.nodes[0].getmempoolentry(tx_id[-1])['vsize'])
diff --git a/test/functional/mining_prioritisetransaction.py b/test/functional/mining_prioritisetransaction.py
index fb3974c1d5..64e66ac30a 100755
--- a/test/functional/mining_prioritisetransaction.py
+++ b/test/functional/mining_prioritisetransaction.py
@@ -26,7 +26,7 @@ class PrioritiseTransactionTest(BitcoinTestFramework):
self.num_nodes = 1
self.extra_args = [[
"-printpriority=1",
- "-acceptnonstdtxn=1",
+ "-datacarriersize=100000",
]] * self.num_nodes
self.supports_cli = False
diff --git a/test/functional/p2p_invalid_tx.py b/test/functional/p2p_invalid_tx.py
index 139f4d64e7..28efd5a81e 100755
--- a/test/functional/p2p_invalid_tx.py
+++ b/test/functional/p2p_invalid_tx.py
@@ -60,7 +60,6 @@ class InvalidTxRequestTest(BitcoinTestFramework):
block.solve()
# Save the coinbase for later
block1 = block
- tip = block.sha256
node.p2ps[0].send_blocks_and_test([block], node, success=True)
self.log.info("Mature the block.")
@@ -93,24 +92,24 @@ class InvalidTxRequestTest(BitcoinTestFramework):
SCRIPT_PUB_KEY_OP_TRUE = b'\x51\x75' * 15 + b'\x51'
tx_withhold = CTransaction()
tx_withhold.vin.append(CTxIn(outpoint=COutPoint(block1.vtx[0].sha256, 0)))
- tx_withhold.vout.append(CTxOut(nValue=50 * COIN - 12000, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE))
+ tx_withhold.vout = [CTxOut(nValue=25 * COIN - 12000, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE)] * 2
tx_withhold.calc_sha256()
# Our first orphan tx with some outputs to create further orphan txs
tx_orphan_1 = CTransaction()
tx_orphan_1.vin.append(CTxIn(outpoint=COutPoint(tx_withhold.sha256, 0)))
- tx_orphan_1.vout = [CTxOut(nValue=10 * COIN, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE)] * 3
+ tx_orphan_1.vout = [CTxOut(nValue=8 * COIN, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE)] * 3
tx_orphan_1.calc_sha256()
# A valid transaction with low fee
tx_orphan_2_no_fee = CTransaction()
tx_orphan_2_no_fee.vin.append(CTxIn(outpoint=COutPoint(tx_orphan_1.sha256, 0)))
- tx_orphan_2_no_fee.vout.append(CTxOut(nValue=10 * COIN, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE))
+ tx_orphan_2_no_fee.vout.append(CTxOut(nValue=8 * COIN, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE))
# A valid transaction with sufficient fee
tx_orphan_2_valid = CTransaction()
tx_orphan_2_valid.vin.append(CTxIn(outpoint=COutPoint(tx_orphan_1.sha256, 1)))
- tx_orphan_2_valid.vout.append(CTxOut(nValue=10 * COIN - 12000, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE))
+ tx_orphan_2_valid.vout.append(CTxOut(nValue=8 * COIN - 12000, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE))
tx_orphan_2_valid.calc_sha256()
# An invalid transaction with negative fee
@@ -157,6 +156,7 @@ class InvalidTxRequestTest(BitcoinTestFramework):
with node.assert_debug_log(['orphanage overflow, removed 1 tx']):
node.p2ps[0].send_txs_and_test(orphan_tx_pool, node, success=False)
+ self.log.info('Test orphan with rejected parents')
rejected_parent = CTransaction()
rejected_parent.vin.append(CTxIn(outpoint=COutPoint(tx_orphan_2_invalid.sha256, 0)))
rejected_parent.vout.append(CTxOut(nValue=11 * COIN, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE))
@@ -164,6 +164,64 @@ class InvalidTxRequestTest(BitcoinTestFramework):
with node.assert_debug_log(['not keeping orphan with rejected parents {}'.format(rejected_parent.hash)]):
node.p2ps[0].send_txs_and_test([rejected_parent], node, success=False)
+ self.log.info('Test that a peer disconnection causes erase its transactions from the orphan pool')
+ with node.assert_debug_log(['Erased 100 orphan tx from peer=25']):
+ self.reconnect_p2p(num_connections=1)
+
+ self.log.info('Test that a transaction in the orphan pool is included in a new tip block causes erase this transaction from the orphan pool')
+ tx_withhold_until_block_A = CTransaction()
+ tx_withhold_until_block_A.vin.append(CTxIn(outpoint=COutPoint(tx_withhold.sha256, 1)))
+ tx_withhold_until_block_A.vout = [CTxOut(nValue=12 * COIN, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE)] * 2
+ tx_withhold_until_block_A.calc_sha256()
+
+ tx_orphan_include_by_block_A = CTransaction()
+ tx_orphan_include_by_block_A.vin.append(CTxIn(outpoint=COutPoint(tx_withhold_until_block_A.sha256, 0)))
+ tx_orphan_include_by_block_A.vout.append(CTxOut(nValue=12 * COIN - 12000, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE))
+ tx_orphan_include_by_block_A.calc_sha256()
+
+ self.log.info('Send the orphan ... ')
+ node.p2ps[0].send_txs_and_test([tx_orphan_include_by_block_A], node, success=False)
+
+ tip = int(node.getbestblockhash(), 16)
+ height = node.getblockcount() + 1
+ block_A = create_block(tip, create_coinbase(height))
+ block_A.vtx.extend([tx_withhold, tx_withhold_until_block_A, tx_orphan_include_by_block_A])
+ block_A.hashMerkleRoot = block_A.calc_merkle_root()
+ block_A.solve()
+
+ self.log.info('Send the block that includes the previous orphan ... ')
+ with node.assert_debug_log(["Erased 1 orphan tx included or conflicted by block"]):
+ node.p2ps[0].send_blocks_and_test([block_A], node, success=True)
+
+ self.log.info('Test that a transaction in the orphan pool conflicts with a new tip block causes erase this transaction from the orphan pool')
+ tx_withhold_until_block_B = CTransaction()
+ tx_withhold_until_block_B.vin.append(CTxIn(outpoint=COutPoint(tx_withhold_until_block_A.sha256, 1)))
+ tx_withhold_until_block_B.vout.append(CTxOut(nValue=11 * COIN, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE))
+ tx_withhold_until_block_B.calc_sha256()
+
+ tx_orphan_include_by_block_B = CTransaction()
+ tx_orphan_include_by_block_B.vin.append(CTxIn(outpoint=COutPoint(tx_withhold_until_block_B.sha256, 0)))
+ tx_orphan_include_by_block_B.vout.append(CTxOut(nValue=10 * COIN, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE))
+ tx_orphan_include_by_block_B.calc_sha256()
+
+ tx_orphan_conflict_by_block_B = CTransaction()
+ tx_orphan_conflict_by_block_B.vin.append(CTxIn(outpoint=COutPoint(tx_withhold_until_block_B.sha256, 0)))
+ tx_orphan_conflict_by_block_B.vout.append(CTxOut(nValue=9 * COIN, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE))
+ tx_orphan_conflict_by_block_B.calc_sha256()
+ self.log.info('Send the orphan ... ')
+ node.p2ps[0].send_txs_and_test([tx_orphan_conflict_by_block_B], node, success=False)
+
+ tip = int(node.getbestblockhash(), 16)
+ height = node.getblockcount() + 1
+ block_B = create_block(tip, create_coinbase(height))
+ block_B.vtx.extend([tx_withhold_until_block_B, tx_orphan_include_by_block_B])
+ block_B.hashMerkleRoot = block_B.calc_merkle_root()
+ block_B.solve()
+
+ self.log.info('Send the block that includes a transaction which conflicts with the previous orphan ... ')
+ with node.assert_debug_log(["Erased 1 orphan tx included or conflicted by block"]):
+ node.p2ps[0].send_blocks_and_test([block_B], node, success=True)
+
if __name__ == '__main__':
InvalidTxRequestTest().main()
diff --git a/test/functional/p2p_segwit.py b/test/functional/p2p_segwit.py
index 952f1e5cc5..eedb7e6fa1 100755
--- a/test/functional/p2p_segwit.py
+++ b/test/functional/p2p_segwit.py
@@ -371,6 +371,10 @@ class SegWitTest(BitcoinTestFramework):
block1 = self.build_next_block()
block1.solve()
+ # Send an empty headers message, to clear out any prior getheaders
+ # messages that our peer may be waiting for us on.
+ self.test_node.send_message(msg_headers())
+
self.test_node.announce_block_and_wait_for_getdata(block1, use_header=False)
assert self.test_node.last_message["getdata"].inv[0].type == blocktype
test_witness_block(self.nodes[0], self.test_node, block1, True)
diff --git a/test/functional/p2p_timeouts.py b/test/functional/p2p_timeouts.py
index f0abbc7d8b..15a879ae3c 100755
--- a/test/functional/p2p_timeouts.py
+++ b/test/functional/p2p_timeouts.py
@@ -94,6 +94,11 @@ class TimeoutsTest(BitcoinTestFramework):
no_version_node.wait_for_disconnect(timeout=1)
no_send_node.wait_for_disconnect(timeout=1)
+ self.stop_nodes(0)
+ self.nodes[0].assert_start_raises_init_error(
+ expected_msg='Error: peertimeout must be a positive integer.',
+ extra_args=['-peertimeout=0'],
+ )
if __name__ == '__main__':
TimeoutsTest().main()
diff --git a/test/functional/rpc_packages.py b/test/functional/rpc_packages.py
index 63533affd0..f687849287 100755
--- a/test/functional/rpc_packages.py
+++ b/test/functional/rpc_packages.py
@@ -15,16 +15,20 @@ from test_framework.messages import (
CTxInWitness,
tx_from_hex,
)
+from test_framework.p2p import P2PTxInvStore
from test_framework.script import (
CScript,
OP_TRUE,
)
from test_framework.util import (
assert_equal,
+ assert_fee_amount,
+ assert_raises_rpc_error,
)
from test_framework.wallet import (
create_child_with_parents,
create_raw_chain,
+ DEFAULT_FEE,
make_chain,
)
@@ -51,7 +55,7 @@ class RPCPackagesTest(BitcoinTestFramework):
self.address = node.get_deterministic_priv_key().address
self.coins = []
# The last 100 coinbase transactions are premature
- for b in self.generatetoaddress(node, 200, self.address)[:100]:
+ for b in self.generatetoaddress(node, 220, self.address)[:-100]:
coinbase = node.getblock(blockhash=b, verbosity=2)["tx"][0]
self.coins.append({
"txid": coinbase["txid"],
@@ -82,7 +86,7 @@ class RPCPackagesTest(BitcoinTestFramework):
self.test_multiple_parents()
self.test_conflicting()
self.test_rbf()
-
+ self.test_submitpackage()
def test_independent(self):
self.log.info("Test multiple independent transactions in a package")
@@ -132,8 +136,7 @@ class RPCPackagesTest(BitcoinTestFramework):
def test_chain(self):
node = self.nodes[0]
- first_coin = self.coins.pop()
- (chain_hex, chain_txns) = create_raw_chain(node, first_coin, self.address, self.privkeys)
+ (chain_hex, chain_txns) = create_raw_chain(node, self.coins.pop(), self.address, self.privkeys)
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]])
@@ -306,5 +309,127 @@ class RPCPackagesTest(BitcoinTestFramework):
}]
self.assert_testres_equal(self.independent_txns_hex + [signed_replacement_tx["hex"]], testres_rbf_package)
+ def assert_equal_package_results(self, node, testmempoolaccept_result, submitpackage_result):
+ """Assert that a successful submitpackage result is consistent with testmempoolaccept
+ results and getmempoolentry info. Note that the result structs are different and, due to
+ policy differences between testmempoolaccept and submitpackage (i.e. package feerate),
+ some information may be different.
+ """
+ for testres_tx in testmempoolaccept_result:
+ # Grab this result from the submitpackage_result
+ submitres_tx = submitpackage_result["tx-results"][testres_tx["wtxid"]]
+ assert_equal(submitres_tx["txid"], testres_tx["txid"])
+ # No "allowed" if the tx was already in the mempool
+ if "allowed" in testres_tx and testres_tx["allowed"]:
+ assert_equal(submitres_tx["vsize"], testres_tx["vsize"])
+ assert_equal(submitres_tx["fees"]["base"], testres_tx["fees"]["base"])
+ entry_info = node.getmempoolentry(submitres_tx["txid"])
+ assert_equal(submitres_tx["vsize"], entry_info["vsize"])
+ assert_equal(submitres_tx["fees"]["base"], entry_info["fees"]["base"])
+
+ def test_submit_child_with_parents(self, num_parents, partial_submit):
+ node = self.nodes[0]
+ peer = node.add_p2p_connection(P2PTxInvStore())
+ # Test a package with num_parents parents and 1 child transaction.
+ package_hex = []
+ package_txns = []
+ values = []
+ scripts = []
+ for _ in range(num_parents):
+ parent_coin = self.coins.pop()
+ value = parent_coin["amount"]
+ (tx, txhex, value, spk) = make_chain(node, self.address, self.privkeys, parent_coin["txid"], value)
+ package_hex.append(txhex)
+ package_txns.append(tx)
+ values.append(value)
+ scripts.append(spk)
+ if partial_submit and random.choice([True, False]):
+ node.sendrawtransaction(txhex)
+ child_hex = create_child_with_parents(node, self.address, self.privkeys, package_txns, values, scripts)
+ package_hex.append(child_hex)
+ package_txns.append(tx_from_hex(child_hex))
+
+ testmempoolaccept_result = node.testmempoolaccept(rawtxs=package_hex)
+ submitpackage_result = node.submitpackage(package=package_hex)
+
+ # Check that each result is present, with the correct size and fees
+ for i in range(num_parents + 1):
+ tx = package_txns[i]
+ wtxid = tx.getwtxid()
+ assert wtxid in submitpackage_result["tx-results"]
+ tx_result = submitpackage_result["tx-results"][wtxid]
+ assert_equal(tx_result, {
+ "txid": tx.rehash(),
+ "vsize": tx.get_vsize(),
+ "fees": {
+ "base": DEFAULT_FEE,
+ }
+ })
+
+ # submitpackage result should be consistent with testmempoolaccept and getmempoolentry
+ self.assert_equal_package_results(node, testmempoolaccept_result, submitpackage_result)
+
+ # Package feerate is calculated for the remaining transactions after deduplication and
+ # individual submission. If only 0 or 1 transaction is left, e.g. because all transactions
+ # had high-feerates or were already in the mempool, no package feerate is provided.
+ # In this case, since all of the parents have high fees, each is accepted individually.
+ assert "package-feerate" not in submitpackage_result
+
+ # The node should announce each transaction. No guarantees for propagation.
+ peer.wait_for_broadcast([tx.getwtxid() for tx in package_txns])
+ self.generate(node, 1)
+
+
+ def test_submit_cpfp(self):
+ node = self.nodes[0]
+ peer = node.add_p2p_connection(P2PTxInvStore())
+
+ # 2 parent 1 child CPFP. First parent pays high fees, second parent pays 0 fees and is
+ # fee-bumped by the child.
+ coin_rich = self.coins.pop()
+ coin_poor = self.coins.pop()
+ tx_rich, hex_rich, value_rich, spk_rich = make_chain(node, self.address, self.privkeys, coin_rich["txid"], coin_rich["amount"])
+ tx_poor, hex_poor, value_poor, spk_poor = make_chain(node, self.address, self.privkeys, coin_poor["txid"], coin_poor["amount"], fee=0)
+ package_txns = [tx_rich, tx_poor]
+ hex_child = create_child_with_parents(node, self.address, self.privkeys, package_txns, [value_rich, value_poor], [spk_rich, spk_poor])
+ tx_child = tx_from_hex(hex_child)
+ package_txns.append(tx_child)
+
+ submitpackage_result = node.submitpackage([hex_rich, hex_poor, hex_child])
+
+ rich_parent_result = submitpackage_result["tx-results"][tx_rich.getwtxid()]
+ poor_parent_result = submitpackage_result["tx-results"][tx_poor.getwtxid()]
+ child_result = submitpackage_result["tx-results"][tx_child.getwtxid()]
+ assert_equal(rich_parent_result["fees"]["base"], DEFAULT_FEE)
+ assert_equal(poor_parent_result["fees"]["base"], 0)
+ assert_equal(child_result["fees"]["base"], DEFAULT_FEE)
+ # Package feerate is calculated for the remaining transactions after deduplication and
+ # individual submission. Since this package had a 0-fee parent, package feerate must have
+ # been used and returned.
+ assert "package-feerate" in submitpackage_result
+ assert_fee_amount(DEFAULT_FEE, rich_parent_result["vsize"] + child_result["vsize"], submitpackage_result["package-feerate"])
+
+ # The node will broadcast each transaction, still abiding by its peer's fee filter
+ peer.wait_for_broadcast([tx.getwtxid() for tx in package_txns])
+ self.generate(node, 1)
+
+
+ def test_submitpackage(self):
+ node = self.nodes[0]
+
+ self.log.info("Submitpackage valid packages with 1 child and some number of parents")
+ for num_parents in [1, 2, 24]:
+ self.test_submit_child_with_parents(num_parents, False)
+ self.test_submit_child_with_parents(num_parents, True)
+
+ self.log.info("Submitpackage valid packages with CPFP")
+ self.test_submit_cpfp()
+
+ self.log.info("Submitpackage only allows packages of 1 child with its parents")
+ # Chain of 3 transactions has too many generations
+ chain_hex, _ = create_raw_chain(node, self.coins.pop(), self.address, self.privkeys, 3)
+ assert_raises_rpc_error(-25, "not-child-with-parents", node.submitpackage, chain_hex)
+
+
if __name__ == "__main__":
RPCPackagesTest().main()
diff --git a/test/functional/test_framework/blocktools.py b/test/functional/test_framework/blocktools.py
index 40fcbf7761..574ea10356 100644
--- a/test/functional/test_framework/blocktools.py
+++ b/test/functional/test_framework/blocktools.py
@@ -162,30 +162,6 @@ def create_tx_with_script(prevtx, n, script_sig=b"", *, amount, script_pub_key=C
tx.calc_sha256()
return tx
-def create_transaction(node, txid, to_address, *, amount):
- """ Return signed transaction spending the first output of the
- input txid. Note that the node must have a wallet that can
- sign for the output that is being spent.
- """
- raw_tx = create_raw_transaction(node, txid, to_address, amount=amount)
- tx = tx_from_hex(raw_tx)
- return tx
-
-def create_raw_transaction(node, txid, to_address, *, amount):
- """ Return raw signed transaction spending the first output of the
- input txid. Note that the node must have a wallet that can sign
- for the output that is being spent.
- """
- psbt = node.createpsbt(inputs=[{"txid": txid, "vout": 0}], outputs={to_address: amount})
- for _ in range(2):
- for w in node.listwallets():
- wrpc = node.get_wallet_rpc(w)
- signed_psbt = wrpc.walletprocesspsbt(psbt)
- psbt = signed_psbt['psbt']
- final_psbt = node.finalizepsbt(psbt)
- assert_equal(final_psbt["complete"], True)
- return final_psbt['hex']
-
def get_legacy_sigopcount_block(block, accurate=True):
count = 0
for tx in block.vtx:
diff --git a/test/functional/test_framework/util.py b/test/functional/test_framework/util.py
index 6a588275ea..1ee23f7574 100644
--- a/test/functional/test_framework/util.py
+++ b/test/functional/test_framework/util.py
@@ -499,22 +499,13 @@ def chain_transaction(node, parent_txids, vouts, value, fee, num_outputs):
# Create large OP_RETURN txouts that can be appended to a transaction
-# to make it large (helper for constructing large transactions).
+# to make it large (helper for constructing large transactions). The
+# total serialized size of the txouts is about 66k vbytes.
def gen_return_txouts():
- # Some pre-processing to create a bunch of OP_RETURN txouts to insert into transactions we create
- # So we have big transactions (and therefore can't fit very many into each block)
- # create one script_pubkey
- script_pubkey = "6a4d0200" # OP_RETURN OP_PUSH2 512 bytes
- for _ in range(512):
- script_pubkey = script_pubkey + "01"
- # concatenate 128 txouts of above script_pubkey which we'll insert before the txout for change
- txouts = []
from .messages import CTxOut
- txout = CTxOut()
- txout.nValue = 0
- txout.scriptPubKey = bytes.fromhex(script_pubkey)
- for _ in range(128):
- txouts.append(txout)
+ from .script import CScript, OP_RETURN
+ txouts = [CTxOut(nValue=0, scriptPubKey=CScript([OP_RETURN, b'\x01'*67437]))]
+ assert_equal(sum([len(txout.serialize()) for txout in txouts]), 67456)
return txouts
diff --git a/test/functional/test_framework/wallet.py b/test/functional/test_framework/wallet.py
index 68d5dfa880..2164627781 100644
--- a/test/functional/test_framework/wallet.py
+++ b/test/functional/test_framework/wallet.py
@@ -197,7 +197,7 @@ class MiniWallet:
return utxos
def send_self_transfer(self, *, from_node, **kwargs):
- """Create and send a tx with the specified fee_rate. Fee may be exact or at most one satoshi higher than needed."""
+ """Call create_self_transfer and send the transaction."""
tx = self.create_self_transfer(**kwargs)
self.sendrawtransaction(from_node=from_node, tx_hex=tx['hex'])
return tx
@@ -232,21 +232,27 @@ class MiniWallet:
*,
utxos_to_spend: Optional[List[dict]] = None,
num_outputs=1,
+ amount_per_output=0,
sequence=0,
fee_per_output=1000,
):
"""
Create and return a transaction that spends the given UTXOs and creates a
- certain number of outputs with equal amounts.
+ certain number of outputs with equal amounts. The output amounts can be
+ set by amount_per_output or automatically calculated with a fee_per_output.
"""
utxos_to_spend = utxos_to_spend or [self.get_utxo()]
+ sequence = [sequence] * len(utxos_to_spend) if type(sequence) is int else sequence
+ assert_equal(len(utxos_to_spend), len(sequence))
# create simple tx template (1 input, 1 output)
tx = self.create_self_transfer(
fee_rate=0,
- utxo_to_spend=utxos_to_spend[0], sequence=sequence)["tx"]
+ utxo_to_spend=utxos_to_spend[0])["tx"]
# duplicate inputs, witnesses and outputs
tx.vin = [deepcopy(tx.vin[0]) for _ in range(len(utxos_to_spend))]
+ for txin, seq in zip(tx.vin, sequence):
+ txin.nSequence = seq
tx.wit.vtxinwit = [deepcopy(tx.wit.vtxinwit[0]) for _ in range(len(utxos_to_spend))]
tx.vout = [deepcopy(tx.vout[0]) for _ in range(num_outputs)]
@@ -258,7 +264,7 @@ class MiniWallet:
inputs_value_total = sum([int(COIN * utxo['value']) for utxo in utxos_to_spend])
outputs_value_total = inputs_value_total - fee_per_output * num_outputs
for o in tx.vout:
- o.nValue = outputs_value_total // num_outputs
+ o.nValue = amount_per_output or (outputs_value_total // num_outputs)
txid = tx.rehash()
return {
"new_utxos": [self._create_utxo(
@@ -272,21 +278,23 @@ class MiniWallet:
"tx": tx,
}
- def create_self_transfer(self, *, fee_rate=Decimal("0.003"), utxo_to_spend=None, 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."""
+ def create_self_transfer(self, *, fee_rate=Decimal("0.003"), fee=Decimal("0"), utxo_to_spend=None, locktime=0, sequence=0):
+ """Create and return a tx with the specified fee. If fee is 0, use fee_rate, where the resulting fee may be exact or at most one satoshi higher than needed."""
utxo_to_spend = utxo_to_spend or self.get_utxo()
+ assert fee_rate >= 0
+ assert fee >= 0
if self._mode in (MiniWalletMode.RAW_OP_TRUE, MiniWalletMode.ADDRESS_OP_TRUE):
vsize = Decimal(104) # anyone-can-spend
elif self._mode == MiniWalletMode.RAW_P2PK:
vsize = Decimal(168) # P2PK (73 bytes scriptSig + 35 bytes scriptPubKey + 60 bytes other)
else:
assert False
- send_value = utxo_to_spend["value"] - (fee_rate * vsize / 1000)
+ send_value = utxo_to_spend["value"] - (fee or (fee_rate * vsize / 1000))
assert send_value > 0
tx = CTransaction()
tx.vin = [CTxIn(COutPoint(int(utxo_to_spend['txid'], 16), utxo_to_spend['vout']), nSequence=sequence)]
- tx.vout = [CTxOut(int(COIN * send_value), self._scriptPubKey)]
+ tx.vout = [CTxOut(int(COIN * send_value), bytearray(self._scriptPubKey))]
tx.nLockTime = locktime
if self._mode == MiniWalletMode.RAW_P2PK:
self.sign_tx(tx)
diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py
index 6a44f9d21d..08229de57a 100755
--- a/test/functional/test_runner.py
+++ b/test/functional/test_runner.py
@@ -130,6 +130,7 @@ BASE_SCRIPTS = [
'wallet_address_types.py --descriptors',
'feature_bip68_sequence.py',
'p2p_feefilter.py',
+ 'rpc_packages.py',
'feature_reindex.py',
'feature_abortnode.py',
# vv Tests less than 30s vv
@@ -231,7 +232,6 @@ BASE_SCRIPTS = [
'mempool_packages.py',
'mempool_package_onemore.py',
'rpc_createmultisig.py',
- 'rpc_packages.py',
'mempool_package_limits.py',
'feature_versionbits_warning.py',
'rpc_preciousblock.py',
@@ -244,8 +244,7 @@ BASE_SCRIPTS = [
'rpc_generate.py',
'wallet_balance.py --legacy-wallet',
'wallet_balance.py --descriptors',
- 'feature_nulldummy.py --legacy-wallet',
- 'feature_nulldummy.py --descriptors',
+ 'feature_nulldummy.py',
'mempool_accept.py',
'mempool_expiry.py',
'wallet_import_rescan.py --legacy-wallet',
diff --git a/test/functional/wallet_basic.py b/test/functional/wallet_basic.py
index a6c93ba5f9..f66fab19ac 100755
--- a/test/functional/wallet_basic.py
+++ b/test/functional/wallet_basic.py
@@ -25,7 +25,7 @@ class WalletTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 4
self.extra_args = [[
- "-acceptnonstdtxn=1", "-walletrejectlongchains=0"
+ "-dustrelayfee=0", "-walletrejectlongchains=0"
]] * self.num_nodes
self.setup_clean_chain = True
self.supports_cli = False