Merge bitcoin/bitcoin#22210: test: Use MiniWallet in test_no_inherited_signaling RBF test

fa7d71f270 test: Run pep-8 on touched test (MarcoFalke)
fab7e99c2a test: Use MiniWallet in test_no_inherited_signaling RBF test (MarcoFalke)
fab871f649 test: Remove unused generate() from test (MarcoFalke)
faff3f35b7 test: Add txin.sequence option to MiniWallet (MarcoFalke)

Pull request description:

  This comes with nice benefits:
  * Less code and complexity
  * Test can be run without wallet compiled in

  Also add some additional checks for `getmempoolentry` (#22209) and other cleanups 🎨

ACKs for top commit:
  mjdietzx:
    Tested ACK fa7d71f270 thanks for the explanations, nicely done
  theStack:
    ACK fa7d71f270 🍷

Tree-SHA512: 0e9b8fe985779d8d7034d256deed627125bb374b6ae2972c461b3a220739a51061c6147ad69339bee16282f82716c7f3f8a7a89c693ceb1e47ea50709272332a
This commit is contained in:
MarcoFalke committed 2021-06-19 08:47:52 +02:00
commit e172ea8804
3 files changed
+46 -52

No files matched your search

+40 -46
View File
@@ -7,17 +7,20 @@
from decimal import Decimal from decimal import Decimal
from test_framework.blocktools import COINBASE_MATURITY from test_framework.blocktools import COINBASE_MATURITY
from test_framework.messages import COIN, COutPoint, CTransaction, CTxIn, CTxOut from test_framework.messages import COIN, COutPoint, CTransaction, CTxIn, CTxOut, BIP125_SEQUENCE_NUMBER
from test_framework.script import CScript, OP_DROP from test_framework.script import CScript, OP_DROP
from test_framework.test_framework import BitcoinTestFramework 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, satoshi_round
from test_framework.script_util import DUMMY_P2WPKH_SCRIPT, DUMMY_2_P2WPKH_SCRIPT from test_framework.script_util import DUMMY_P2WPKH_SCRIPT, DUMMY_2_P2WPKH_SCRIPT
from test_framework.wallet import MiniWallet
MAX_REPLACEMENT_LIMIT = 100 MAX_REPLACEMENT_LIMIT = 100
def txToHex(tx): def txToHex(tx):
return tx.serialize().hex() return tx.serialize().hex()
def make_utxo(node, amount, confirmed=True, scriptPubKey=DUMMY_P2WPKH_SCRIPT): def make_utxo(node, amount, confirmed=True, scriptPubKey=DUMMY_P2WPKH_SCRIPT):
"""Create a txout with a given amount and scriptPubKey """Create a txout with a given amount and scriptPubKey
@@ -78,9 +81,6 @@ class ReplaceByFeeTest(BitcoinTestFramework):
self.skip_if_no_wallet() self.skip_if_no_wallet()
def run_test(self): 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 # Ensure nodes are synced
@@ -568,68 +568,62 @@ class ReplaceByFeeTest(BitcoinTestFramework):
assert_equal(json1["vin"][0]["sequence"], 4294967294) assert_equal(json1["vin"][0]["sequence"], 4294967294)
def test_no_inherited_signaling(self): def test_no_inherited_signaling(self):
# Send tx from which to conflict outputs later wallet = MiniWallet(self.nodes[0])
base_txid = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), Decimal("10")) wallet.scan_blocks(start=76, num=1)
self.nodes[0].generate(1) confirmed_utxo = wallet.get_utxo()
self.sync_blocks()
# Create an explicitly opt-in parent transaction # Create an explicitly opt-in parent transaction
optin_parent_tx = self.nodes[0].createrawtransaction([{ optin_parent_tx = wallet.send_self_transfer(
'txid': base_txid, from_node=self.nodes[0],
'vout': 0, utxo_to_spend=confirmed_utxo,
"sequence": 0xfffffffd, sequence=BIP125_SEQUENCE_NUMBER,
}], {self.nodes[0].getnewaddress(): Decimal("9.99998")}) fee_rate=Decimal('0.01'),
)
assert_equal(True, self.nodes[0].getmempoolentry(optin_parent_tx['txid'])['bip125-replaceable'])
optin_parent_tx = self.nodes[0].signrawtransactionwithwallet(optin_parent_tx) replacement_parent_tx = wallet.create_self_transfer(
from_node=self.nodes[0],
# Broadcast parent tx utxo_to_spend=confirmed_utxo,
optin_parent_txid = self.nodes[0].sendrawtransaction(hexstring=optin_parent_tx["hex"], maxfeerate=0) sequence=BIP125_SEQUENCE_NUMBER,
assert optin_parent_txid in self.nodes[0].getrawmempool() fee_rate=Decimal('0.02'),
)
replacement_parent_tx = self.nodes[0].createrawtransaction([{
'txid': base_txid,
'vout': 0,
"sequence": 0xfffffffd,
}], {self.nodes[0].getnewaddress(): Decimal("9.90000")})
replacement_parent_tx = self.nodes[0].signrawtransactionwithwallet(replacement_parent_tx)
# Test if parent tx can be replaced. # Test if parent tx can be replaced.
res = self.nodes[0].testmempoolaccept(rawtxs=[replacement_parent_tx['hex']], maxfeerate=0)[0] res = self.nodes[0].testmempoolaccept(rawtxs=[replacement_parent_tx['hex']])[0]
# Parent can be replaced. # Parent can be replaced.
assert_equal(res['allowed'], True) assert_equal(res['allowed'], True)
# Create an opt-out child tx spending the opt-in parent # Create an opt-out child tx spending the opt-in parent
optout_child_tx = self.nodes[0].createrawtransaction([{ parent_utxo = wallet.get_utxo(txid=optin_parent_tx['txid'])
'txid': optin_parent_txid, optout_child_tx = wallet.send_self_transfer(
'vout': 0, from_node=self.nodes[0],
"sequence": 0xffffffff, utxo_to_spend=parent_utxo,
}], {self.nodes[0].getnewaddress(): Decimal("9.99990")}) sequence=0xffffffff,
fee_rate=Decimal('0.01'),
)
optout_child_tx = self.nodes[0].signrawtransactionwithwallet(optout_child_tx) # Reports true due to inheritance
assert_equal(True, self.nodes[0].getmempoolentry(optout_child_tx['txid'])['bip125-replaceable'])
# Broadcast child tx replacement_child_tx = wallet.create_self_transfer(
optout_child_txid = self.nodes[0].sendrawtransaction(hexstring=optout_child_tx["hex"], maxfeerate=0) from_node=self.nodes[0],
assert optout_child_txid in self.nodes[0].getrawmempool() utxo_to_spend=parent_utxo,
sequence=0xffffffff,
replacement_child_tx = self.nodes[0].createrawtransaction([{ fee_rate=Decimal('0.02'),
'txid': optin_parent_txid, mempool_valid=False,
'vout': 0, )
"sequence": 0xffffffff,
}], {self.nodes[0].getnewaddress(): Decimal("9.00000")})
replacement_child_tx = self.nodes[0].signrawtransactionwithwallet(replacement_child_tx)
# Broadcast replacement child tx # Broadcast replacement child tx
# BIP 125 : # BIP 125 :
# 1. The original transactions signal replaceability explicitly or through inheritance as described in the above # 1. The original transactions signal replaceability explicitly or through inheritance as described in the above
# Summary section. # Summary section.
# The original transaction (`optout_child_tx`) doesn't signal RBF but its parent (`optin_parent_txid`) does. # 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. # The replacement transaction (`replacement_child_tx`) should be able to replace the original transaction.
# See CVE-2021-31876 for further explanations. # See CVE-2021-31876 for further explanations.
assert optin_parent_txid in self.nodes[0].getrawmempool() 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) assert_raises_rpc_error(-26, 'txn-mempool-conflict', self.nodes[0].sendrawtransaction, replacement_child_tx["hex"], 0)
if __name__ == '__main__': if __name__ == '__main__':
ReplaceByFeeTest().main() ReplaceByFeeTest().main()
+1 -1
View File
@@ -39,7 +39,7 @@ MAX_BLOOM_HASH_FUNCS = 50
COIN = 100000000 # 1 btc in satoshis COIN = 100000000 # 1 btc in satoshis
MAX_MONEY = 21000000 * COIN 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_PROTOCOL_MESSAGE_LENGTH = 4000000 # Maximum length of incoming protocol messages
MAX_HEADERS_RESULTS = 2000 # Number of headers sent in one getheaders result MAX_HEADERS_RESULTS = 2000 # Number of headers sent in one getheaders result
+5 -5
View File
@@ -124,13 +124,13 @@ class MiniWallet:
else: else:
return self._utxos[index] return self._utxos[index]
def send_self_transfer(self, *, fee_rate=Decimal("0.003"), from_node, utxo_to_spend=None, locktime=0): 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.""" """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) tx = self.create_self_transfer(**kwargs)
self.sendrawtransaction(from_node=from_node, tx_hex=tx['hex']) self.sendrawtransaction(from_node=kwargs['from_node'], tx_hex=tx['hex'])
return tx return tx
def create_self_transfer(self, *, fee_rate=Decimal("0.003"), from_node, utxo_to_spend=None, mempool_valid=True, locktime=0): 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.""" """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']) 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 utxo_to_spend = utxo_to_spend or self._utxos.pop() # Pick the largest utxo (if none provided) and hope it covers the fee
@@ -140,7 +140,7 @@ class MiniWallet:
assert send_value > 0 assert send_value > 0
tx = CTransaction() 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.vout = [CTxOut(int(send_value * COIN), self._scriptPubKey)]
tx.nLockTime = locktime tx.nLockTime = locktime
if not self._address: if not self._address: