Skip to content

Commit b7967d3

Browse files
committed
optimize get_spends RPC
1 parent 35291ee commit b7967d3

File tree

5 files changed

+53
-20
lines changed

5 files changed

+53
-20
lines changed

chia/_tests/core/full_node/stores/test_block_store.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,10 @@ async def test_block_store(tmp_dir: Path, db_version: int, bt: BlockTools, use_c
9191
assert block == await store.get_full_block(block.header_hash)
9292
assert bytes(block) == await store.get_full_block_bytes(block.header_hash)
9393
assert GeneratorBlockInfo(
94-
block.foliage.prev_block_hash, block.transactions_generator, block.transactions_generator_ref_list
94+
block.foliage.prev_block_hash,
95+
block.height,
96+
block.transactions_generator,
97+
block.transactions_generator_ref_list,
9598
) == await store.get_block_info(block.header_hash)
9699
assert maybe_serialize(block.transactions_generator) == await store.get_generator(block.header_hash)
97100
assert block_record == (await store.get_block_record(block_record_hh))

chia/_tests/util/test_full_block_utils.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,7 @@ async def test_parser():
275275
assert block.transactions_generator == bi.transactions_generator
276276
assert block.prev_header_hash == bi.prev_header_hash
277277
assert block.transactions_generator_ref_list == bi.transactions_generator_ref_list
278+
assert block.height == bi.height
278279
# this doubles the run-time of this test, with questionable utility
279280
# assert gen == FullBlock.from_bytes(block_bytes).transactions_generator
280281

chia/full_node/block_store.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,10 @@ async def get_block_info(self, header_hash: bytes32) -> Optional[GeneratorBlockI
251251
cached = self.block_cache.get(header_hash)
252252
if cached is not None:
253253
return GeneratorBlockInfo(
254-
cached.foliage.prev_block_hash, cached.transactions_generator, cached.transactions_generator_ref_list
254+
cached.foliage.prev_block_hash,
255+
cached.height,
256+
cached.transactions_generator,
257+
cached.transactions_generator_ref_list,
255258
)
256259

257260
formatted_str = "SELECT block, height from full_blocks WHERE header_hash=?"
@@ -270,7 +273,7 @@ async def get_block_info(self, header_hash: bytes32) -> Optional[GeneratorBlockI
270273
# definition of parsing a block
271274
b = FullBlock.from_bytes(block_bytes)
272275
return GeneratorBlockInfo(
273-
b.foliage.prev_block_hash, b.transactions_generator, b.transactions_generator_ref_list
276+
b.foliage.prev_block_hash, b.height, b.transactions_generator, b.transactions_generator_ref_list
274277
)
275278

276279
async def get_generator(self, header_hash: bytes32) -> Optional[bytes]:

chia/full_node/full_block_utils.py

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,26 @@ def skip_reward_chain_block(buf: memoryview) -> memoryview:
143143
return skip_bool(buf) # is_transaction_block
144144

145145

146+
def height_from_reward_chain_block(buf: memoryview) -> tuple[memoryview, uint32]:
147+
buf = skip_uint128(buf) # weight
148+
height = uint32.from_bytes(buf[:4])
149+
buf = skip_uint32(buf) # height
150+
buf = skip_uint128(buf) # total_iters
151+
buf = skip_uint8(buf) # signage_point_index
152+
buf = skip_bytes32(buf) # pos_ss_cc_challenge_hash
153+
154+
buf = skip_proof_of_space(buf) # proof_of_space
155+
buf = skip_optional(buf, skip_vdf_info) # challenge_chain_sp_vdf
156+
buf = skip_g2_element(buf) # challenge_chain_sp_signature
157+
buf = skip_vdf_info(buf) # challenge_chain_ip_vdf
158+
buf = skip_optional(buf, skip_vdf_info) # reward_chain_sp_vdf
159+
buf = skip_g2_element(buf) # reward_chain_sp_signature
160+
buf = skip_vdf_info(buf) # reward_chain_ip_vdf
161+
buf = skip_optional(buf, skip_vdf_info) # infused_challenge_chain_ip_vdf
162+
buf = skip_bool(buf) # is_transaction_block
163+
return buf, height
164+
165+
146166
def skip_pool_target(buf: memoryview) -> memoryview:
147167
# buf = skip_bytes32(buf) # puzzle_hash
148168
# return skip_uint32(buf) # max_height
@@ -228,13 +248,14 @@ def generator_from_block(buf: memoryview) -> Optional[bytes]:
228248
@dataclass(frozen=True)
229249
class GeneratorBlockInfo:
230250
prev_header_hash: bytes32
251+
height: uint32
231252
transactions_generator: Optional[SerializedProgram]
232253
transactions_generator_ref_list: list[uint32]
233254

234255

235256
def block_info_from_block(buf: memoryview) -> GeneratorBlockInfo:
236257
buf = skip_list(buf, skip_end_of_sub_slot_bundle) # finished_sub_slots
237-
buf = skip_reward_chain_block(buf) # reward_chain_block
258+
buf, height = height_from_reward_chain_block(buf) # reward_chain_block
238259
buf = skip_optional(buf, skip_vdf_proof) # challenge_chain_sp_proof
239260
buf = skip_vdf_proof(buf) # challenge_chain_ip_proof
240261
buf = skip_optional(buf, skip_vdf_proof) # reward_chain_sp_proof
@@ -262,7 +283,7 @@ def block_info_from_block(buf: memoryview) -> GeneratorBlockInfo:
262283
refs.append(uint32.from_bytes(buf[:4]))
263284
buf = buf[4:]
264285

265-
return GeneratorBlockInfo(prev_hash, generator, refs)
286+
return GeneratorBlockInfo(prev_hash, height, generator, refs)
266287

267288

268289
def header_block_from_block(

chia/full_node/full_node_rpc_api.py

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
from chia.consensus.get_block_generator import get_block_generator
2828
from chia.consensus.pos_quality import UI_ACTUAL_SPACE_CONSTANT_FACTOR
2929
from chia.full_node.fee_estimator_interface import FeeEstimatorInterface
30+
from chia.full_node.full_block_utils import GeneratorBlockInfo, get_height_and_tx_status_from_block
3031
from chia.full_node.full_node import FullNode
3132
from chia.protocols.outbound_message import NodeType
3233
from chia.rpc.rpc_server import Endpoint, EndpointResult
@@ -481,15 +482,15 @@ async def get_block_spends(self, request: dict[str, Any]) -> EndpointResult:
481482
if "header_hash" not in request:
482483
raise ValueError("No header_hash in request")
483484
header_hash = bytes32.from_hexstr(request["header_hash"])
484-
full_block: Optional[FullBlock] = await self.service.block_store.get_full_block(header_hash)
485-
if full_block is None:
485+
block_info: Optional[GeneratorBlockInfo] = await self.service.block_store.get_block_info(header_hash)
486+
if block_info is None:
486487
raise ValueError(f"Block {header_hash.hex()} not found")
487488

488-
block_generator = await get_block_generator(self.service.blockchain.lookup_block_generators, full_block)
489+
block_generator = await get_block_generator(self.service.blockchain.lookup_block_generators, block_info)
489490
if block_generator is None: # if block is not a transaction block.
490491
return {"block_spends": []}
491492

492-
flags = get_flags_for_height_and_constants(full_block.height, self.service.constants)
493+
flags = get_flags_for_height_and_constants(block_info.height, self.service.constants)
493494
spends = await asyncio.get_running_loop().run_in_executor(
494495
self.executor,
495496
get_spends_for_trusted_block,
@@ -505,15 +506,15 @@ async def get_block_spends_with_conditions(self, request: dict[str, Any]) -> End
505506
if "header_hash" not in request:
506507
raise ValueError("No header_hash in request")
507508
header_hash = bytes32.from_hexstr(request["header_hash"])
508-
full_block: Optional[FullBlock] = await self.service.block_store.get_full_block(header_hash)
509-
if full_block is None:
509+
block_info: Optional[GeneratorBlockInfo] = await self.service.block_store.get_block_info(header_hash)
510+
if block_info is None:
510511
raise ValueError(f"Block {header_hash.hex()} not found")
511512

512-
block_generator = await get_block_generator(self.service.blockchain.lookup_block_generators, full_block)
513+
block_generator = await get_block_generator(self.service.blockchain.lookup_block_generators, block_info)
513514
if block_generator is None: # if block is not a transaction block.
514515
return {"block_spends_with_conditions": []}
515516

516-
flags = get_flags_for_height_and_constants(full_block.height, self.service.constants)
517+
flags = get_flags_for_height_and_constants(block_info.height, self.service.constants)
517518
spends_with_conditions = await asyncio.get_running_loop().run_in_executor(
518519
self.executor,
519520
get_spends_for_trusted_block_with_conditions,
@@ -792,7 +793,9 @@ async def get_puzzle_and_solution(self, request: dict[str, Any]) -> EndpointResu
792793
assert block_generator is not None
793794

794795
try:
795-
puzzle, solution = get_puzzle_and_solution_for_coin(
796+
puzzle, solution = await asyncio.get_running_loop().run_in_executor(
797+
self.executor,
798+
get_puzzle_and_solution_for_coin,
796799
block_generator.program,
797800
block_generator.generator_refs,
798801
self.service.constants.MAX_BLOCK_COST_CLVM,
@@ -807,16 +810,18 @@ async def get_additions_and_removals(self, request: dict[str, Any]) -> EndpointR
807810
if "header_hash" not in request:
808811
raise ValueError("No header_hash in request")
809812
header_hash = bytes32.from_hexstr(request["header_hash"])
810-
811-
block: Optional[FullBlock] = await self.service.block_store.get_full_block(header_hash)
812-
if block is None:
813+
block_bytes: Optional[bytes] = await self.service.block_store.get_full_block_bytes(header_hash)
814+
if block_bytes is None:
813815
raise ValueError(f"Block {header_hash.hex()} not found")
814816

817+
block_view = memoryview(block_bytes)
818+
height, _is_tx_block = get_height_and_tx_status_from_block(block_view)
819+
815820
async with self.service.blockchain.priority_mutex.acquire(priority=BlockchainMutexPriority.low):
816-
if self.service.blockchain.height_to_hash(block.height) != header_hash:
821+
if self.service.blockchain.height_to_hash(height) != header_hash:
817822
raise ValueError(f"Block at {header_hash.hex()} is no longer in the blockchain (it's in a fork)")
818-
additions: list[CoinRecord] = await self.service.coin_store.get_coins_added_at_height(block.height)
819-
removals: list[CoinRecord] = await self.service.coin_store.get_coins_removed_at_height(block.height)
823+
additions: list[CoinRecord] = await self.service.coin_store.get_coins_added_at_height(height)
824+
removals: list[CoinRecord] = await self.service.coin_store.get_coins_removed_at_height(height)
820825

821826
return {
822827
"additions": [coin_record_dict_backwards_compat(cr.to_json_dict()) for cr in additions],

0 commit comments

Comments
 (0)