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.
751#752# Merkle tree malleability (CVE-2012-2459): repeating sequences of transactions in a block without753# affecting the merkle root of a block, while still invalidating it.754# See: src/consensus/merkle.h755#756# b57 has three txns: coinbase, tx, tx1. The merkle root computation will duplicate tx.757# Result: OK758#759# b56 copies b57 but duplicates tx1 and does not recalculate the block hash. So it has a valid merkle760# root but duplicate transactions.761# Result: Fails762#763# b57p2 has six transactions in its merkle tree:764# - coinbase, tx, tx1, tx2, tx3, tx4765# Merkle root calculation will duplicate as necessary.766# Result: OK.767#768# b56p2 copies b57p2 but adds both tx3 and tx4. The purpose of the test is to make sure the code catches769# duplicate txns that are not next to one another with the "bad-txns-duplicate" error (which indicates770# that the error was caught early, avoiding a DOS vulnerability.)772# b57 - a good block with 2 txs, don't submit until end773self.move_tip(55)774self.next_block(57)775tx = self.create_and_sign_transaction(out[16], 1)776tx1 = self.create_tx(tx, 0, 1)777b57 = self.update_block(57, [tx, tx1])779# b56 - copy b57, add a duplicate tx780self.log.info("Reject a block with a duplicate transaction in the Merkle Tree (but with a valid Merkle Root)")781self.move_tip(55)782b56 = copy.deepcopy(b57)783self.blocks[56] = b56784assert_equal(len(b56.vtx), 3)785b56 = self.update_block(56, [tx1])786assert_equal(b56.hash_hex, b57.hash_hex)787self.send_blocks([b56], success=False, reject_reason='bad-txns-duplicate', reconnect=True)789# b57p2 - a good block with 6 tx'es, don't submit until end790self.move_tip(55)791self.next_block("57p2")792tx = self.create_and_sign_transaction(out[16], 1)793tx1 = self.create_tx(tx, 0, 1)794tx2 = self.create_tx(tx1, 0, 1)795tx3 = self.create_tx(tx2, 0, 1)796tx4 = self.create_tx(tx3, 0, 1)797b57p2 = self.update_block("57p2", [tx, tx1, tx2, tx3, tx4])799# b56p2 - copy b57p2, duplicate two non-consecutive tx's800self.log.info("Reject a block with two duplicate transactions in the Merkle Tree (but with a valid Merkle Root)")801self.move_tip(55)802b56p2 = copy.deepcopy(b57p2)803self.blocks["b56p2"] = b56p2804assert_equal(b56p2.hash_hex, b57p2.hash_hex)805assert_equal(len(b56p2.vtx), 6)806b56p2 = self.update_block("b56p2", [tx3, tx4])807self.send_blocks([b56p2], success=False, reject_reason='bad-txns-duplicate', reconnect=True)809self.move_tip("57p2")810self.send_blocks([b57p2], True)812self.move_tip(57)813self.send_blocks([b57], False) # The tip is not updated because 57p2 seen first814self.save_spendable_output()816# Test a few invalid tx types817#818# -> b35 (10) -> b39 (11) -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 ()819# \-> ??? (17)820#822# tx with prevout.n out of range823self.log.info("Reject a block with a transaction with prevout.n out of range")824self.move_tip(57)825self.next_block(58, spend=out[17])826tx = CTransaction()827assert len(out[17].vout) < 42828tx.vin.append(CTxIn(COutPoint(out[17].txid_int, 42), CScript([OP_TRUE]), SEQUENCE_FINAL))829tx.vout.append(CTxOut(0, b""))830b58 = self.update_block(58, [tx])831self.send_blocks([b58], success=False, reject_reason='bad-txns-inputs-missingorspent', reconnect=True)833# tx with output value > input value834self.log.info("Reject a block with a transaction with outputs > inputs")835self.move_tip(57)836self.next_block(59)837tx = self.create_and_sign_transaction(out[17], 51 * COIN)838b59 = self.update_block(59, [tx])839self.send_blocks([b59], success=False, reject_reason='bad-txns-in-belowout', reconnect=True)841# reset to good chain842self.move_tip(57)843b60 = self.next_block(60)844self.send_blocks([b60], True)845self.save_spendable_output()847# Test BIP30 (reject duplicate)848#849# -> b39 (11) -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 ()850# \-> b61 ()851#852# Blocks are not allowed to contain a transaction whose id matches that of an earlier,853# not-fully-spent transaction in the same chain. To test, make identical coinbases;854# the second one should be rejected. See also CVE-2012-1909.855#856self.log.info("Reject a block with a transaction with a duplicate hash of a previous transaction (BIP30)")857self.move_tip(60)858b61 = self.next_block(61)859b61.vtx[0].nLockTime = 0860b61.vtx[0].vin[0].scriptSig = DUPLICATE_COINBASE_SCRIPT_SIG861b61 = self.update_block(61, [])862assert_equal(duplicate_tx.serialize(), b61.vtx[0].serialize())863# BIP30 is always checked on regtest, regardless of the BIP34 activation height864self.send_blocks([b61], success=False, reject_reason='bad-txns-BIP30', reconnect=True)866# Test BIP30 (allow duplicate if spent)867#868# -> b57 (16) -> b60 ()869# \-> b_spend_dup_cb (b_dup_cb) -> b_dup_2 ()870#871self.move_tip(57)872self.next_block('spend_dup_cb')873tx = CTransaction()874tx.vin.append(CTxIn(COutPoint(duplicate_tx.txid_int, 0)))875tx.vout.append(CTxOut(0, CScript([OP_TRUE])))876self.sign_tx(tx, duplicate_tx)877b_spend_dup_cb = self.update_block('spend_dup_cb', [tx])879b_dup_2 = self.next_block('dup_2')880b_dup_2.vtx[0].nLockTime = 0881b_dup_2.vtx[0].vin[0].scriptSig = DUPLICATE_COINBASE_SCRIPT_SIG882b_dup_2 = self.update_block('dup_2', [])883assert_equal(duplicate_tx.serialize(), b_dup_2.vtx[0].serialize())884assert_equal(self.nodes[0].gettxout(txid=duplicate_tx.txid_hex, n=0)['confirmations'], 119)885self.send_blocks([b_spend_dup_cb, b_dup_2], success=True)886# The duplicate has less confirmations887assert_equal(self.nodes[0].gettxout(txid=duplicate_tx.txid_hex, n=0)['confirmations'], 1)889# Test tx.isFinal is properly rejected (not an exhaustive tx.isFinal test, that should be in data-driven transaction tests)890#891# -> b_spend_dup_cb (b_dup_cb) -> b_dup_2 ()892# \-> b62 (18)893#894self.log.info("Reject a block with a transaction with a nonfinal locktime")895self.move_tip('dup_2')896self.next_block(62)897tx = CTransaction()898tx.nLockTime = 0xffffffff # this locktime is non-final899tx.vin.append(CTxIn(COutPoint(out[18].txid_int, 0))) # don't set nSequence900tx.vout.append(CTxOut(0, CScript([OP_TRUE])))901assert_greater_than(SEQUENCE_FINAL, tx.vin[0].nSequence)902b62 = self.update_block(62, [tx])903self.send_blocks([b62], success=False, reject_reason='bad-txns-nonfinal', reconnect=True)905# Test a non-final coinbase is also rejected906#907# -> b_spend_dup_cb (b_dup_cb) -> b_dup_2 ()908# \-> b63 (-)909#910self.log.info("Reject a block with a coinbase transaction with a nonfinal locktime")911self.move_tip('dup_2')912b63 = self.next_block(63)913b63.vtx[0].nLockTime = 0xffffffff914b63.vtx[0].vin[0].nSequence = 0xDEADBEEF915b63 = self.update_block(63, [])916self.send_blocks([b63], success=False, reject_reason='bad-txns-nonfinal', reconnect=True)918# This checks that a block with a bloated VARINT between the block_header and the array of tx such that919# the block is > MAX_BLOCK_WEIGHT with the bloated varint, but <= MAX_BLOCK_WEIGHT without the bloated varint,920# does not cause a subsequent, identical block with canonical encoding to be rejected. The test does not921# care whether the bloated block is accepted or rejected; it only cares that the second block is accepted.922#923# What matters is that the receiving node should not reject the bloated block, and then reject the canonical924# block on the basis that it's the same as an already-rejected block (which would be a consensus failure.)925#926# -> b_spend_dup_cb (b_dup_cb) -> b_dup_2 () -> b64 (18)927# \928# b64a (18)929# b64a is a bloated block (non-canonical varint)930# b64 is a good block (same as b64 but w/ canonical varint)931#932self.log.info("Accept a valid block even if a bloated version of the block has previously been sent")933self.move_tip('dup_2')934regular_block = self.next_block("64a", spend=out[18])936# make it a "broken_block," with non-canonical serialization937b64a = CBrokenBlock(regular_block)938b64a.initialize(regular_block)939self.blocks["64a"] = b64a940self.tip = b64a941tx = CTransaction()943# use canonical serialization to calculate size944script_length = (MAX_BLOCK_WEIGHT - 4 * len(b64a.normal_serialize()) - 276) // 4945script_output = CScript([b'\x00' * script_length])946tx.vout.append(CTxOut(0, script_output))947tx.vin.append(CTxIn(COutPoint(b64a.vtx[1].txid_int, 0)))948b64a = self.update_block("64a", [tx])949assert_equal(b64a.get_weight(), MAX_BLOCK_WEIGHT + 8 * 4)950self.send_blocks([b64a], success=False, reject_reason='non-canonical ReadCompactSize()')952# bitcoind doesn't disconnect us for sending a bloated block, but if we subsequently953# resend the header message, it won't send us the getdata message again. Just954# disconnect and reconnect and then call sync_blocks.955# TODO: improve this test to be less dependent on P2P DOS behaviour.956node.disconnect_p2ps()957self.reconnect_p2p()959self.move_tip('dup_2')960b64 = CBlock(b64a)961b64.vtx = copy.deepcopy(b64a.vtx)962assert_equal(b64.hash_hex, b64a.hash_hex)963assert_equal(b64.get_weight(), MAX_BLOCK_WEIGHT)964self.blocks[64] = b64965b64 = self.update_block(64, [])966self.send_blocks([b64], True)967self.save_spendable_output()969# Spend an output created in the block itself970#971# -> b_dup_2 () -> b64 (18) -> b65 (19)972#973self.log.info("Accept a block with a transaction spending an output created in the same block")974self.move_tip(64)975self.next_block(65)976tx1 = self.create_and_sign_transaction(out[19], out[19].vout[0].nValue)977tx2 = self.create_and_sign_transaction(tx1, 0)978b65 = self.update_block(65, [tx1, tx2])979self.send_blocks([b65], True)980self.save_spendable_output()982# Attempt to spend an output created later in the same block983#984# -> b64 (18) -> b65 (19)985# \-> b66 (20)986self.log.info("Reject a block with a transaction spending an output created later in the same block")987self.move_tip(65)988self.next_block(66)989tx1 = self.create_and_sign_transaction(out[20], out[20].vout[0].nValue)990tx2 = self.create_and_sign_transaction(tx1, 1)991b66 = self.update_block(66, [tx2, tx1])992self.send_blocks([b66], success=False, reject_reason='bad-txns-inputs-missingorspent', reconnect=True)994# Attempt to double-spend a transaction created in a block995#996# -> b64 (18) -> b65 (19)997# \-> b67 (20)998#999#1000self.log.info("Reject a block with a transaction double spending a transaction created in the same block")
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.