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.
252# Check block hex format253response_hex = self.test_rest_request(f"/block/{bb_hash}", req_type=ReqType.HEX, ret_type=RetType.OBJ)254assert_greater_than(int(response_hex.getheader('content-length')), BLOCK_HEADER_SIZE*2)255response_hex_bytes = response_hex.read().strip(b'\n')256assert_equal(response_bytes.hex().encode(), response_hex_bytes)258# Compare with hex block header259response_header_hex = self.test_rest_request(f"/headers/{bb_hash}", req_type=ReqType.HEX, ret_type=RetType.OBJ, query_params={"count": 1})260assert_greater_than(int(response_header_hex.getheader('content-length')), BLOCK_HEADER_SIZE*2)261response_header_hex_bytes = response_header_hex.read(BLOCK_HEADER_SIZE*2)262assert_equal(response_bytes[:BLOCK_HEADER_SIZE].hex().encode(), response_header_hex_bytes)264# Check json format265block_json_obj = self.test_rest_request(f"/block/{bb_hash}")266assert_equal(block_json_obj['hash'], bb_hash)267assert_equal(self.test_rest_request(f"/blockhashbyheight/{block_json_obj['height']}")['blockhash'], bb_hash)269# Check hex/bin format270resp_hex = self.test_rest_request(f"/blockhashbyheight/{block_json_obj['height']}", req_type=ReqType.HEX, ret_type=RetType.OBJ)271assert_equal(resp_hex.read().decode('utf-8').rstrip(), bb_hash)272resp_bytes = self.test_rest_request(f"/blockhashbyheight/{block_json_obj['height']}", req_type=ReqType.BIN, ret_type=RetType.BYTES)273blockhash = resp_bytes[::-1].hex()274assert_equal(blockhash, bb_hash)276# Check invalid blockhashbyheight requests277resp = self.test_rest_request(f"/blockhashbyheight/{INVALID_PARAM}", ret_type=RetType.OBJ, status=400)278assert_equal(resp.read().decode('utf-8').rstrip(), f"Invalid height: {INVALID_PARAM}")279resp = self.test_rest_request("/blockhashbyheight/+1", ret_type=RetType.OBJ, status=400)280assert_equal(resp.read().decode('utf-8').rstrip(), "Invalid height: +1")281resp = self.test_rest_request("/blockhashbyheight/1000000", ret_type=RetType.OBJ, status=404)282assert_equal(resp.read().decode('utf-8').rstrip(), "Block height out of range")283resp = self.test_rest_request("/blockhashbyheight/-1", ret_type=RetType.OBJ, status=400)284assert_equal(resp.read().decode('utf-8').rstrip(), "Invalid height: -1")285self.test_rest_request("/blockhashbyheight/", ret_type=RetType.OBJ, status=400)287# Compare with json block header288json_obj = self.test_rest_request(f"/headers/{bb_hash}", query_params={"count": 1})289assert_equal(len(json_obj), 1) # ensure that there is one header in the json response290assert_equal(json_obj[0]['hash'], bb_hash) # request/response hash should be the same292# Check tolerance for invalid URI (% symbol at the end of the request)293for invalid_uri in [f"/headers/{bb_hash}%", f"/blockfilterheaders/basic/{bb_hash}%"]:294resp = self.test_rest_request(invalid_uri, ret_type=RetType.OBJ, status=400)295assert_equal(resp.read().decode('utf-8').rstrip(), f"Invalid hash: {bb_hash}%")296resp = self.test_rest_request("/mempool/contents.json?%", ret_type=RetType.OBJ, status=200)297assert_equal(resp.read().decode('utf-8').rstrip(), "{}")299# Compare with normal RPC block response300rpc_block_json = self.nodes[0].getblock(bb_hash)301for key in ['hash', 'confirmations', 'height', 'version', 'merkleroot', 'time', 'nonce', 'bits', 'target', 'difficulty', 'chainwork', 'previousblockhash']:302assert_equal(json_obj[0][key], rpc_block_json[key])304# See if we can get 5 headers in one response305self.generate(self.nodes[1], 5)306expected_filter = {307'basic block filter index': {'synced': True, 'best_block_height': 208},308}309self.wait_until(lambda: self.nodes[0].getindexinfo("basic block filter index") == expected_filter)310json_obj = self.test_rest_request(f"/headers/{bb_hash}", query_params={"count": 5})311assert_equal(len(json_obj), 5) # now we should have 5 header objects312json_obj = self.test_rest_request(f"/blockfilterheaders/basic/{bb_hash}", query_params={"count": 5})313first_filter_header = json_obj[0]314assert_equal(len(json_obj), 5) # now we should have 5 filter header objects315json_obj = self.test_rest_request(f"/blockfilter/basic/{bb_hash}")317# Compare with normal RPC blockfilter response318rpc_blockfilter = self.nodes[0].getblockfilter(bb_hash)319assert_equal(first_filter_header, rpc_blockfilter['header'])320assert_equal(json_obj['filter'], rpc_blockfilter['filter'])322# Test blockfilterheaders with an invalid hash and filtertype323resp = self.test_rest_request(f"/blockfilterheaders/{INVALID_PARAM}/{bb_hash}", ret_type=RetType.OBJ, status=400)324assert_equal(resp.read().decode('utf-8').rstrip(), f"Unknown filtertype {INVALID_PARAM}")325resp = self.test_rest_request(f"/blockfilterheaders/basic/{INVALID_PARAM}", ret_type=RetType.OBJ, status=400)326assert_equal(resp.read().decode('utf-8').rstrip(), f"Invalid hash: {INVALID_PARAM}")328# Test number parsing329for num in ['5a', '-5', '0', '2001', '99999999999999999999999999999999999']:330assert_equal(331bytes(f'Header count is invalid or out of acceptable range (1-2000): {num}\r\n', 'ascii'),332self.test_rest_request(f"/headers/{bb_hash}", ret_type=RetType.BYTES, status=400, query_params={"count": num}),333)335self.log.info("Test tx inclusion in the /mempool and /block URIs")337# Make 3 chained txs and mine them on node 1338txs = []339input_txid = txid340for _ in range(3):341utxo_to_spend = self.wallet.get_utxo(txid=input_txid)342txs.append(self.wallet.send_self_transfer(from_node=self.nodes[0], utxo_to_spend=utxo_to_spend)['txid'])343input_txid = txs[-1]344self.sync_all()346# Check that there are exactly 3 transactions in the TX memory pool before generating the block347json_obj = self.test_rest_request("/mempool/info")348assert_equal(json_obj['size'], 3)349# the size of the memory pool should be greater than 3x ~100 bytes350assert_greater_than(json_obj['bytes'], 300)352mempool_info = self.nodes[0].getmempoolinfo()353# pop unstable unbroadcastcount before check354for obj in [json_obj, mempool_info]:355obj.pop("unbroadcastcount")356assert_equal(json_obj, mempool_info)358# Check that there are our submitted transactions in the TX memory pool359json_obj = self.test_rest_request("/mempool/contents")360raw_mempool_verbose = self.nodes[0].getrawmempool(verbose=True)362assert_equal(json_obj, raw_mempool_verbose)364for i, tx in enumerate(txs):365assert tx in json_obj366assert_equal(json_obj[tx]['spentby'], txs[i + 1:i + 2])367assert_equal(json_obj[tx]['depends'], txs[i - 1:i])369# Check the mempool response for explicit parameters370json_obj = self.test_rest_request("/mempool/contents", query_params={"verbose": "true", "mempool_sequence": "false"})371assert_equal(json_obj, raw_mempool_verbose)373# Check the mempool response for not verbose374json_obj = self.test_rest_request("/mempool/contents", query_params={"verbose": "false"})375raw_mempool = self.nodes[0].getrawmempool(verbose=False)377assert_equal(json_obj, raw_mempool)379# Check the mempool response for sequence380json_obj = self.test_rest_request("/mempool/contents", query_params={"verbose": "false", "mempool_sequence": "true"})381raw_mempool = self.nodes[0].getrawmempool(verbose=False, mempool_sequence=True)383assert_equal(json_obj, raw_mempool)385# Check for error response if verbose=true and mempool_sequence=true386resp = self.test_rest_request("/mempool/contents", ret_type=RetType.OBJ, status=400, query_params={"verbose": "true", "mempool_sequence": "true"})387assert_equal(resp.read().decode('utf-8').strip(), 'Verbose results cannot contain mempool sequence values. (hint: set "verbose=false")')389# Check for error response if verbose is not "true" or "false"390resp = self.test_rest_request("/mempool/contents", ret_type=RetType.OBJ, status=400, query_params={"verbose": "TRUE"})391assert_equal(resp.read().decode('utf-8').strip(), 'The "verbose" query parameter must be either "true" or "false".')393# Check for error response if mempool_sequence is not "true" or "false"394resp = self.test_rest_request("/mempool/contents", ret_type=RetType.OBJ, status=400, query_params={"verbose": "false", "mempool_sequence": "TRUE"})395assert_equal(resp.read().decode('utf-8').strip(), 'The "mempool_sequence" query parameter must be either "true" or "false".')397# Now mine the transactions398newblockhash = self.generate(self.nodes[1], 1)400# Check if the 3 tx show up in the new block401json_obj = self.test_rest_request(f"/block/{newblockhash[0]}")402non_coinbase_txs = {tx['txid'] for tx in json_obj['tx']403if 'coinbase' not in tx['vin'][0]}404assert_equal(non_coinbase_txs, set(txs))406# Verify that the non-coinbase tx has "prevout" key set407for tx_obj in json_obj["tx"]:408for vin in tx_obj["vin"]:409if "coinbase" not in vin:410assert "prevout" in vin411assert_equal(vin["prevout"]["generated"], False)412else:413assert "prevout" not in vin415# Check the same but without tx details416json_obj = self.test_rest_request(f"/block/notxdetails/{newblockhash[0]}")417for tx in txs:418assert tx in json_obj['tx']420self.log.info("Test the /chaininfo URI")422bb_hash = self.nodes[0].getbestblockhash()424json_obj = self.test_rest_request("/chaininfo")425assert_equal(json_obj['bestblockhash'], bb_hash)427# Compare with normal RPC getblockchaininfo response428blockchain_info = self.nodes[0].getblockchaininfo()429assert_equal(blockchain_info, json_obj)431# Test compatibility of deprecated and newer endpoints432self.log.info("Test compatibility of deprecated and newer endpoints")433assert_equal(self.test_rest_request(f"/headers/{bb_hash}", query_params={"count": 1}), self.test_rest_request(f"/headers/1/{bb_hash}"))434assert_equal(self.test_rest_request(f"/blockfilterheaders/basic/{bb_hash}", query_params={"count": 1}), self.test_rest_request(f"/blockfilterheaders/basic/5/{bb_hash}"))436self.log.info("Test the /spenttxouts URI")438block_count = self.nodes[0].getblockcount()439for height in range(0, block_count + 1):440blockhash = self.nodes[0].getblockhash(height)441spent_bin = self.test_rest_request(f"/spenttxouts/{blockhash}", req_type=ReqType.BIN, ret_type=RetType.BYTES)442spent_hex = self.test_rest_request(f"/spenttxouts/{blockhash}", req_type=ReqType.HEX, ret_type=RetType.BYTES)443spent_json = self.test_rest_request(f"/spenttxouts/{blockhash}", req_type=ReqType.JSON, ret_type=RetType.JSON)445assert_equal(bytes.fromhex(spent_hex.decode()), spent_bin)447spent = deser_block_spent_outputs(BytesIO(spent_bin))448block = self.nodes[0].getblock(blockhash, 3) # return prevout for each input449assert_equal(len(spent), len(block["tx"]))450assert_equal(len(spent_json), len(block["tx"]))452for i, tx in enumerate(block["tx"]):453prevouts = [txin["prevout"] for txin in tx["vin"] if "coinbase" not in txin]454# compare with `getblock` JSON output (coinbase tx has no prevouts)455actual = [(txout.scriptPubKey.hex(), Decimal(txout.nValue) / COIN) for txout in spent[i]]456expected = [(p["scriptPubKey"]["hex"], p["value"]) for p in prevouts]457assert_equal(expected, actual)458# also compare JSON format459actual = [(prevout["scriptPubKey"], prevout["value"]) for prevout in spent_json[i]]460expected = [(p["scriptPubKey"], p["value"]) for p in prevouts]461assert_equal(expected, actual)463self.log.info("Test the /blockpart URI")465blockhash = self.nodes[0].getbestblockhash()466block_bin = self.test_rest_request(f"/block/{blockhash}", req_type=ReqType.BIN, ret_type=RetType.BYTES)467for req_type in (ReqType.BIN, ReqType.HEX):468def get_block_part(status: int = 200, **kwargs):469resp = self.test_rest_request(f"/blockpart/{blockhash}", status=status,470req_type=req_type, ret_type=RetType.BYTES, **kwargs)471assert isinstance(resp, bytes)472if req_type is ReqType.HEX and status == 200:473resp = bytes.fromhex(resp.decode().strip())474return resp476assert_equal(block_bin, get_block_part(query_params={"offset": 0, "size": len(block_bin)}))478assert len(block_bin) >= 500479assert_equal(block_bin[20:320], get_block_part(query_params={"offset": 20, "size": 300}))480assert_equal(block_bin[-5:], get_block_part(query_params={"offset": len(block_bin) - 5, "size": 5}))482get_block_part(status=400, query_params={"offset": 10})483get_block_part(status=400, query_params={"size": 100})484get_block_part(status=400, query_params={"offset": "x"})485get_block_part(status=400, query_params={"size": "y"})486get_block_part(status=400, query_params={"offset": "x", "size": "y"})487get_block_part(status=400, query_params="%XY")488get_block_part(status=400, query_params={"offset": 0, "size": 0})489get_block_part(status=400, query_params={"offset": len(block_bin), "size": 0})490get_block_part(status=400, query_params={"offset": len(block_bin), "size": 1})491get_block_part(status=400, query_params={"offset": len(block_bin) + 1, "size": 1})492get_block_part(status=400, query_params={"offset": 0, "size": len(block_bin) + 1})494res = self.test_rest_request(f"/blockpart/{blockhash}", status=400, req_type=ReqType.BIN, ret_type=RetType.OBJ)495assert res.read().decode().startswith("Block part offset missing or invalid")497res = self.test_rest_request(f"/blockpart/{blockhash}", query_params={"offset":0, "size":1}, status=400, req_type=ReqType.JSON, ret_type=RetType.OBJ)498assert res.read().decode().startswith("JSON output is not supported for this request type")500self.log.info("Missing block data should cause REST API to fail")
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.