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.
501# p2sh_script = OP_HASH160, ripemd160(sha256(script)), OP_EQUAL502#503self.log.info("Check P2SH SIGOPS are correctly counted")504self.move_tip(35)505self.next_block(39)506b39_outputs = 0507b39_sigops_per_output = 6509# Build the redeem script, hash it, use hash to create the p2sh script510redeem_script = CScript([self.coinbase_pubkey] + [OP_2DUP, OP_CHECKSIGVERIFY] * 5 + [OP_CHECKSIG])511p2sh_script = script_to_p2sh_script(redeem_script)513# Create a transaction that spends one satoshi to the p2sh_script, the rest to OP_TRUE514# This must be signed because it is spending a coinbase515spend = out[11]516tx = self.create_tx(spend, 0, 1, p2sh_script)517tx.vout.append(CTxOut(spend.vout[0].nValue - 1, CScript([OP_TRUE])))518self.sign_tx(tx, spend)519b39 = self.update_block(39, [tx])520b39_outputs += 1522# Until block is full, add tx's with 1 satoshi to p2sh_script, the rest to OP_TRUE523tx_new = None524tx_last = tx525total_weight = b39.get_weight()526while total_weight < MAX_BLOCK_WEIGHT:527tx_new = self.create_tx(tx_last, 1, 1, p2sh_script)528tx_new.vout.append(CTxOut(tx_last.vout[1].nValue - 1, CScript([OP_TRUE])))529total_weight += tx_new.get_weight()530if total_weight >= MAX_BLOCK_WEIGHT:531break532b39.vtx.append(tx_new) # add tx to block533tx_last = tx_new534b39_outputs += 1536# The accounting in the loop above can be off, because it misses the537# compact size encoding of the number of transactions in the block.538# Make sure we didn't accidentally make too big a block. Note that the539# size of the block has non-determinism due to the ECDSA signature in540# the first transaction.541while b39.get_weight() >= MAX_BLOCK_WEIGHT:542del b39.vtx[-1]544b39 = self.update_block(39, [])545self.send_blocks([b39], True)546self.save_spendable_output()548# Test sigops in P2SH redeem scripts549#550# b40 creates 3333 tx's spending the 6-sigop P2SH outputs from b39 for a total of 19998 sigops.551# The first tx has one sigop and then at the end we add 2 more to put us just over the max.552#553# b41 does the same, less one, so it has the maximum sigops permitted.554#555self.log.info("Reject a block with too many P2SH sigops")556self.move_tip(39)557b40 = self.next_block(40, spend=out[12])558sigops = get_legacy_sigopcount_block(b40)559numTxes = (MAX_BLOCK_SIGOPS - sigops) // b39_sigops_per_output560assert_equal(numTxes <= b39_outputs, True)562lastOutpoint = COutPoint(b40.vtx[1].txid_int, 0)563new_txs = []564for i in range(1, numTxes + 1):565tx = CTransaction()566tx.vout.append(CTxOut(1, CScript([OP_TRUE])))567tx.vin.append(CTxIn(lastOutpoint, b''))568# second input is corresponding P2SH output from b39569tx.vin.append(CTxIn(COutPoint(b39.vtx[i].txid_int, 0), b''))570# Note: must pass the redeem_script (not p2sh_script) to the signature hash function571tx.vin[1].scriptSig = CScript([redeem_script])572sign_input_legacy(tx, 1, redeem_script, self.coinbase_key)573new_txs.append(tx)574lastOutpoint = COutPoint(tx.txid_int, 0)576b40_sigops_to_fill = MAX_BLOCK_SIGOPS - (numTxes * b39_sigops_per_output + sigops) + 1577tx = CTransaction()578tx.vin.append(CTxIn(lastOutpoint, b''))579tx.vout.append(CTxOut(1, CScript([OP_CHECKSIG] * b40_sigops_to_fill)))580new_txs.append(tx)581self.update_block(40, new_txs)582self.send_blocks([b40], success=False, reject_reason='bad-blk-sigops', reconnect=True)584# same as b40, but one less sigop585self.log.info("Accept a block with the max number of P2SH sigops")586self.move_tip(39)587b41 = self.next_block(41, spend=None)588self.update_block(41, b40.vtx[1:-1])589b41_sigops_to_fill = b40_sigops_to_fill - 1590tx = CTransaction()591tx.vin.append(CTxIn(lastOutpoint, b''))592tx.vout.append(CTxOut(1, CScript([OP_CHECKSIG] * b41_sigops_to_fill)))593self.update_block(41, [tx])594self.send_blocks([b41], True)596# Fork off of b39 to create a constant base again597#598# b23 (6) -> b30 (7) -> b31 (8) -> b33 (9) -> b35 (10) -> b39 (11) -> b42 (12) -> b43 (13)599# \-> b41 (12)600#601self.move_tip(39)602b42 = self.next_block(42, spend=out[12])603self.save_spendable_output()605b43 = self.next_block(43, spend=out[13])606self.save_spendable_output()607self.send_blocks([b42, b43], True)609# Test a number of really invalid scenarios610#611# -> b31 (8) -> b33 (9) -> b35 (10) -> b39 (11) -> b42 (12) -> b43 (13) -> b44 (14)612# \-> ??? (15)614# The next few blocks are going to be created "by hand" since they'll do funky things, such as having615# the first transaction be non-coinbase, etc. The purpose of b44 is to make sure this works.616self.log.info("Build block 44 manually")617height = self.block_heights[self.tip.hash_int] + 1618coinbase = create_coinbase(height, self.coinbase_pubkey)619b44 = CBlock()620b44.nTime = self.tip.nTime + 1621b44.hashPrevBlock = self.tip.hash_int622b44.nBits = REGTEST_N_BITS623b44.vtx.append(coinbase)624tx = self.create_and_sign_transaction(out[14], 1)625b44.vtx.append(tx)626b44.hashMerkleRoot = b44.calc_merkle_root()627b44.solve()628self.tip = b44629self.block_heights[b44.hash_int] = height630self.blocks[44] = b44631self.send_blocks([b44], True)633self.log.info("Reject a block with a non-coinbase as the first tx")634non_coinbase = self.create_tx(out[15], 0, 1)635b45 = CBlock()636b45.nTime = self.tip.nTime + 1637b45.hashPrevBlock = self.tip.hash_int638b45.nBits = REGTEST_N_BITS639b45.vtx.append(non_coinbase)640b45.hashMerkleRoot = b45.calc_merkle_root()641b45.solve()642self.block_heights[b45.hash_int] = self.block_heights[self.tip.hash_int] + 1643self.tip = b45644self.blocks[45] = b45645self.send_blocks([b45], success=False, reject_reason='bad-cb-missing', reconnect=True)647self.log.info("Reject a block with no transactions")648self.move_tip(44)649b46 = CBlock()650b46.nTime = b44.nTime + 1651b46.hashPrevBlock = b44.hash_int652b46.nBits = REGTEST_N_BITS653b46.vtx = []654b46.hashMerkleRoot = 0655b46.solve()656self.block_heights[b46.hash_int] = self.block_heights[b44.hash_int] + 1657self.tip = b46658assert 46 not in self.blocks659self.blocks[46] = b46660self.send_blocks([b46], success=False, reject_reason='bad-blk-length', reconnect=True)662self.log.info("Reject a block with invalid work")663self.move_tip(44)664b47 = self.next_block(47)665target = uint256_from_compact(b47.nBits)666while b47.hash_int <= target:667# Rehash nonces until an invalid too-high-hash block is found.668b47.nNonce += 1669self.send_blocks([b47], False, force_send=True, reject_reason='high-hash', reconnect=True)671self.log.info("Reject a block with a timestamp >2 hours in the future")672self.move_tip(44)673b48 = self.next_block(48)674b48.nTime = int(time.time()) + 60 * 60 * 3675# Header timestamp has changed. Re-solve the block.676b48.solve()677self.send_blocks([b48], False, force_send=True, reject_reason='time-too-new')679self.log.info("Reject a block with invalid merkle hash")680self.move_tip(44)681b49 = self.next_block(49)682b49.hashMerkleRoot += 1683b49.solve()684self.send_blocks([b49], success=False, reject_reason='bad-txnmrklroot', reconnect=True)686self.log.info("Reject a block with incorrect POW limit")687self.move_tip(44)688b50 = self.next_block(50)689b50.nBits = b50.nBits - 1690b50.solve()691self.send_blocks([b50], False, force_send=True, reject_reason='bad-diffbits', reconnect=True)693self.log.info("Reject a block with two coinbase transactions")694self.move_tip(44)695self.next_block(51)696cb2 = create_coinbase(51, self.coinbase_pubkey)697b51 = self.update_block(51, [cb2])698self.send_blocks([b51], success=False, reject_reason='bad-cb-multiple', reconnect=True)700self.log.info("Reject a block with duplicate transactions")701# Note: txns have to be in the right position in the merkle tree to trigger this error702self.move_tip(44)703b52 = self.next_block(52, spend=out[15])704tx = self.create_tx(b52.vtx[1], 0, 1)705b52 = self.update_block(52, [tx, tx])706self.send_blocks([b52], success=False, reject_reason='bad-txns-duplicate', reconnect=True)708# Test block timestamps709# -> b31 (8) -> b33 (9) -> b35 (10) -> b39 (11) -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15)710# \-> b54 (15)711# -> b44 (14)\-> b48 ()712self.move_tip(43)713b53 = self.next_block(53, spend=out[14])714self.send_blocks([b53], False)715self.save_spendable_output()717self.log.info("Reject a block with timestamp before MedianTimePast")718b54 = self.next_block(54, spend=out[15])719b54.nTime = b35.nTime - 1720b54.solve()721self.send_blocks([b54], False, force_send=True, reject_reason='time-too-old', reconnect=True)723# valid timestamp724self.move_tip(53)725b55 = self.next_block(55, spend=out[15])726self.update_block(55, [], nTime=b35.nTime)727self.send_blocks([b55], True)728self.save_spendable_output()730# The block which was previously rejected because of being "too far(3 hours)" must be accepted 2 hours later.731# The new block is only 1 hour into future now and we must reorg onto to the new longer chain.732# The new bestblock b48p is invalidated manually.733# -> b31 (8) -> b33 (9) -> b35 (10) -> b39 (11) -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15)734# \-> b54 (15)735# -> b44 (14)\-> b48 () -> b48p ()736self.log.info("Accept a previously rejected future block at a later time")737node.setmocktime(int(time.time()) + 2*60*60)738self.move_tip(48)739self.block_heights[b48.hash_int] = self.block_heights[b44.hash_int] + 1 # b48 is a parent of b44740b48p = self.next_block("48p")741self.send_blocks([b48, b48p], success=True) # Reorg to the longer chain742node.invalidateblock(b48p.hash_hex) # mark b48p as invalid743node.setmocktime(0)745# Test Merkle tree malleability746#747# -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15) -> b57p2 (16)748# \-> b57 (16)749# \-> b56p2 (16)750# \-> b56 (16)
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.