Changeset View
Changeset View
Standalone View
Standalone View
test/functional/mempool_accept.py
#!/usr/bin/env python3 | #!/usr/bin/env python3 | ||||
# Copyright (c) 2017-2019 The Bitcoin Core developers | # Copyright (c) 2017-2019 The Bitcoin Core developers | ||||
# Distributed under the MIT software license, see the accompanying | # Distributed under the MIT software license, see the accompanying | ||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php. | # file COPYING or http://www.opensource.org/licenses/mit-license.php. | ||||
"""Test mempool acceptance of raw transactions.""" | """Test mempool acceptance of raw transactions.""" | ||||
from io import BytesIO | |||||
from test_framework.test_framework import BitcoinTestFramework | from test_framework.test_framework import BitcoinTestFramework | ||||
from test_framework.messages import ( | from test_framework.messages import ( | ||||
COIN, | COIN, | ||||
COutPoint, | COutPoint, | ||||
CTransaction, | CTransaction, | ||||
CTxOut, | CTxOut, | ||||
FromHex, | |||||
MAX_BLOCK_BASE_SIZE, | MAX_BLOCK_BASE_SIZE, | ||||
ToHex, | |||||
) | ) | ||||
from test_framework.script import ( | from test_framework.script import ( | ||||
hash160, | hash160, | ||||
CScript, | CScript, | ||||
OP_0, | OP_0, | ||||
OP_EQUAL, | OP_EQUAL, | ||||
OP_HASH160, | OP_HASH160, | ||||
OP_RETURN, | OP_RETURN, | ||||
) | ) | ||||
from test_framework.util import ( | from test_framework.util import ( | ||||
assert_equal, | assert_equal, | ||||
assert_raises_rpc_error, | assert_raises_rpc_error, | ||||
hex_str_to_bytes, | |||||
wait_until, | wait_until, | ||||
) | ) | ||||
class MempoolAcceptanceTest(BitcoinTestFramework): | class MempoolAcceptanceTest(BitcoinTestFramework): | ||||
def set_test_params(self): | def set_test_params(self): | ||||
self.num_nodes = 1 | self.num_nodes = 1 | ||||
self.extra_args = [[ | self.extra_args = [[ | ||||
▲ Show 20 Lines • Show All 45 Lines • ▼ Show 20 Lines | def run_test(self): | ||||
self.log.info('A transaction not in the mempool') | self.log.info('A transaction not in the mempool') | ||||
fee = 0.00000700 | fee = 0.00000700 | ||||
raw_tx_0 = node.signrawtransactionwithwallet(node.createrawtransaction( | raw_tx_0 = node.signrawtransactionwithwallet(node.createrawtransaction( | ||||
inputs=[{"txid": txid_in_block, "vout": 0, | inputs=[{"txid": txid_in_block, "vout": 0, | ||||
"sequence": 0xfffffffd}], | "sequence": 0xfffffffd}], | ||||
outputs=[{node.getnewaddress(): 0.3 - fee}], | outputs=[{node.getnewaddress(): 0.3 - fee}], | ||||
))['hex'] | ))['hex'] | ||||
tx = CTransaction() | tx = FromHex(CTransaction(), raw_tx_0) | ||||
tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_0))) | |||||
txid_0 = tx.rehash() | txid_0 = tx.rehash() | ||||
self.check_mempool_result( | self.check_mempool_result( | ||||
result_expected=[{'txid': txid_0, 'allowed': True}], | result_expected=[{'txid': txid_0, 'allowed': True}], | ||||
rawtxs=[raw_tx_0], | rawtxs=[raw_tx_0], | ||||
) | ) | ||||
self.log.info('A final transaction not in the mempool') | self.log.info('A final transaction not in the mempool') | ||||
# Pick a random coin(base) to spend | # Pick a random coin(base) to spend | ||||
coin = coins.pop() | coin = coins.pop() | ||||
raw_tx_final = node.signrawtransactionwithwallet(node.createrawtransaction( | raw_tx_final = node.signrawtransactionwithwallet(node.createrawtransaction( | ||||
inputs=[{'txid': coin['txid'], 'vout': coin['vout'], | inputs=[{'txid': coin['txid'], 'vout': coin['vout'], | ||||
"sequence": 0xffffffff}], # SEQUENCE_FINAL | "sequence": 0xffffffff}], # SEQUENCE_FINAL | ||||
outputs=[{node.getnewaddress(): 0.025}], | outputs=[{node.getnewaddress(): 0.025}], | ||||
locktime=node.getblockcount() + 2000, # Can be anything | locktime=node.getblockcount() + 2000, # Can be anything | ||||
))['hex'] | ))['hex'] | ||||
tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_final))) | tx = FromHex(CTransaction(), raw_tx_final) | ||||
self.check_mempool_result( | self.check_mempool_result( | ||||
result_expected=[{'txid': tx.rehash(), 'allowed': True}], | result_expected=[{'txid': tx.rehash(), 'allowed': True}], | ||||
rawtxs=[tx.serialize().hex()], | rawtxs=[ToHex(tx)], | ||||
allowhighfees=True, | allowhighfees=True, | ||||
) | ) | ||||
node.sendrawtransaction(hexstring=raw_tx_final, allowhighfees=True) | node.sendrawtransaction(hexstring=raw_tx_final, allowhighfees=True) | ||||
self.mempool_size += 1 | self.mempool_size += 1 | ||||
self.log.info('A transaction in the mempool') | self.log.info('A transaction in the mempool') | ||||
node.sendrawtransaction(hexstring=raw_tx_0) | node.sendrawtransaction(hexstring=raw_tx_0) | ||||
self.mempool_size += 1 | self.mempool_size += 1 | ||||
self.check_mempool_result( | self.check_mempool_result( | ||||
result_expected=[{'txid': txid_0, 'allowed': False, | result_expected=[{'txid': txid_0, 'allowed': False, | ||||
'reject-reason': '18: txn-already-in-mempool'}], | 'reject-reason': '18: txn-already-in-mempool'}], | ||||
rawtxs=[raw_tx_0], | rawtxs=[raw_tx_0], | ||||
) | ) | ||||
# Removed RBF test | # Removed RBF test | ||||
# self.log.info('A transaction that replaces a mempool transaction') | # self.log.info('A transaction that replaces a mempool transaction') | ||||
# ... | # ... | ||||
self.log.info('A transaction that conflicts with an unconfirmed tx') | self.log.info('A transaction that conflicts with an unconfirmed tx') | ||||
# Send the transaction that replaces the mempool transaction and opts out of replaceability | # Send the transaction that conflicts with the mempool transaction | ||||
node.sendrawtransaction( | node.sendrawtransaction( | ||||
hexstring=tx.serialize().hex(), allowhighfees=True) | hexstring=ToHex(tx), allowhighfees=True) | ||||
# take original raw_tx_0 | # take original raw_tx_0 | ||||
tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_0))) | tx = FromHex(CTransaction(), raw_tx_0) | ||||
tx.vout[0].nValue -= int(4 * fee * COIN) # Set more fee | tx.vout[0].nValue -= int(4 * fee * COIN) # Set more fee | ||||
# skip re-signing the tx | # skip re-signing the tx | ||||
self.check_mempool_result( | self.check_mempool_result( | ||||
result_expected=[{'txid': tx.rehash( | result_expected=[{'txid': tx.rehash( | ||||
), 'allowed': False, 'reject-reason': '18: txn-mempool-conflict'}], | ), 'allowed': False, 'reject-reason': '18: txn-mempool-conflict'}], | ||||
rawtxs=[tx.serialize().hex()], | rawtxs=[ToHex(tx)], | ||||
allowhighfees=True, | allowhighfees=True, | ||||
) | ) | ||||
self.log.info('A transaction with missing inputs, that never existed') | self.log.info('A transaction with missing inputs, that never existed') | ||||
tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_0))) | tx = FromHex(CTransaction(), raw_tx_0) | ||||
tx.vin[0].prevout = COutPoint(hash=int('ff' * 32, 16), n=14) | tx.vin[0].prevout = COutPoint(hash=int('ff' * 32, 16), n=14) | ||||
# skip re-signing the tx | # skip re-signing the tx | ||||
self.check_mempool_result( | self.check_mempool_result( | ||||
result_expected=[ | result_expected=[ | ||||
{'txid': tx.rehash(), 'allowed': False, 'reject-reason': 'missing-inputs'}], | {'txid': tx.rehash(), 'allowed': False, 'reject-reason': 'missing-inputs'}], | ||||
rawtxs=[tx.serialize().hex()], | rawtxs=[ToHex(tx)], | ||||
) | ) | ||||
self.log.info( | self.log.info( | ||||
'A transaction with missing inputs, that existed once in the past') | 'A transaction with missing inputs, that existed once in the past') | ||||
tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_0))) | tx = FromHex(CTransaction(), raw_tx_0) | ||||
# Set vout to 1, to spend the other outpoint (49 coins) of the in-chain-tx we want to double spend | # Set vout to 1, to spend the other outpoint (49 coins) of the in-chain-tx we want to double spend | ||||
tx.vin[0].prevout.n = 1 | tx.vin[0].prevout.n = 1 | ||||
raw_tx_1 = node.signrawtransactionwithwallet( | raw_tx_1 = node.signrawtransactionwithwallet( | ||||
tx.serialize().hex())['hex'] | ToHex(tx))['hex'] | ||||
txid_1 = node.sendrawtransaction( | txid_1 = node.sendrawtransaction( | ||||
hexstring=raw_tx_1, allowhighfees=True) | hexstring=raw_tx_1, allowhighfees=True) | ||||
# Now spend both to "clearly hide" the outputs, ie. remove the coins from the utxo set by spending them | # Now spend both to "clearly hide" the outputs, ie. remove the coins from the utxo set by spending them | ||||
raw_tx_spend_both = node.signrawtransactionwithwallet(node.createrawtransaction( | raw_tx_spend_both = node.signrawtransactionwithwallet(node.createrawtransaction( | ||||
inputs=[ | inputs=[ | ||||
{'txid': txid_0, 'vout': 0}, | {'txid': txid_0, 'vout': 0}, | ||||
{'txid': txid_1, 'vout': 0}, | {'txid': txid_1, 'vout': 0}, | ||||
], | ], | ||||
Show All 15 Lines | def run_test(self): | ||||
rawtxs=[raw_tx_1], | rawtxs=[raw_tx_1], | ||||
) | ) | ||||
self.log.info('Create a signed "reference" tx for later use') | self.log.info('Create a signed "reference" tx for later use') | ||||
raw_tx_reference = node.signrawtransactionwithwallet(node.createrawtransaction( | raw_tx_reference = node.signrawtransactionwithwallet(node.createrawtransaction( | ||||
inputs=[{'txid': txid_spend_both, 'vout': 0}], | inputs=[{'txid': txid_spend_both, 'vout': 0}], | ||||
outputs=[{node.getnewaddress(): 0.05}], | outputs=[{node.getnewaddress(): 0.05}], | ||||
))['hex'] | ))['hex'] | ||||
tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference))) | tx = FromHex(CTransaction(), raw_tx_reference) | ||||
# Reference tx should be valid on itself | # Reference tx should be valid on itself | ||||
self.check_mempool_result( | self.check_mempool_result( | ||||
result_expected=[{'txid': tx.rehash(), 'allowed': True}], | result_expected=[{'txid': tx.rehash(), 'allowed': True}], | ||||
rawtxs=[tx.serialize().hex()], | rawtxs=[ToHex(tx)], | ||||
) | ) | ||||
self.log.info('A transaction with no outputs') | self.log.info('A transaction with no outputs') | ||||
tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference))) | tx = FromHex(CTransaction(), raw_tx_reference) | ||||
tx.vout = [] | tx.vout = [] | ||||
# Skip re-signing the transaction for context independent checks from now on | # Skip re-signing the transaction for context independent checks from now on | ||||
# tx.deserialize(BytesIO(hex_str_to_bytes(node.signrawtransactionwithwallet(tx.serialize().hex())['hex']))) | # FromHex(tx, node.signrawtransactionwithwallet(ToHex(tx))['hex']) | ||||
self.check_mempool_result( | self.check_mempool_result( | ||||
result_expected=[{'txid': tx.rehash( | result_expected=[{'txid': tx.rehash( | ||||
), 'allowed': False, 'reject-reason': '16: bad-txns-vout-empty'}], | ), 'allowed': False, 'reject-reason': '16: bad-txns-vout-empty'}], | ||||
rawtxs=[tx.serialize().hex()], | rawtxs=[ToHex(tx)], | ||||
) | ) | ||||
self.log.info('A really large transaction') | self.log.info('A really large transaction') | ||||
tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference))) | tx = FromHex(CTransaction(), raw_tx_reference) | ||||
tx.vin = [tx.vin[0]] * (1 + MAX_BLOCK_BASE_SIZE // | tx.vin = [tx.vin[0]] * (1 + MAX_BLOCK_BASE_SIZE // | ||||
len(tx.vin[0].serialize())) | len(tx.vin[0].serialize())) | ||||
self.check_mempool_result( | self.check_mempool_result( | ||||
result_expected=[ | result_expected=[ | ||||
{'txid': tx.rehash(), 'allowed': False, 'reject-reason': '16: bad-txns-oversize'}], | {'txid': tx.rehash(), 'allowed': False, 'reject-reason': '16: bad-txns-oversize'}], | ||||
rawtxs=[tx.serialize().hex()], | rawtxs=[ToHex(tx)], | ||||
) | ) | ||||
self.log.info('A transaction with negative output value') | self.log.info('A transaction with negative output value') | ||||
tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference))) | tx = FromHex(CTransaction(), raw_tx_reference) | ||||
tx.vout[0].nValue *= -1 | tx.vout[0].nValue *= -1 | ||||
self.check_mempool_result( | self.check_mempool_result( | ||||
result_expected=[{'txid': tx.rehash( | result_expected=[{'txid': tx.rehash( | ||||
), 'allowed': False, 'reject-reason': '16: bad-txns-vout-negative'}], | ), 'allowed': False, 'reject-reason': '16: bad-txns-vout-negative'}], | ||||
rawtxs=[tx.serialize().hex()], | rawtxs=[ToHex(tx)], | ||||
) | ) | ||||
self.log.info('A transaction with too large output value') | self.log.info('A transaction with too large output value') | ||||
tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference))) | tx = FromHex(CTransaction(), raw_tx_reference) | ||||
tx.vout[0].nValue = 21000000 * COIN + 1 | tx.vout[0].nValue = 21000000 * COIN + 1 | ||||
self.check_mempool_result( | self.check_mempool_result( | ||||
result_expected=[{'txid': tx.rehash( | result_expected=[{'txid': tx.rehash( | ||||
), 'allowed': False, 'reject-reason': '16: bad-txns-vout-toolarge'}], | ), 'allowed': False, 'reject-reason': '16: bad-txns-vout-toolarge'}], | ||||
rawtxs=[tx.serialize().hex()], | rawtxs=[ToHex(tx)], | ||||
) | ) | ||||
self.log.info('A transaction with too large sum of output values') | self.log.info('A transaction with too large sum of output values') | ||||
tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference))) | tx = FromHex(CTransaction(), raw_tx_reference) | ||||
tx.vout = [tx.vout[0]] * 2 | tx.vout = [tx.vout[0]] * 2 | ||||
tx.vout[0].nValue = 21000000 * COIN | tx.vout[0].nValue = 21000000 * COIN | ||||
self.check_mempool_result( | self.check_mempool_result( | ||||
result_expected=[{'txid': tx.rehash( | result_expected=[{'txid': tx.rehash( | ||||
), 'allowed': False, 'reject-reason': '16: bad-txns-txouttotal-toolarge'}], | ), 'allowed': False, 'reject-reason': '16: bad-txns-txouttotal-toolarge'}], | ||||
rawtxs=[tx.serialize().hex()], | rawtxs=[ToHex(tx)], | ||||
) | ) | ||||
self.log.info('A transaction with duplicate inputs') | self.log.info('A transaction with duplicate inputs') | ||||
tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference))) | tx = FromHex(CTransaction(), raw_tx_reference) | ||||
tx.vin = [tx.vin[0]] * 2 | tx.vin = [tx.vin[0]] * 2 | ||||
self.check_mempool_result( | self.check_mempool_result( | ||||
result_expected=[{'txid': tx.rehash( | result_expected=[{'txid': tx.rehash( | ||||
), 'allowed': False, 'reject-reason': '16: bad-txns-inputs-duplicate'}], | ), 'allowed': False, 'reject-reason': '16: bad-txns-inputs-duplicate'}], | ||||
rawtxs=[tx.serialize().hex()], | rawtxs=[ToHex(tx)], | ||||
) | ) | ||||
self.log.info('A coinbase transaction') | self.log.info('A coinbase transaction') | ||||
# Pick the input of the first tx we signed, so it has to be a coinbase tx | # Pick the input of the first tx we signed, so it has to be a coinbase tx | ||||
raw_tx_coinbase_spent = node.getrawtransaction( | raw_tx_coinbase_spent = node.getrawtransaction( | ||||
txid=node.decoderawtransaction(hexstring=raw_tx_in_block)['vin'][0]['txid']) | txid=node.decoderawtransaction(hexstring=raw_tx_in_block)['vin'][0]['txid']) | ||||
tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_coinbase_spent))) | tx = FromHex(CTransaction(), raw_tx_coinbase_spent) | ||||
self.check_mempool_result( | self.check_mempool_result( | ||||
result_expected=[ | result_expected=[ | ||||
{'txid': tx.rehash(), 'allowed': False, 'reject-reason': '16: bad-tx-coinbase'}], | {'txid': tx.rehash(), 'allowed': False, 'reject-reason': '16: bad-tx-coinbase'}], | ||||
rawtxs=[tx.serialize().hex()], | rawtxs=[ToHex(tx)], | ||||
) | ) | ||||
self.log.info('Some nonstandard transactions') | self.log.info('Some nonstandard transactions') | ||||
tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference))) | tx = FromHex(CTransaction(), raw_tx_reference) | ||||
tx.nVersion = 3 # A version currently non-standard | tx.nVersion = 3 # A version currently non-standard | ||||
self.check_mempool_result( | self.check_mempool_result( | ||||
result_expected=[ | result_expected=[ | ||||
{'txid': tx.rehash(), 'allowed': False, 'reject-reason': '64: version'}], | {'txid': tx.rehash(), 'allowed': False, 'reject-reason': '64: version'}], | ||||
rawtxs=[tx.serialize().hex()], | rawtxs=[ToHex(tx)], | ||||
) | ) | ||||
tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference))) | tx = FromHex(CTransaction(), raw_tx_reference) | ||||
tx.vout[0].scriptPubKey = CScript([OP_0]) # Some non-standard script | tx.vout[0].scriptPubKey = CScript([OP_0]) # Some non-standard script | ||||
self.check_mempool_result( | self.check_mempool_result( | ||||
result_expected=[ | result_expected=[ | ||||
{'txid': tx.rehash(), 'allowed': False, 'reject-reason': '64: scriptpubkey'}], | {'txid': tx.rehash(), 'allowed': False, 'reject-reason': '64: scriptpubkey'}], | ||||
rawtxs=[tx.serialize().hex()], | rawtxs=[ToHex(tx)], | ||||
) | ) | ||||
tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference))) | tx = FromHex(CTransaction(), raw_tx_reference) | ||||
# Some not-pushonly scriptSig | # Some not-pushonly scriptSig | ||||
tx.vin[0].scriptSig = CScript([OP_HASH160]) | tx.vin[0].scriptSig = CScript([OP_HASH160]) | ||||
self.check_mempool_result( | self.check_mempool_result( | ||||
result_expected=[{'txid': tx.rehash( | result_expected=[{'txid': tx.rehash( | ||||
), 'allowed': False, 'reject-reason': '64: scriptsig-not-pushonly'}], | ), 'allowed': False, 'reject-reason': '64: scriptsig-not-pushonly'}], | ||||
rawtxs=[tx.serialize().hex()], | rawtxs=[ToHex(tx)], | ||||
) | ) | ||||
tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference))) | tx = FromHex(CTransaction(), raw_tx_reference) | ||||
output_p2sh_burn = CTxOut(nValue=540, scriptPubKey=CScript( | output_p2sh_burn = CTxOut(nValue=540, scriptPubKey=CScript( | ||||
[OP_HASH160, hash160(b'burn'), OP_EQUAL])) | [OP_HASH160, hash160(b'burn'), OP_EQUAL])) | ||||
# Use enough outputs to make the tx too large for our policy | # Use enough outputs to make the tx too large for our policy | ||||
num_scripts = 100000 // len(output_p2sh_burn.serialize()) | num_scripts = 100000 // len(output_p2sh_burn.serialize()) | ||||
tx.vout = [output_p2sh_burn] * num_scripts | tx.vout = [output_p2sh_burn] * num_scripts | ||||
self.check_mempool_result( | self.check_mempool_result( | ||||
result_expected=[ | result_expected=[ | ||||
{'txid': tx.rehash(), 'allowed': False, 'reject-reason': '64: tx-size'}], | {'txid': tx.rehash(), 'allowed': False, 'reject-reason': '64: tx-size'}], | ||||
rawtxs=[tx.serialize().hex()], | rawtxs=[ToHex(tx)], | ||||
) | ) | ||||
tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference))) | tx = FromHex(CTransaction(), raw_tx_reference) | ||||
tx.vout[0] = output_p2sh_burn | tx.vout[0] = output_p2sh_burn | ||||
# Make output smaller, such that it is dust for our policy | # Make output smaller, such that it is dust for our policy | ||||
tx.vout[0].nValue -= 1 | tx.vout[0].nValue -= 1 | ||||
self.check_mempool_result( | self.check_mempool_result( | ||||
result_expected=[ | result_expected=[ | ||||
{'txid': tx.rehash(), 'allowed': False, 'reject-reason': '64: dust'}], | {'txid': tx.rehash(), 'allowed': False, 'reject-reason': '64: dust'}], | ||||
rawtxs=[tx.serialize().hex()], | rawtxs=[ToHex(tx)], | ||||
) | ) | ||||
tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference))) | tx = FromHex(CTransaction(), raw_tx_reference) | ||||
tx.vout[0].scriptPubKey = CScript([OP_RETURN, b'\xff']) | tx.vout[0].scriptPubKey = CScript([OP_RETURN, b'\xff']) | ||||
tx.vout = [tx.vout[0]] * 2 | tx.vout = [tx.vout[0]] * 2 | ||||
self.check_mempool_result( | self.check_mempool_result( | ||||
result_expected=[ | result_expected=[ | ||||
{'txid': tx.rehash(), 'allowed': False, 'reject-reason': '64: multi-op-return'}], | {'txid': tx.rehash(), 'allowed': False, 'reject-reason': '64: multi-op-return'}], | ||||
rawtxs=[tx.serialize().hex()], | rawtxs=[ToHex(tx)], | ||||
) | ) | ||||
self.log.info('A timelocked transaction') | self.log.info('A timelocked transaction') | ||||
tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference))) | tx = FromHex(CTransaction(), raw_tx_reference) | ||||
# Should be non-max, so locktime is not ignored | # Should be non-max, so locktime is not ignored | ||||
tx.vin[0].nSequence -= 1 | tx.vin[0].nSequence -= 1 | ||||
tx.nLockTime = node.getblockcount() + 1 | tx.nLockTime = node.getblockcount() + 1 | ||||
self.check_mempool_result( | self.check_mempool_result( | ||||
result_expected=[ | result_expected=[ | ||||
{'txid': tx.rehash(), 'allowed': False, 'reject-reason': '64: bad-txns-nonfinal'}], | {'txid': tx.rehash(), 'allowed': False, 'reject-reason': '64: bad-txns-nonfinal'}], | ||||
rawtxs=[tx.serialize().hex()], | rawtxs=[ToHex(tx)], | ||||
) | ) | ||||
self.log.info('A transaction that is locked by BIP68 sequence logic') | self.log.info('A transaction that is locked by BIP68 sequence logic') | ||||
tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference))) | tx = FromHex(CTransaction(), raw_tx_reference) | ||||
# We could include it in the second block mined from now, but not the very next one | # We could include it in the second block mined from now, but not the very next one | ||||
tx.vin[0].nSequence = 2 | tx.vin[0].nSequence = 2 | ||||
# Can skip re-signing the tx because of early rejection | # Can skip re-signing the tx because of early rejection | ||||
self.check_mempool_result( | self.check_mempool_result( | ||||
result_expected=[ | result_expected=[ | ||||
{'txid': tx.rehash(), 'allowed': False, 'reject-reason': '64: non-BIP68-final'}], | {'txid': tx.rehash(), 'allowed': False, 'reject-reason': '64: non-BIP68-final'}], | ||||
rawtxs=[tx.serialize().hex()], | rawtxs=[ToHex(tx)], | ||||
allowhighfees=True, | allowhighfees=True, | ||||
) | ) | ||||
if __name__ == '__main__': | if __name__ == '__main__': | ||||
MempoolAcceptanceTest().main() | MempoolAcceptanceTest().main() |