2017-11-17 14:54:39 -03:00
#!/usr/bin/env python3
2022-12-24 20:49:50 -03:00
# Copyright (c) 2017-2022 The Bitcoin Core developers
2017-11-17 14:54:39 -03:00
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
""" Test mempool acceptance of raw transactions. """
2022-01-10 23:30:23 -03:00
from copy import deepcopy
2020-05-24 08:58:56 -04:00
from decimal import Decimal
2018-11-27 14:54:05 -03:00
import math
2018-12-11 15:30:50 -03:00
2017-11-17 14:54:39 -03:00
from test_framework . test_framework import BitcoinTestFramework
from test_framework . messages import (
2022-07-12 11:28:20 -04:00
MAX_BIP125_RBF_SEQUENCE ,
2017-11-17 14:54:39 -03:00
COIN ,
COutPoint ,
2022-10-05 15:50:30 -03:00
CTransaction ,
2021-07-05 17:06:06 -04:00
CTxIn ,
2022-10-05 15:50:30 -03:00
CTxInWitness ,
2017-11-17 14:54:39 -03:00
CTxOut ,
2021-06-30 19:43:45 -04:00
MAX_BLOCK_WEIGHT ,
2024-06-18 09:38:16 -04:00
WITNESS_SCALE_FACTOR ,
2020-03-03 14:48:49 -03:00
MAX_MONEY ,
2022-01-10 22:08:01 -03:00
SEQUENCE_FINAL ,
2021-06-15 17:02:28 -04:00
tx_from_hex ,
2017-11-17 14:54:39 -03:00
)
from test_framework . script import (
CScript ,
OP_0 ,
OP_HASH160 ,
OP_RETURN ,
2022-10-05 15:50:30 -03:00
OP_TRUE ,
2023-12-20 10:58:53 -03:00
SIGHASH_ALL ,
sign_input_legacy ,
2017-11-17 14:54:39 -03:00
)
2021-06-27 17:52:38 -04:00
from test_framework . script_util import (
2022-10-05 15:50:30 -03:00
DUMMY_MIN_OP_RETURN_SCRIPT ,
2021-10-18 17:25:04 -03:00
keys_to_multisig_script ,
2022-10-05 15:50:30 -03:00
MIN_PADDING ,
MIN_STANDARD_TX_NONWITNESS_SIZE ,
2024-06-27 14:45:01 -04:00
PAY_TO_ANCHOR ,
2021-06-27 17:52:38 -04:00
script_to_p2sh_script ,
2022-10-05 15:50:30 -03:00
script_to_p2wsh_script ,
2021-06-27 17:52:38 -04:00
)
2017-11-17 14:54:39 -03:00
from test_framework . util import (
assert_equal ,
2022-10-05 15:50:30 -03:00
assert_greater_than ,
2017-11-17 14:54:39 -03:00
assert_raises_rpc_error ,
)
2022-01-10 23:30:23 -03:00
from test_framework . wallet import MiniWallet
2023-05-23 17:38:31 -04:00
from test_framework . wallet_util import generate_keypair
2017-11-17 14:54:39 -03:00
class MempoolAcceptanceTest ( BitcoinTestFramework ) :
def set_test_params ( self ) :
self . num_nodes = 1
self . extra_args = [ [
2019-11-20 17:40:53 -03:00
' -txindex ' , ' -permitbaremultisig=0 ' ,
2017-11-17 14:54:39 -03:00
] ] * self . num_nodes
2019-12-06 11:37:49 -03:00
self . supports_cli = False
2017-11-17 14:54:39 -03:00
def check_mempool_result ( self , result_expected , * args , * * kwargs ) :
""" Wrapper to check result of testmempoolaccept on node_0 ' s mempool """
result_test = self . nodes [ 0 ] . testmempoolaccept ( * args , * * kwargs )
2021-01-07 12:47:35 -03:00
for r in result_test :
2022-09-28 10:07:56 -03:00
# Skip these checks for now
r . pop ( ' wtxid ' )
if " fees " in r :
r [ " fees " ] . pop ( " effective-feerate " )
2022-09-28 10:07:56 -03:00
r [ " fees " ] . pop ( " effective-includes " )
2017-11-17 14:54:39 -03:00
assert_equal ( result_expected , result_test )
assert_equal ( self . nodes [ 0 ] . getmempoolinfo ( ) [ ' size ' ] , self . mempool_size ) # Must not change mempool state
def run_test ( self ) :
node = self . nodes [ 0 ]
2022-01-10 23:30:23 -03:00
self . wallet = MiniWallet ( node )
2017-11-17 14:54:39 -03:00
self . log . info ( ' Start with empty mempool, and 200 blocks ' )
self . mempool_size = 0
2019-02-11 15:56:19 -03:00
assert_equal ( node . getblockcount ( ) , 200 )
2017-11-17 14:54:39 -03:00
assert_equal ( node . getmempoolinfo ( ) [ ' size ' ] , self . mempool_size )
self . log . info ( ' Should not accept garbage to testmempoolaccept ' )
2022-09-12 10:04:15 -03:00
assert_raises_rpc_error ( - 3 , ' JSON value of type string is not of expected type array ' , lambda : node . testmempoolaccept ( rawtxs = ' ff00baar ' ) )
2021-02-11 15:50:45 -03:00
assert_raises_rpc_error ( - 8 , ' Array must contain between 1 and 25 transactions. ' , lambda : node . testmempoolaccept ( rawtxs = [ ' ff22 ' ] * 26 ) )
assert_raises_rpc_error ( - 8 , ' Array must contain between 1 and 25 transactions. ' , lambda : node . testmempoolaccept ( rawtxs = [ ] ) )
2017-11-17 14:54:39 -03:00
assert_raises_rpc_error ( - 22 , ' TX decode failed ' , lambda : node . testmempoolaccept ( rawtxs = [ ' ff00baar ' ] ) )
self . log . info ( ' A transaction already in the blockchain ' )
2022-01-10 23:30:23 -03:00
tx = self . wallet . create_self_transfer ( ) [ ' tx ' ] # Pick a random coin(base) to spend
tx . vout . append ( deepcopy ( tx . vout [ 0 ] ) )
tx . vout [ 0 ] . nValue = int ( 0.3 * COIN )
tx . vout [ 1 ] . nValue = int ( 49 * COIN )
raw_tx_in_block = tx . serialize ( ) . hex ( )
2022-06-01 11:05:13 -04:00
txid_in_block = self . wallet . sendrawtransaction ( from_node = node , tx_hex = raw_tx_in_block )
2021-08-19 11:10:24 -04:00
self . generate ( node , 1 )
2018-12-11 15:30:50 -03:00
self . mempool_size = 0
2024-02-14 13:10:34 -03:00
# Also check feerate. 1BTC/kvB fails
assert_raises_rpc_error ( - 8 , " Fee rates larger than or equal to 1BTC/kvB are not accepted " , lambda : self . check_mempool_result (
result_expected = None ,
rawtxs = [ raw_tx_in_block ] ,
maxfeerate = 1 ,
) )
2024-02-21 00:13:48 -03:00
# Check negative feerate
assert_raises_rpc_error ( - 3 , " Amount out of range " , lambda : self . check_mempool_result (
result_expected = None ,
rawtxs = [ raw_tx_in_block ] ,
maxfeerate = - 0.01 ,
) )
2024-02-14 13:10:34 -03:00
# ... 0.99 passes
2017-11-17 14:54:39 -03:00
self . check_mempool_result (
2019-10-10 12:19:42 -03:00
result_expected = [ { ' txid ' : txid_in_block , ' allowed ' : False , ' reject-reason ' : ' txn-already-known ' } ] ,
2017-11-17 14:54:39 -03:00
rawtxs = [ raw_tx_in_block ] ,
2024-02-14 13:10:34 -03:00
maxfeerate = 0.99 ,
2017-11-17 14:54:39 -03:00
)
self . log . info ( ' A transaction not in the mempool ' )
2020-09-29 14:41:58 -03:00
fee = Decimal ( ' 0.000007 ' )
2022-01-10 23:30:23 -03:00
utxo_to_spend = self . wallet . get_utxo ( txid = txid_in_block ) # use 0.3 BTC UTXO
2022-07-12 11:28:20 -04:00
tx = self . wallet . create_self_transfer ( utxo_to_spend = utxo_to_spend , sequence = MAX_BIP125_RBF_SEQUENCE ) [ ' tx ' ]
2022-01-10 23:30:23 -03:00
tx . vout [ 0 ] . nValue = int ( ( Decimal ( ' 0.3 ' ) - fee ) * COIN )
raw_tx_0 = tx . serialize ( ) . hex ( )
2017-11-17 14:54:39 -03:00
txid_0 = tx . rehash ( )
self . check_mempool_result (
2020-09-29 14:41:58 -03:00
result_expected = [ { ' txid ' : txid_0 , ' allowed ' : True , ' vsize ' : tx . get_vsize ( ) , ' fees ' : { ' base ' : fee } } ] ,
2017-11-17 14:54:39 -03:00
rawtxs = [ raw_tx_0 ] ,
)
2018-12-11 15:30:50 -03:00
self . log . info ( ' A final transaction not in the mempool ' )
2020-09-29 14:41:58 -03:00
output_amount = Decimal ( ' 0.025 ' )
2022-01-10 23:30:23 -03:00
tx = self . wallet . create_self_transfer (
sequence = SEQUENCE_FINAL ,
2018-12-11 15:30:50 -03:00
locktime = node . getblockcount ( ) + 2000 , # Can be anything
2022-01-10 23:30:23 -03:00
) [ ' tx ' ]
tx . vout [ 0 ] . nValue = int ( output_amount * COIN )
raw_tx_final = tx . serialize ( ) . hex ( )
2021-06-15 17:02:28 -04:00
tx = tx_from_hex ( raw_tx_final )
2022-01-10 23:30:23 -03:00
fee_expected = Decimal ( ' 50.0 ' ) - output_amount
2018-12-11 15:30:50 -03:00
self . check_mempool_result (
2020-09-29 14:41:58 -03:00
result_expected = [ { ' txid ' : tx . rehash ( ) , ' allowed ' : True , ' vsize ' : tx . get_vsize ( ) , ' fees ' : { ' base ' : fee_expected } } ] ,
2019-02-18 12:35:48 -03:00
rawtxs = [ tx . serialize ( ) . hex ( ) ] ,
2018-06-27 04:21:07 -04:00
maxfeerate = 0 ,
2018-12-11 15:30:50 -03:00
)
2018-06-27 04:21:07 -04:00
node . sendrawtransaction ( hexstring = raw_tx_final , maxfeerate = 0 )
2018-12-11 15:30:50 -03:00
self . mempool_size + = 1
2017-11-17 14:54:39 -03:00
self . log . info ( ' A transaction in the mempool ' )
node . sendrawtransaction ( hexstring = raw_tx_0 )
2018-12-11 15:30:50 -03:00
self . mempool_size + = 1
2017-11-17 14:54:39 -03:00
self . check_mempool_result (
2019-10-10 12:19:42 -03:00
result_expected = [ { ' txid ' : txid_0 , ' allowed ' : False , ' reject-reason ' : ' txn-already-in-mempool ' } ] ,
2017-11-17 14:54:39 -03:00
rawtxs = [ raw_tx_0 ] ,
)
self . log . info ( ' A transaction that replaces a mempool transaction ' )
2021-06-15 17:02:28 -04:00
tx = tx_from_hex ( raw_tx_0 )
2017-11-17 14:54:39 -03:00
tx . vout [ 0 ] . nValue - = int ( fee * COIN ) # Double the fee
2022-01-10 23:30:23 -03:00
raw_tx_0 = tx . serialize ( ) . hex ( )
2017-11-17 14:54:39 -03:00
txid_0 = tx . rehash ( )
self . check_mempool_result (
2020-09-29 14:41:58 -03:00
result_expected = [ { ' txid ' : txid_0 , ' allowed ' : True , ' vsize ' : tx . get_vsize ( ) , ' fees ' : { ' base ' : ( 2 * fee ) } } ] ,
2017-11-17 14:54:39 -03:00
rawtxs = [ raw_tx_0 ] ,
)
2018-06-27 04:21:07 -04:00
node . sendrawtransaction ( hexstring = tx . serialize ( ) . hex ( ) , maxfeerate = 0 )
2017-11-17 14:54:39 -03:00
self . log . info ( ' A transaction with missing inputs, that never existed ' )
2021-06-15 17:02:28 -04:00
tx = tx_from_hex ( raw_tx_0 )
2017-11-17 14:54:39 -03:00
tx . vin [ 0 ] . prevout = COutPoint ( hash = int ( ' ff ' * 32 , 16 ) , n = 14 )
self . check_mempool_result (
result_expected = [ { ' txid ' : tx . rehash ( ) , ' allowed ' : False , ' reject-reason ' : ' missing-inputs ' } ] ,
2019-02-18 12:35:48 -03:00
rawtxs = [ tx . serialize ( ) . hex ( ) ] ,
2017-11-17 14:54:39 -03:00
)
self . log . info ( ' A transaction with missing inputs, that existed once in the past ' )
2021-06-15 17:02:28 -04:00
tx = tx_from_hex ( raw_tx_0 )
2017-11-17 14:54:39 -03:00
tx . vin [ 0 ] . prevout . n = 1 # Set vout to 1, to spend the other outpoint (49 coins) of the in-chain-tx we want to double spend
2022-01-10 23:30:23 -03:00
raw_tx_1 = tx . serialize ( ) . hex ( )
2018-06-27 04:21:07 -04:00
txid_1 = node . sendrawtransaction ( hexstring = raw_tx_1 , maxfeerate = 0 )
2017-11-17 14:54:39 -03:00
# Now spend both to "clearly hide" the outputs, ie. remove the coins from the utxo set by spending them
2022-01-10 23:30:23 -03:00
tx = self . wallet . create_self_transfer ( ) [ ' tx ' ]
tx . vin . append ( deepcopy ( tx . vin [ 0 ] ) )
tx . wit . vtxinwit . append ( deepcopy ( tx . wit . vtxinwit [ 0 ] ) )
tx . vin [ 0 ] . prevout = COutPoint ( hash = int ( txid_0 , 16 ) , n = 0 )
tx . vin [ 1 ] . prevout = COutPoint ( hash = int ( txid_1 , 16 ) , n = 0 )
tx . vout [ 0 ] . nValue = int ( 0.1 * COIN )
raw_tx_spend_both = tx . serialize ( ) . hex ( )
2022-06-01 11:05:13 -04:00
txid_spend_both = self . wallet . sendrawtransaction ( from_node = node , tx_hex = raw_tx_spend_both )
2021-08-19 11:10:24 -04:00
self . generate ( node , 1 )
2017-11-17 14:54:39 -03:00
self . mempool_size = 0
# Now see if we can add the coins back to the utxo set by sending the exact txs again
self . check_mempool_result (
result_expected = [ { ' txid ' : txid_0 , ' allowed ' : False , ' reject-reason ' : ' missing-inputs ' } ] ,
rawtxs = [ raw_tx_0 ] ,
)
self . check_mempool_result (
result_expected = [ { ' txid ' : txid_1 , ' allowed ' : False , ' reject-reason ' : ' missing-inputs ' } ] ,
rawtxs = [ raw_tx_1 ] ,
)
2022-01-10 23:30:23 -03:00
self . log . info ( ' Create a " reference " tx for later use ' )
utxo_to_spend = self . wallet . get_utxo ( txid = txid_spend_both )
tx = self . wallet . create_self_transfer ( utxo_to_spend = utxo_to_spend , sequence = SEQUENCE_FINAL ) [ ' tx ' ]
tx . vout [ 0 ] . nValue = int ( 0.05 * COIN )
raw_tx_reference = tx . serialize ( ) . hex ( )
2017-11-17 14:54:39 -03:00
# Reference tx should be valid on itself
self . check_mempool_result (
2020-09-29 14:41:58 -03:00
result_expected = [ { ' txid ' : tx . rehash ( ) , ' allowed ' : True , ' vsize ' : tx . get_vsize ( ) , ' fees ' : { ' base ' : Decimal ( ' 0.1 ' ) - Decimal ( ' 0.05 ' ) } } ] ,
2019-02-18 12:35:48 -03:00
rawtxs = [ tx . serialize ( ) . hex ( ) ] ,
2019-08-01 13:03:01 -04:00
maxfeerate = 0 ,
2017-11-17 14:54:39 -03:00
)
self . log . info ( ' A transaction with no outputs ' )
2021-06-15 17:02:28 -04:00
tx = tx_from_hex ( raw_tx_reference )
2017-11-17 14:54:39 -03:00
tx . vout = [ ]
self . check_mempool_result (
2019-10-10 12:19:42 -03:00
result_expected = [ { ' txid ' : tx . rehash ( ) , ' allowed ' : False , ' reject-reason ' : ' bad-txns-vout-empty ' } ] ,
2019-02-18 12:35:48 -03:00
rawtxs = [ tx . serialize ( ) . hex ( ) ] ,
2017-11-17 14:54:39 -03:00
)
self . log . info ( ' A really large transaction ' )
2021-06-15 17:02:28 -04:00
tx = tx_from_hex ( raw_tx_reference )
2024-06-18 09:38:16 -04:00
tx . vin = [ tx . vin [ 0 ] ] * math . ceil ( ( MAX_BLOCK_WEIGHT / / WITNESS_SCALE_FACTOR ) / len ( tx . vin [ 0 ] . serialize ( ) ) )
2017-11-17 14:54:39 -03:00
self . check_mempool_result (
2019-10-10 12:19:42 -03:00
result_expected = [ { ' txid ' : tx . rehash ( ) , ' allowed ' : False , ' reject-reason ' : ' bad-txns-oversize ' } ] ,
2019-02-18 12:35:48 -03:00
rawtxs = [ tx . serialize ( ) . hex ( ) ] ,
2017-11-17 14:54:39 -03:00
)
self . log . info ( ' A transaction with negative output value ' )
2021-06-15 17:02:28 -04:00
tx = tx_from_hex ( raw_tx_reference )
2017-11-17 14:54:39 -03:00
tx . vout [ 0 ] . nValue * = - 1
self . check_mempool_result (
2019-10-10 12:19:42 -03:00
result_expected = [ { ' txid ' : tx . rehash ( ) , ' allowed ' : False , ' reject-reason ' : ' bad-txns-vout-negative ' } ] ,
2019-02-18 12:35:48 -03:00
rawtxs = [ tx . serialize ( ) . hex ( ) ] ,
2017-11-17 14:54:39 -03:00
)
2018-11-10 14:11:22 -03:00
# The following two validations prevent overflow of the output amounts (see CVE-2010-5139).
2017-11-17 14:54:39 -03:00
self . log . info ( ' A transaction with too large output value ' )
2021-06-15 17:02:28 -04:00
tx = tx_from_hex ( raw_tx_reference )
2020-03-03 14:48:49 -03:00
tx . vout [ 0 ] . nValue = MAX_MONEY + 1
2017-11-17 14:54:39 -03:00
self . check_mempool_result (
2019-10-10 12:19:42 -03:00
result_expected = [ { ' txid ' : tx . rehash ( ) , ' allowed ' : False , ' reject-reason ' : ' bad-txns-vout-toolarge ' } ] ,
2019-02-18 12:35:48 -03:00
rawtxs = [ tx . serialize ( ) . hex ( ) ] ,
2017-11-17 14:54:39 -03:00
)
self . log . info ( ' A transaction with too large sum of output values ' )
2021-06-15 17:02:28 -04:00
tx = tx_from_hex ( raw_tx_reference )
2017-11-17 14:54:39 -03:00
tx . vout = [ tx . vout [ 0 ] ] * 2
2020-03-03 14:48:49 -03:00
tx . vout [ 0 ] . nValue = MAX_MONEY
2017-11-17 14:54:39 -03:00
self . check_mempool_result (
2019-10-10 12:19:42 -03:00
result_expected = [ { ' txid ' : tx . rehash ( ) , ' allowed ' : False , ' reject-reason ' : ' bad-txns-txouttotal-toolarge ' } ] ,
2019-02-18 12:35:48 -03:00
rawtxs = [ tx . serialize ( ) . hex ( ) ] ,
2017-11-17 14:54:39 -03:00
)
self . log . info ( ' A transaction with duplicate inputs ' )
2021-06-15 17:02:28 -04:00
tx = tx_from_hex ( raw_tx_reference )
2017-11-17 14:54:39 -03:00
tx . vin = [ tx . vin [ 0 ] ] * 2
self . check_mempool_result (
2019-10-10 12:19:42 -03:00
result_expected = [ { ' txid ' : tx . rehash ( ) , ' allowed ' : False , ' reject-reason ' : ' bad-txns-inputs-duplicate ' } ] ,
2019-02-18 12:35:48 -03:00
rawtxs = [ tx . serialize ( ) . hex ( ) ] ,
2017-11-17 14:54:39 -03:00
)
2021-07-05 17:06:06 -04:00
self . log . info ( ' A non-coinbase transaction with coinbase-like outpoint ' )
tx = tx_from_hex ( raw_tx_reference )
tx . vin . append ( CTxIn ( COutPoint ( hash = 0 , n = 0xffffffff ) ) )
self . check_mempool_result (
result_expected = [ { ' txid ' : tx . rehash ( ) , ' allowed ' : False , ' reject-reason ' : ' bad-txns-prevout-null ' } ] ,
rawtxs = [ tx . serialize ( ) . hex ( ) ] ,
)
2017-11-17 14:54:39 -03:00
self . log . info ( ' A coinbase transaction ' )
2022-01-10 23:30:23 -03:00
# Pick the input of the first tx we created, so it has to be a coinbase tx
2017-11-17 14:54:39 -03:00
raw_tx_coinbase_spent = node . getrawtransaction ( txid = node . decoderawtransaction ( hexstring = raw_tx_in_block ) [ ' vin ' ] [ 0 ] [ ' txid ' ] )
2021-06-15 17:02:28 -04:00
tx = tx_from_hex ( raw_tx_coinbase_spent )
2017-11-17 14:54:39 -03:00
self . check_mempool_result (
2019-10-10 12:19:42 -03:00
result_expected = [ { ' txid ' : tx . rehash ( ) , ' allowed ' : False , ' reject-reason ' : ' coinbase ' } ] ,
2019-02-18 12:35:48 -03:00
rawtxs = [ tx . serialize ( ) . hex ( ) ] ,
2017-11-17 14:54:39 -03:00
)
self . log . info ( ' Some nonstandard transactions ' )
2021-06-15 17:02:28 -04:00
tx = tx_from_hex ( raw_tx_reference )
2024-01-26 17:27:13 -03:00
tx . version = 4 # A version currently non-standard
2017-11-17 14:54:39 -03:00
self . check_mempool_result (
2019-10-10 12:19:42 -03:00
result_expected = [ { ' txid ' : tx . rehash ( ) , ' allowed ' : False , ' reject-reason ' : ' version ' } ] ,
2019-02-18 12:35:48 -03:00
rawtxs = [ tx . serialize ( ) . hex ( ) ] ,
2017-11-17 14:54:39 -03:00
)
2021-06-15 17:02:28 -04:00
tx = tx_from_hex ( raw_tx_reference )
2017-11-17 14:54:39 -03:00
tx . vout [ 0 ] . scriptPubKey = CScript ( [ OP_0 ] ) # Some non-standard script
self . check_mempool_result (
2019-10-10 12:19:42 -03:00
result_expected = [ { ' txid ' : tx . rehash ( ) , ' allowed ' : False , ' reject-reason ' : ' scriptpubkey ' } ] ,
2019-02-18 12:35:48 -03:00
rawtxs = [ tx . serialize ( ) . hex ( ) ] ,
2019-11-20 17:40:53 -03:00
)
2021-06-15 17:02:28 -04:00
tx = tx_from_hex ( raw_tx_reference )
2023-05-23 17:38:31 -04:00
_ , pubkey = generate_keypair ( )
2021-10-18 17:25:04 -03:00
tx . vout [ 0 ] . scriptPubKey = keys_to_multisig_script ( [ pubkey ] * 3 , k = 2 ) # Some bare multisig script (2-of-3)
2019-11-20 17:40:53 -03:00
self . check_mempool_result (
result_expected = [ { ' txid ' : tx . rehash ( ) , ' allowed ' : False , ' reject-reason ' : ' bare-multisig ' } ] ,
rawtxs = [ tx . serialize ( ) . hex ( ) ] ,
2017-11-17 14:54:39 -03:00
)
2021-06-15 17:02:28 -04:00
tx = tx_from_hex ( raw_tx_reference )
2017-11-17 14:54:39 -03:00
tx . vin [ 0 ] . scriptSig = CScript ( [ OP_HASH160 ] ) # Some not-pushonly scriptSig
self . check_mempool_result (
2019-10-10 12:19:42 -03:00
result_expected = [ { ' txid ' : tx . rehash ( ) , ' allowed ' : False , ' reject-reason ' : ' scriptsig-not-pushonly ' } ] ,
2019-02-18 12:35:48 -03:00
rawtxs = [ tx . serialize ( ) . hex ( ) ] ,
2017-11-17 14:54:39 -03:00
)
2021-06-15 17:02:28 -04:00
tx = tx_from_hex ( raw_tx_reference )
2019-11-20 06:59:44 -03:00
tx . vin [ 0 ] . scriptSig = CScript ( [ b ' a ' * 1648 ] ) # Some too large scriptSig (>1650 bytes)
self . check_mempool_result (
result_expected = [ { ' txid ' : tx . rehash ( ) , ' allowed ' : False , ' reject-reason ' : ' scriptsig-size ' } ] ,
rawtxs = [ tx . serialize ( ) . hex ( ) ] ,
)
2021-06-15 17:02:28 -04:00
tx = tx_from_hex ( raw_tx_reference )
2021-06-27 17:52:38 -04:00
output_p2sh_burn = CTxOut ( nValue = 540 , scriptPubKey = script_to_p2sh_script ( b ' burn ' ) )
2017-11-17 14:54:39 -03:00
num_scripts = 100000 / / len ( output_p2sh_burn . serialize ( ) ) # Use enough outputs to make the tx too large for our policy
tx . vout = [ output_p2sh_burn ] * num_scripts
self . check_mempool_result (
2019-10-10 12:19:42 -03:00
result_expected = [ { ' txid ' : tx . rehash ( ) , ' allowed ' : False , ' reject-reason ' : ' tx-size ' } ] ,
2019-02-18 12:35:48 -03:00
rawtxs = [ tx . serialize ( ) . hex ( ) ] ,
2017-11-17 14:54:39 -03:00
)
2021-06-15 17:02:28 -04:00
tx = tx_from_hex ( raw_tx_reference )
2017-11-17 14:54:39 -03:00
tx . vout [ 0 ] = output_p2sh_burn
tx . vout [ 0 ] . nValue - = 1 # Make output smaller, such that it is dust for our policy
self . check_mempool_result (
2019-10-10 12:19:42 -03:00
result_expected = [ { ' txid ' : tx . rehash ( ) , ' allowed ' : False , ' reject-reason ' : ' dust ' } ] ,
2019-02-18 12:35:48 -03:00
rawtxs = [ tx . serialize ( ) . hex ( ) ] ,
2017-11-17 14:54:39 -03:00
)
2021-06-15 17:02:28 -04:00
tx = tx_from_hex ( raw_tx_reference )
2017-11-17 14:54:39 -03:00
tx . vout [ 0 ] . scriptPubKey = CScript ( [ OP_RETURN , b ' \xff ' ] )
tx . vout = [ tx . vout [ 0 ] ] * 2
self . check_mempool_result (
2019-10-10 12:19:42 -03:00
result_expected = [ { ' txid ' : tx . rehash ( ) , ' allowed ' : False , ' reject-reason ' : ' multi-op-return ' } ] ,
2019-02-18 12:35:48 -03:00
rawtxs = [ tx . serialize ( ) . hex ( ) ] ,
2017-11-17 14:54:39 -03:00
)
self . log . info ( ' A timelocked transaction ' )
2021-06-15 17:02:28 -04:00
tx = tx_from_hex ( raw_tx_reference )
2017-11-17 14:54:39 -03:00
tx . vin [ 0 ] . nSequence - = 1 # Should be non-max, so locktime is not ignored
tx . nLockTime = node . getblockcount ( ) + 1
self . check_mempool_result (
2019-10-10 12:19:42 -03:00
result_expected = [ { ' txid ' : tx . rehash ( ) , ' allowed ' : False , ' reject-reason ' : ' non-final ' } ] ,
2019-02-18 12:35:48 -03:00
rawtxs = [ tx . serialize ( ) . hex ( ) ] ,
2017-11-17 14:54:39 -03:00
)
self . log . info ( ' A transaction that is locked by BIP68 sequence logic ' )
2021-06-15 17:02:28 -04:00
tx = tx_from_hex ( raw_tx_reference )
2017-11-17 14:54:39 -03:00
tx . vin [ 0 ] . nSequence = 2 # We could include it in the second block mined from now, but not the very next one
self . check_mempool_result (
2019-10-10 12:19:42 -03:00
result_expected = [ { ' txid ' : tx . rehash ( ) , ' allowed ' : False , ' reject-reason ' : ' non-BIP68-final ' } ] ,
2019-02-18 12:35:48 -03:00
rawtxs = [ tx . serialize ( ) . hex ( ) ] ,
2018-06-27 04:21:07 -04:00
maxfeerate = 0 ,
2017-11-17 14:54:39 -03:00
)
2022-10-05 15:50:30 -03:00
# Prep for tiny-tx tests with wsh(OP_TRUE) output
seed_tx = self . wallet . send_to ( from_node = node , scriptPubKey = script_to_p2wsh_script ( CScript ( [ OP_TRUE ] ) ) , amount = COIN )
self . generate ( node , 1 )
self . log . info ( ' A tiny transaction(in non-witness bytes) that is disallowed ' )
tx = CTransaction ( )
2023-05-12 09:27:05 -04:00
tx . vin . append ( CTxIn ( COutPoint ( int ( seed_tx [ " txid " ] , 16 ) , seed_tx [ " sent_vout " ] ) , b " " , SEQUENCE_FINAL ) )
2022-10-05 15:50:30 -03:00
tx . wit . vtxinwit = [ CTxInWitness ( ) ]
tx . wit . vtxinwit [ 0 ] . scriptWitness . stack = [ CScript ( [ OP_TRUE ] ) ]
tx . vout . append ( CTxOut ( 0 , CScript ( [ OP_RETURN ] + ( [ OP_0 ] * ( MIN_PADDING - 2 ) ) ) ) )
# Note it's only non-witness size that matters!
assert_equal ( len ( tx . serialize_without_witness ( ) ) , 64 )
assert_equal ( MIN_STANDARD_TX_NONWITNESS_SIZE - 1 , 64 )
assert_greater_than ( len ( tx . serialize ( ) ) , 64 )
self . check_mempool_result (
result_expected = [ { ' txid ' : tx . rehash ( ) , ' allowed ' : False , ' reject-reason ' : ' tx-size-small ' } ] ,
rawtxs = [ tx . serialize ( ) . hex ( ) ] ,
maxfeerate = 0 ,
)
self . log . info ( ' Minimally-small transaction(in non-witness bytes) that is allowed ' )
tx . vout [ 0 ] = CTxOut ( COIN - 1000 , DUMMY_MIN_OP_RETURN_SCRIPT )
assert_equal ( len ( tx . serialize_without_witness ( ) ) , MIN_STANDARD_TX_NONWITNESS_SIZE )
self . check_mempool_result (
result_expected = [ { ' txid ' : tx . rehash ( ) , ' allowed ' : True , ' vsize ' : tx . get_vsize ( ) , ' fees ' : { ' base ' : Decimal ( ' 0.00001000 ' ) } } ] ,
rawtxs = [ tx . serialize ( ) . hex ( ) ] ,
maxfeerate = 0 ,
)
2017-11-17 14:54:39 -03:00
2024-06-27 14:45:01 -04:00
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 ,
)
2024-07-31 11:21:01 -04:00
# 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 ( ) )
2024-06-27 14:45:01 -04:00
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 ( ) ] )
2023-12-20 10:58:53 -03:00
self . log . info ( ' Spending a confirmed bare multisig is okay ' )
address = self . wallet . get_address ( )
tx = tx_from_hex ( raw_tx_reference )
privkey , pubkey = generate_keypair ( )
tx . vout [ 0 ] . scriptPubKey = keys_to_multisig_script ( [ pubkey ] * 3 , k = 1 ) # Some bare multisig script (1-of-3)
tx . rehash ( )
self . generateblock ( node , address , [ tx . serialize ( ) . hex ( ) ] )
tx_spend = CTransaction ( )
tx_spend . vin . append ( CTxIn ( COutPoint ( tx . sha256 , 0 ) , b " " ) )
tx_spend . vout . append ( CTxOut ( tx . vout [ 0 ] . nValue - int ( fee * COIN ) , script_to_p2wsh_script ( CScript ( [ OP_TRUE ] ) ) ) )
tx_spend . rehash ( )
sign_input_legacy ( tx_spend , 0 , tx . vout [ 0 ] . scriptPubKey , privkey , sighash_type = SIGHASH_ALL )
tx_spend . vin [ 0 ] . scriptSig = bytes ( CScript ( [ OP_0 ] ) ) + tx_spend . vin [ 0 ] . scriptSig
self . check_mempool_result (
result_expected = [ { ' txid ' : tx_spend . rehash ( ) , ' allowed ' : True , ' vsize ' : tx_spend . get_vsize ( ) , ' fees ' : { ' base ' : Decimal ( ' 0.00000700 ' ) } } ] ,
rawtxs = [ tx_spend . serialize ( ) . hex ( ) ] ,
maxfeerate = 0 ,
)
2017-11-17 14:54:39 -03:00
if __name__ == ' __main__ ' :
2024-07-16 17:05:14 -04:00
MempoolAcceptanceTest ( __file__ ) . main ( )