rest: allow reading partial block data from storage

It will allow fetching specific transactions using an external index,
following https://github.com/bitcoin/bitcoin/pull/32541#issuecomment-3267485313.

Co-authored-by: Hodlinator <172445034+hodlinator@users.noreply.github.com>
Co-authored-by: Lőrinc <pap.lorinc@gmail.com>
This commit is contained in:
committed 2025-12-11 19:02:56 +01:00
1 parent 4e2af1c065
commit 07135290c1
4 files changed
+106 -16

No files matched your search

+52 -4
View File
@@ -28,7 +28,6 @@ from test_framework.wallet import (
MiniWallet,
getnewdestination,
)
from typing import Optional
INVALID_PARAM = "abc"
@@ -66,13 +65,16 @@ class RESTTest (BitcoinTestFramework):
body: str = '',
status: int = 200,
ret_type: RetType = RetType.JSON,
query_params: Optional[dict[str, typing.Any]] = None,
query_params: typing.Union[dict[str, typing.Any], str, None] = None,
) -> typing.Union[http.client.HTTPResponse, bytes, str, None]:
rest_uri = '/rest' + uri
if req_type in ReqType:
rest_uri += f'.{req_type.name.lower()}'
if query_params:
rest_uri += f'?{urllib.parse.urlencode(query_params)}'
if isinstance(query_params, str):
rest_uri += f'?{query_params}'
else:
rest_uri += f'?{urllib.parse.urlencode(query_params)}'
conn = http.client.HTTPConnection(self.url.hostname, self.url.port)
self.log.debug(f'{http_method} {rest_uri} {body}')
@@ -82,7 +84,7 @@ class RESTTest (BitcoinTestFramework):
conn.request('POST', rest_uri, body)
resp = conn.getresponse()
assert_equal(resp.status, status)
assert resp.status == status, f"Expected: {status}, Got: {resp.status} - Response: {str(resp.read())}"
if ret_type == RetType.OBJ:
return resp
@@ -455,6 +457,52 @@ class RESTTest (BitcoinTestFramework):
expected = [(p["scriptPubKey"], p["value"]) for p in prevouts]
assert_equal(expected, actual)
self.log.info("Test the /blockpart URI")
blockhash = self.nodes[0].getbestblockhash()
block_bin = self.test_rest_request(f"/block/{blockhash}", req_type=ReqType.BIN, ret_type=RetType.BYTES)
for req_type in (ReqType.BIN, ReqType.HEX):
def get_block_part(status: int = 200, **kwargs):
resp = self.test_rest_request(f"/blockpart/{blockhash}", status=status,
req_type=req_type, ret_type=RetType.BYTES, **kwargs)
assert isinstance(resp, bytes)
if req_type is ReqType.HEX and status == 200:
resp = bytes.fromhex(resp.decode().strip())
return resp
assert_equal(block_bin, get_block_part(query_params={"offset": 0, "size": len(block_bin)}))
assert len(block_bin) >= 500
assert_equal(block_bin[20:320], get_block_part(query_params={"offset": 20, "size": 300}))
assert_equal(block_bin[-5:], get_block_part(query_params={"offset": len(block_bin) - 5, "size": 5}))
get_block_part(status=400, query_params={"offset": 10})
get_block_part(status=400, query_params={"size": 100})
get_block_part(status=400, query_params={"offset": "x"})
get_block_part(status=400, query_params={"size": "y"})
get_block_part(status=400, query_params={"offset": "x", "size": "y"})
assert get_block_part(status=400, query_params="%XY").decode("utf-8").startswith("URI parsing failed")
get_block_part(status=400, query_params={"offset": 0, "size": 0})
get_block_part(status=400, query_params={"offset": len(block_bin), "size": 0})
get_block_part(status=400, query_params={"offset": len(block_bin) + 1, "size": 1})
get_block_part(status=400, query_params={"offset": len(block_bin), "size": 1})
get_block_part(status=400, query_params={"offset": len(block_bin) + 1, "size": 1})
get_block_part(status=400, query_params={"offset": 0, "size": len(block_bin) + 1})
self.test_rest_request(f"/blockpart/{blockhash}", status=400, req_type=ReqType.JSON, ret_type=RetType.OBJ)
self.log.info("Missing block data should cause REST API to fail")
self.test_rest_request(f"/block/{blockhash}", status=200, req_type=ReqType.BIN, ret_type=RetType.OBJ)
self.test_rest_request(f"/blockpart/{blockhash}", query_params={"offset": 0, "size": 1}, status=200, req_type=ReqType.BIN, ret_type=RetType.OBJ)
blk_files = list(self.nodes[0].blocks_path.glob("blk*.dat"))
for blk_file in blk_files:
blk_file.rename(blk_file.with_suffix('.bkp'))
self.test_rest_request(f"/block/{blockhash}", status=500, req_type=ReqType.BIN, ret_type=RetType.OBJ)
self.test_rest_request(f"/blockpart/{blockhash}", query_params={"offset": 0, "size": 1}, status=500, req_type=ReqType.BIN, ret_type=RetType.OBJ)
for blk_file in blk_files:
blk_file.with_suffix('.bkp').rename(blk_file)
self.log.info("Test the /deploymentinfo URI")