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.
502current_block_height = self.nodes[0].getchaintips()[0]["height"]503check_opts = self.capnp_modules['mining'].BlockCheckOptions()505# Send a real transaction so the template includes it.506self.miniwallet.send_self_transfer(fee_rate=10, from_node=self.nodes[0])508async with destroying((await mining.createNewBlock(ctx, self.default_block_create_options)).result, ctx) as template:509block = await self.build_candidate_block(template, ctx)510coinbase = block.vtx[0]511self.log.debug("Template should include a mempool transaction")512assert len(block.vtx) >= 2, "Block should include at least the coinbase and the mempool tx"513balance = self.miniwallet.get_balance()514original_version = block.nVersion516self.log.debug("Disconnect node 2 before block submission tests")517# The default topology is 2 -> 1 -> 0. Splitting the 1-2 edge518# lets node 2 accept/reject complete blocks independently.519self.disconnect_nodes(1, 2)521self.log.debug("submitSolution should reject an empty coinbase")522submitted = (await template.submitSolution(ctx, 0, 0, 0, b"")).result523assert_equal(submitted, False)525self.log.debug("Submit solution that can't be deserialized")526try:527await template.submitSolution(ctx, 0, 0, 0, b"\x00")528raise AssertionError("submitSolution unexpectedly succeeded")529except capnp.lib.capnp.KjException as e:530assert_capnp_failed(e, "remote exception: std::exception: SpanReader::read(): end of data:")532self.log.debug("Submit a block with a bad version")533block.nVersion = 0534block.solve()535check = await mining.checkBlock(ctx, block.serialize(), check_opts)536assert_equal(check.result, False)537assert_equal(check.reason, "bad-version(0x00000000)")538assert_equal(check.debug, "rejected nVersion=0x00000000 block")539self.log.debug("submitSolution should reject a bad-version block")540result = await template.submitSolution(ctx, block.nVersion, block.nTime, block.nNonce, coinbase.serialize())541assert_equal(result.result, False)542assert_equal(result.reason, "bad-version(0x00000000)")543assert_equal(result.debug, "rejected nVersion=0x00000000 block")544self.log.debug("submitBlock should reject a bad-version block")545await self.assert_submit_block(546mining2,547ctx2,548block,549result=False,550reason="bad-version(0x00000000)",551debug="rejected nVersion=0x00000000 block",552)553self.log.debug("Submit a valid block")554block.nVersion = original_version555block.solve()557self.log.debug("First call checkBlock()")558block_valid = (await mining.checkBlock(ctx, block.serialize(), check_opts)).result559assert_equal(block_valid, True)561# The remote template block will be mutated, capture the original:562remote_block_before = await mining_get_block(template, ctx)564self.log.debug("Submitted coinbase must include witness")565assert_not_equal(coinbase.serialize_without_witness().hex(), coinbase.serialize().hex())566missing_witness_block = deepcopy(block)567missing_witness_block.vtx[0].wit.vtxinwit = []568has_witness_commitment = any(569len(o.scriptPubKey) >= 38 and o.scriptPubKey[2:6] == WITNESS_COMMITMENT_HEADER570for o in missing_witness_block.vtx[0].vout571)572assert has_witness_commitment, "Coinbase should have a witness commitment output"573missing_witness_block.hashMerkleRoot = missing_witness_block.calc_merkle_root()574missing_witness_block.solve()575self.log.debug("submitSolution should reject a coinbase missing witness")576result = await template.submitSolution(ctx, block.nVersion, block.nTime, block.nNonce, coinbase.serialize_without_witness())577assert_equal(result.result, False)578assert_equal(result.reason, "bad-witness-nonce-size")579assert_equal(result.debug, "CheckWitnessMalleation : invalid witness reserved value size")581self.log.debug("Even a rejected submitSolution() mutates the template's block")582# Can be used by clients to download and inspect the (rejected)583# reconstructed block.584remote_block_after = await mining_get_block(template, ctx)585assert_not_equal(remote_block_before.serialize().hex(), remote_block_after.serialize().hex())587self.log.debug("submitBlock should reject a block missing coinbase witness")588await self.assert_submit_block(589mining2,590ctx2,591missing_witness_block,592result=False,593reason="bad-witness-nonce-size",594debug="CheckWitnessMalleation : invalid witness reserved value size",595)597self.log.debug("Submit again, with the witness")598result = await template.submitSolution(ctx, block.nVersion, block.nTime, block.nNonce, coinbase.serialize())599assert_equal(result.result, True)600assert_equal(result.reason, "")601assert_equal(result.debug, "")603self.log.debug("Submit a valid complete block through the disconnected node")604await self.assert_submit_block(mining2, ctx2, block, result=True)605assert_equal(self.nodes[2].getchaintips()[0]["height"], current_block_height + 1)606self.log.debug("submitBlock should reject the duplicate complete block")607await self.assert_submit_block(mining2, ctx2, block, result=False, reason="duplicate")609self.log.debug("Block should propagate")610# Check that the IPC node actually updates its own chain611assert_equal(self.nodes[0].getchaintips()[0]["height"], current_block_height + 1)612# Stalls if a regression causes submitSolution() to accept an invalid block:613self.sync_blocks(self.nodes[:2])614# Rejoin node 2 and verify the submitBlock result converges with615# the submitSolution result from node 0.616self.connect_nodes(1, 2)617self.sync_all()618# Check that the other node accepts the block619assert_equal(self.nodes[0].getchaintips()[0], self.nodes[1].getchaintips()[0])621self.miniwallet.rescan_utxos()622assert_equal(self.miniwallet.get_balance(), balance + 1)623self.log.debug("Check block should fail now, since it is a duplicate")624check = await mining.checkBlock(ctx, block.serialize(), check_opts)625assert_equal(check.result, False)626assert_equal(check.reason, "inconclusive-not-best-prevblk")627self.log.debug("submitBlock on the same node should fail with duplicate after submitSolution succeeds")628await self.assert_submit_block(mining, ctx, block, result=False, reason="duplicate")630self.log.debug("submitSolution should return duplicate after submitBlock succeeds")631async with destroying((await mining2.createNewBlock(ctx2, self.default_block_create_options)).result, ctx2) as template2:632duplicate_block = await self.build_candidate_block(template2, ctx2)633duplicate_coinbase = duplicate_block.vtx[0]634duplicate_block.solve()635self.log.debug("Submit a valid complete block before duplicate submitSolution")636await self.assert_submit_block(mining2, ctx2, duplicate_block, result=True)637self.nodes[2].waitforblockheight(current_block_height + 2)638self.log.debug("submitSolution should reject the duplicate block")639result = await template2.submitSolution(ctx2, duplicate_block.nVersion, duplicate_block.nTime, duplicate_block.nNonce, duplicate_coinbase.serialize())640assert_equal(result.result, False)641assert_equal(result.reason, "duplicate")642assert_equal(result.debug, "")643self.sync_all()645self.log.debug(646"submitSolution and submitBlock should report inconclusive for valid stale blocks")647async with AsyncExitStack() as stack:648active_template = await mining_create_block_template(649mining2, stack, ctx2, self.default_block_create_options)650solution_template = await mining_create_block_template(651mining2, stack, ctx2, self.default_block_create_options)652submit_block_template = await mining_create_block_template(653mining2, stack, ctx2, self.default_block_create_options)654assert active_template is not None655assert solution_template is not None656assert submit_block_template is not None658active_block = await self.build_candidate_block(659active_template, ctx2, extra_nonce=b"\x01")660solution_block = await self.build_candidate_block(661solution_template, ctx2, extra_nonce=b"\x02")662submit_block = await self.build_candidate_block(663submit_block_template, ctx2, extra_nonce=b"\x03")664active_block.solve()665solution_block.solve()666submit_block.solve()668# All three templates share a parent. The first block becomes669# active, so the remaining valid blocks are accepted as stale.670await self.assert_submit_block(671mining2, ctx2, active_block, result=True)673solution_coinbase = solution_block.vtx[0]674result = await solution_template.submitSolution(675ctx2,676solution_block.nVersion,677solution_block.nTime,678solution_block.nNonce,679solution_coinbase.serialize(),680)681assert_equal(result.result, False)682assert_equal(result.reason, "inconclusive")683assert_equal(result.debug, "")685await self.assert_submit_block(686mining2, ctx2, submit_block, result=False, reason="inconclusive")687self.sync_all()689self.log.debug("Submit the same invalid block twice")690async with destroying((await mining2.createNewBlock(ctx2, self.default_block_create_options)).result, ctx2) as template2:691invalid_block = await self.build_candidate_block(template2, ctx2)692invalid_block.vtx[0].nLockTime = 2**32 - 1693invalid_block.hashMerkleRoot = invalid_block.calc_merkle_root()694invalid_block.solve()695self.log.debug("submitBlock should reject the non-final block")696await self.assert_submit_block(697mining2,698ctx2,699invalid_block,700result=False,701reason="bad-txns-nonfinal",702debug="non-final transaction",703)704self.log.debug("submitBlock should report duplicate-invalid for the same block")705await self.assert_submit_block(706mining2,707ctx2,708invalid_block,709result=False,710reason="duplicate-invalid",711debug=f"block {invalid_block.hash_hex} was previously marked invalid",712)714self.log.debug("Submit a malformed complete block")715try:716await mining2.submitBlock(ctx2, block.serialize()[:-15])717raise AssertionError("submitBlock unexpectedly succeeded")718except capnp.lib.capnp.KjException as e:719assert_capnp_failed(e, "remote exception: std::exception: SpanReader::read(): end of data:")721self.log.debug("Submit empty block data")722try:723await mining2.submitBlock(ctx2, b"")724raise AssertionError("submitBlock unexpectedly succeeded")725except capnp.lib.capnp.KjException as e:726assert_capnp_failed(e, "remote exception: std::exception: SpanReader::read(): end of data:")727assert_equal(self.nodes[2].is_node_stopped(), False)729asyncio.run(capnp.run(async_routine()))731def run_transaction_lookup_test(self):732"""Test getTransactionsByTxID() and getTransactionsByWitnessID()."""733self.log.info("Running transaction lookup test")735async def async_routine():736ctx, mining = await make_mining_ctx(self)737tx1 = self.miniwallet.send_self_transfer(fee_rate=10, from_node=self.nodes[0])738tx2 = self.miniwallet.send_self_transfer(fee_rate=10, from_node=self.nodes[0], utxo_to_spend=tx1["new_utxo"])740self.log.debug("getTransactionsByTxID() returns mempool txs and nulls")741raw_txs_txid = await mining.getTransactionsByTxID(ctx, [tx1["tx"].txid, tx2["tx"].txid, bytes(32)])742assert_equal(len(raw_txs_txid.result), 3)743assert_equal(raw_txs_txid.result[0].hex(), tx1["hex"])744assert_equal(raw_txs_txid.result[1].hex(), tx2["hex"])745assert_equal(raw_txs_txid.result[2], b'')747self.log.debug("getTransactionsByWitnessID() returns mempool txs and nulls")748raw_txs_wtxid = await mining.getTransactionsByWitnessID(ctx, [tx1["tx"].wtxid, tx2["tx"].wtxid, bytes(32)])749assert_equal(len(raw_txs_wtxid.result), 3)750assert_equal(raw_txs_wtxid.result[0].hex(), tx1["hex"])
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.