Inside Bitcoin
Opening the selected file…
Inside Bitcoin
Opening the selected file…
The code behind the network, with room to understand it.
Release published 2026-10-28 · Snapshot fetched 2026-11-01. Latest stable release at fetch time; development branches and Git history are excluded. Only the selected code window and visible folders load here.
1#!/usr/bin/env python32# Copyright (c) 2017-present The Bitcoin Core developers3# Distributed under the MIT software license, see the accompanying4# file COPYING or http://www.opensource.org/licenses/mit-license.php.5"""Test mempool acceptance of raw transactions."""7from copy import deepcopy8from decimal import Decimal9import math11from test_framework.test_framework import BitcoinTestFramework12from test_framework.blocktools import MAX_STANDARD_TX_WEIGHT13from test_framework.mempool_util import (14DEFAULT_MIN_RELAY_TX_FEE,15DEFAULT_INCREMENTAL_RELAY_FEE,16)17from test_framework.messages import (18MAX_BIP125_RBF_SEQUENCE,19COIN,20COutPoint,21CTransaction,22CTxIn,23CTxInWitness,24CTxOut,25MAX_BLOCK_WEIGHT,26WITNESS_SCALE_FACTOR,27MAX_MONEY,28SEQUENCE_FINAL,29tx_from_hex,30)31from test_framework.script import (32CScript,33OP_0,34OP_HASH160,35OP_RETURN,36OP_TRUE,37SIGHASH_ALL,38sign_input_legacy,39)40from test_framework.script_util import (41DUMMY_MIN_OP_RETURN_SCRIPT,42keys_to_multisig_script,43MIN_PADDING,44MIN_STANDARD_TX_NONWITNESS_SIZE,45PAY_TO_ANCHOR,46script_to_p2sh_script,47script_to_p2wsh_script,48)49from test_framework.util import (50assert_equal,51assert_greater_than,52assert_raises_rpc_error,53sync_txindex,54)55from test_framework.wallet import MiniWallet56from test_framework.wallet_util import generate_keypair59class MempoolAcceptanceTest(BitcoinTestFramework):60def set_test_params(self):61self.num_nodes = 162self.extra_args = [[63'-txindex','-permitbaremultisig=0',64]] * self.num_nodes65self.supports_cli = False67def check_mempool_result(self, result_expected, *args, **kwargs):68"""Wrapper to check result of testmempoolaccept on node_0's mempool"""69result_test = self.nodes[0].testmempoolaccept(*args, **kwargs)70for r in result_test:71# Skip these checks for now72r.pop('wtxid')73if "fees" in r:74r["fees"].pop("effective-feerate")75r["fees"].pop("effective-includes")76if "reject-details" in r:77r.pop("reject-details")78assert_equal(result_expected, result_test)79assert_equal(self.nodes[0].getmempoolinfo()['size'], self.mempool_size) # Must not change mempool state81def run_test(self):82node = self.nodes[0]83self.wallet = MiniWallet(node)85assert_equal(node.getmempoolinfo()['permitbaremultisig'], False)87self.log.info('Start with empty mempool, and 200 blocks')88self.mempool_size = 089assert_equal(node.getblockcount(), 200)90assert_equal(node.getmempoolinfo()['size'], self.mempool_size)92self.log.info("Check default settings")93# Settings are listed in BTC/kvB94assert_equal(node.getmempoolinfo()['minrelaytxfee'], Decimal(DEFAULT_MIN_RELAY_TX_FEE) / COIN)95assert_equal(node.getmempoolinfo()['incrementalrelayfee'], Decimal(DEFAULT_INCREMENTAL_RELAY_FEE) / COIN)97self.log.info('Should not accept garbage to testmempoolaccept')98assert_raises_rpc_error(-3, 'JSON value of type string is not of expected type array', lambda: node.testmempoolaccept(rawtxs='ff00baar'))99assert_raises_rpc_error(-8, 'Array must contain between 1 and 25 transactions.', lambda: node.testmempoolaccept(rawtxs=['ff22']*26))100assert_raises_rpc_error(-8, 'Array must contain between 1 and 25 transactions.', lambda: node.testmempoolaccept(rawtxs=[]))101assert_raises_rpc_error(-22, 'TX decode failed', lambda: node.testmempoolaccept(rawtxs=['ff00baar']))103self.log.info('A transaction already in the blockchain')104tx = self.wallet.create_self_transfer()['tx'] # Pick a random coin(base) to spend105tx.vout.append(deepcopy(tx.vout[0]))106tx.vout[0].nValue = int(0.3 * COIN)107tx.vout[1].nValue = int(49 * COIN)108raw_tx_in_block = tx.serialize().hex()109txid_in_block = self.wallet.sendrawtransaction(from_node=node, tx_hex=raw_tx_in_block)110self.generate(node, 1)111self.mempool_size = 0112# Also check feerate. 1BTC/kvB fails113assert_raises_rpc_error(-8, "Fee rates larger than or equal to 1BTC/kvB are not accepted", lambda: self.check_mempool_result(114result_expected=None,115rawtxs=[raw_tx_in_block],116maxfeerate=1,117))118# Check negative feerate119assert_raises_rpc_error(-3, "Amount out of range", lambda: self.check_mempool_result(120result_expected=None,121rawtxs=[raw_tx_in_block],122maxfeerate=-0.01,123))124# ... 0.99 passes125self.check_mempool_result(126result_expected=[{'txid': txid_in_block, 'allowed': False, 'reject-reason': 'txn-already-known'}],127rawtxs=[raw_tx_in_block],128maxfeerate=0.99,129)131self.log.info('A transaction not in the mempool')132fee = Decimal('0.000007')133utxo_to_spend = self.wallet.get_utxo(txid=txid_in_block) # use 0.3 BTC UTXO134tx = self.wallet.create_self_transfer(utxo_to_spend=utxo_to_spend, sequence=MAX_BIP125_RBF_SEQUENCE)['tx']135tx.vout[0].nValue = int((Decimal('0.3') - fee) * COIN)136raw_tx_0 = tx.serialize().hex()137txid_0 = tx.txid_hex138self.check_mempool_result(139result_expected=[{'txid': txid_0, 'allowed': True, 'vsize_adjusted': tx.get_vsize(), 'vsize': tx.get_vsize(), 'vsize_bip141': tx.get_vsize(),'fees': {'base': fee}}],140rawtxs=[raw_tx_0],141)143self.log.info('A final transaction not in the mempool')144output_amount = Decimal('0.025')145tx = self.wallet.create_self_transfer(146sequence=SEQUENCE_FINAL,147locktime=node.getblockcount() + 2000, # Can be anything148)['tx']149tx.vout[0].nValue = int(output_amount * COIN)150raw_tx_final = tx.serialize().hex()151tx = tx_from_hex(raw_tx_final)152fee_expected = Decimal('50.0') - output_amount153self.check_mempool_result(154result_expected=[{'txid': tx.txid_hex, 'allowed': True, 'vsize_adjusted': tx.get_vsize(), 'vsize': tx.get_vsize(), 'vsize_bip141': tx.get_vsize(), 'fees': {'base': fee_expected}}],155rawtxs=[tx.serialize().hex()],156maxfeerate=0,157)158node.sendrawtransaction(hexstring=raw_tx_final, maxfeerate=0)159self.mempool_size += 1161self.log.info('A transaction in the mempool')162node.sendrawtransaction(hexstring=raw_tx_0)163self.mempool_size += 1164self.check_mempool_result(165result_expected=[{'txid': txid_0, 'allowed': False, 'reject-reason': 'txn-already-in-mempool'}],166rawtxs=[raw_tx_0],167)169self.log.info('A transaction that replaces a mempool transaction')170tx = tx_from_hex(raw_tx_0)171tx.vout[0].nValue -= int(fee * COIN) # Double the fee172raw_tx_0 = tx.serialize().hex()173txid_0 = tx.txid_hex174self.check_mempool_result(175result_expected=[{'txid': txid_0, 'allowed': True, 'vsize_adjusted': tx.get_vsize(), 'vsize': tx.get_vsize(), 'vsize_bip141': tx.get_vsize(), 'fees': {'base': (2 * fee)}}],176rawtxs=[raw_tx_0],177)178node.sendrawtransaction(hexstring=tx.serialize().hex(), maxfeerate=0)180self.log.info('A transaction with missing inputs, that never existed')181tx = tx_from_hex(raw_tx_0)182tx.vin[0].prevout = COutPoint(hash=int('ff' * 32, 16), n=14)183self.check_mempool_result(184result_expected=[{'txid': tx.txid_hex, 'allowed': False, 'reject-reason': 'missing-inputs'}],185rawtxs=[tx.serialize().hex()],186)188self.log.info('A transaction with missing inputs, that existed once in the past')189tx = tx_from_hex(raw_tx_0)190tx.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 spend191raw_tx_1 = tx.serialize().hex()192txid_1 = node.sendrawtransaction(hexstring=raw_tx_1, maxfeerate=0)193# Now spend both to "clearly hide" the outputs, ie. remove the coins from the utxo set by spending them194tx = self.wallet.create_self_transfer()['tx']195tx.vin.append(deepcopy(tx.vin[0]))196tx.wit.vtxinwit.append(deepcopy(tx.wit.vtxinwit[0]))197tx.vin[0].prevout = COutPoint(hash=int(txid_0, 16), n=0)198tx.vin[1].prevout = COutPoint(hash=int(txid_1, 16), n=0)199tx.vout[0].nValue = int(0.1 * COIN)200raw_tx_spend_both = tx.serialize().hex()201txid_spend_both = self.wallet.sendrawtransaction(from_node=node, tx_hex=raw_tx_spend_both)202self.generate(node, 1)203self.mempool_size = 0204# Now see if we can add the coins back to the utxo set by sending the exact txs again205self.check_mempool_result(206result_expected=[{'txid': txid_0, 'allowed': False, 'reject-reason': 'missing-inputs'}],207rawtxs=[raw_tx_0],208)209self.check_mempool_result(210result_expected=[{'txid': txid_1, 'allowed': False, 'reject-reason': 'missing-inputs'}],211rawtxs=[raw_tx_1],212)214self.log.info('Create a "reference" tx for later use')215utxo_to_spend = self.wallet.get_utxo(txid=txid_spend_both)216tx = self.wallet.create_self_transfer(utxo_to_spend=utxo_to_spend, sequence=SEQUENCE_FINAL)['tx']217tx.vout[0].nValue = int(0.05 * COIN)218raw_tx_reference = tx.serialize().hex()219# Reference tx should be valid on itself220self.check_mempool_result(221result_expected=[{'txid': tx.txid_hex, 'allowed': True, 'vsize_adjusted': tx.get_vsize(), 'vsize': tx.get_vsize(), 'vsize_bip141': tx.get_vsize(), 'fees': { 'base': Decimal('0.1') - Decimal('0.05')}}],222rawtxs=[tx.serialize().hex()],223maxfeerate=0,224)226self.log.info('A transaction with no outputs')227tx = tx_from_hex(raw_tx_reference)228tx.vout = []229self.check_mempool_result(230result_expected=[{'txid': tx.txid_hex, 'allowed': False, 'reject-reason': 'bad-txns-vout-empty'}],231rawtxs=[tx.serialize().hex()],232)234self.log.info('A really large transaction')235tx = tx_from_hex(raw_tx_reference)236tx.vin = [tx.vin[0]] * math.ceil((MAX_BLOCK_WEIGHT // WITNESS_SCALE_FACTOR) / len(tx.vin[0].serialize()))237self.check_mempool_result(238result_expected=[{'txid': tx.txid_hex, 'allowed': False, 'reject-reason': 'bad-txns-oversize'}],239rawtxs=[tx.serialize().hex()],240)242self.log.info('A transaction with negative output value')243tx = tx_from_hex(raw_tx_reference)244tx.vout[0].nValue *= -1245self.check_mempool_result(246result_expected=[{'txid': tx.txid_hex, 'allowed': False, 'reject-reason': 'bad-txns-vout-negative'}],247rawtxs=[tx.serialize().hex()],248)250# The following two validations prevent overflow of the output amounts (see CVE-2010-5139).
Independent explanations by MyBitcoinExplorer. Not affiliated with or endorsed by Bitcoin Core. Guides are educational and do not replace the source or its tests.
Bitcoin Core source © its contributors, distributed under the MIT license. Original notices and bundled third-party licenses are retained in the source tree.