Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion chia/_tests/core/full_node/stores/test_block_store.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,10 @@ async def test_block_store(tmp_dir: Path, db_version: int, bt: BlockTools, use_c
assert block == await store.get_full_block(block.header_hash)
assert bytes(block) == await store.get_full_block_bytes(block.header_hash)
assert GeneratorBlockInfo(
block.foliage.prev_block_hash, block.transactions_generator, block.transactions_generator_ref_list
block.foliage.prev_block_hash,
block.height,
block.transactions_generator,
block.transactions_generator_ref_list,
) == await store.get_block_info(block.header_hash)
assert maybe_serialize(block.transactions_generator) == await store.get_generator(block.header_hash)
assert block_record == (await store.get_block_record(block_record_hh))
Expand Down
1 change: 1 addition & 0 deletions chia/_tests/util/test_full_block_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,7 @@ async def test_parser():
assert block.transactions_generator == bi.transactions_generator
assert block.prev_header_hash == bi.prev_header_hash
assert block.transactions_generator_ref_list == bi.transactions_generator_ref_list
assert block.height == bi.height
# this doubles the run-time of this test, with questionable utility
# assert gen == FullBlock.from_bytes(block_bytes).transactions_generator

Expand Down
7 changes: 5 additions & 2 deletions chia/full_node/block_store.py
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,10 @@ async def get_block_info(self, header_hash: bytes32) -> Optional[GeneratorBlockI
cached = self.block_cache.get(header_hash)
if cached is not None:
return GeneratorBlockInfo(
cached.foliage.prev_block_hash, cached.transactions_generator, cached.transactions_generator_ref_list
cached.foliage.prev_block_hash,
cached.height,
cached.transactions_generator,
cached.transactions_generator_ref_list,
)

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

async def get_generator(self, header_hash: bytes32) -> Optional[bytes]:
Expand Down
25 changes: 23 additions & 2 deletions chia/full_node/full_block_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,26 @@ def skip_reward_chain_block(buf: memoryview) -> memoryview:
return skip_bool(buf) # is_transaction_block


def height_from_reward_chain_block(buf: memoryview) -> tuple[memoryview, uint32]:
buf = skip_uint128(buf) # weight
height = uint32.from_bytes(buf[:4])
buf = skip_uint32(buf) # height
buf = skip_uint128(buf) # total_iters
buf = skip_uint8(buf) # signage_point_index
buf = skip_bytes32(buf) # pos_ss_cc_challenge_hash

buf = skip_proof_of_space(buf) # proof_of_space
buf = skip_optional(buf, skip_vdf_info) # challenge_chain_sp_vdf
buf = skip_g2_element(buf) # challenge_chain_sp_signature
buf = skip_vdf_info(buf) # challenge_chain_ip_vdf
buf = skip_optional(buf, skip_vdf_info) # reward_chain_sp_vdf
buf = skip_g2_element(buf) # reward_chain_sp_signature
buf = skip_vdf_info(buf) # reward_chain_ip_vdf
buf = skip_optional(buf, skip_vdf_info) # infused_challenge_chain_ip_vdf
buf = skip_bool(buf) # is_transaction_block
return buf, height


def skip_pool_target(buf: memoryview) -> memoryview:
# buf = skip_bytes32(buf) # puzzle_hash
# return skip_uint32(buf) # max_height
Expand Down Expand Up @@ -228,13 +248,14 @@ def generator_from_block(buf: memoryview) -> Optional[bytes]:
@dataclass(frozen=True)
class GeneratorBlockInfo:
prev_header_hash: bytes32
height: uint32
transactions_generator: Optional[SerializedProgram]
transactions_generator_ref_list: list[uint32]


def block_info_from_block(buf: memoryview) -> GeneratorBlockInfo:
buf = skip_list(buf, skip_end_of_sub_slot_bundle) # finished_sub_slots
buf = skip_reward_chain_block(buf) # reward_chain_block
buf, height = height_from_reward_chain_block(buf) # reward_chain_block
Comment on lines -237 to +258
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can avoid this added complexity by receiving height in block_info_from_block if we must (to keep returning this augmented GeneratorBlockInfo) instead of computing it. The caller has it already.

buf = skip_optional(buf, skip_vdf_proof) # challenge_chain_sp_proof
buf = skip_vdf_proof(buf) # challenge_chain_ip_proof
buf = skip_optional(buf, skip_vdf_proof) # reward_chain_sp_proof
Expand Down Expand Up @@ -262,7 +283,7 @@ def block_info_from_block(buf: memoryview) -> GeneratorBlockInfo:
refs.append(uint32.from_bytes(buf[:4]))
buf = buf[4:]

return GeneratorBlockInfo(prev_hash, generator, refs)
return GeneratorBlockInfo(prev_hash, height, generator, refs)


def header_block_from_block(
Expand Down
35 changes: 20 additions & 15 deletions chia/full_node/full_node_rpc_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
from chia.consensus.get_block_generator import get_block_generator
from chia.consensus.pos_quality import UI_ACTUAL_SPACE_CONSTANT_FACTOR
from chia.full_node.fee_estimator_interface import FeeEstimatorInterface
from chia.full_node.full_block_utils import GeneratorBlockInfo, get_height_and_tx_status_from_block
from chia.full_node.full_node import FullNode
from chia.protocols.outbound_message import NodeType
from chia.rpc.rpc_server import Endpoint, EndpointResult
Expand Down Expand Up @@ -481,15 +482,15 @@ async def get_block_spends(self, request: dict[str, Any]) -> EndpointResult:
if "header_hash" not in request:
raise ValueError("No header_hash in request")
header_hash = bytes32.from_hexstr(request["header_hash"])
full_block: Optional[FullBlock] = await self.service.block_store.get_full_block(header_hash)
if full_block is None:
block_info: Optional[GeneratorBlockInfo] = await self.service.block_store.get_block_info(header_hash)
if block_info is None:
raise ValueError(f"Block {header_hash.hex()} not found")

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

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

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

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

try:
puzzle, solution = get_puzzle_and_solution_for_coin(
puzzle, solution = await asyncio.get_running_loop().run_in_executor(
self.executor,
get_puzzle_and_solution_for_coin,
block_generator.program,
block_generator.generator_refs,
self.service.constants.MAX_BLOCK_COST_CLVM,
Expand All @@ -807,16 +810,18 @@ async def get_additions_and_removals(self, request: dict[str, Any]) -> EndpointR
if "header_hash" not in request:
raise ValueError("No header_hash in request")
header_hash = bytes32.from_hexstr(request["header_hash"])

block: Optional[FullBlock] = await self.service.block_store.get_full_block(header_hash)
if block is None:
block_bytes: Optional[bytes] = await self.service.block_store.get_full_block_bytes(header_hash)
if block_bytes is None:
raise ValueError(f"Block {header_hash.hex()} not found")

block_view = memoryview(block_bytes)
height, _is_tx_block = get_height_and_tx_status_from_block(block_view)

Comment on lines -810 to +819
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should be able to avoid this extra complexity by simply augmenting get_full_block_bytes to return the height as well. We have it in the full_blocks table it already queries.

async with self.service.blockchain.priority_mutex.acquire(priority=BlockchainMutexPriority.low):
if self.service.blockchain.height_to_hash(block.height) != header_hash:
if self.service.blockchain.height_to_hash(height) != header_hash:
raise ValueError(f"Block at {header_hash.hex()} is no longer in the blockchain (it's in a fork)")
additions: list[CoinRecord] = await self.service.coin_store.get_coins_added_at_height(block.height)
removals: list[CoinRecord] = await self.service.coin_store.get_coins_removed_at_height(block.height)
additions: list[CoinRecord] = await self.service.coin_store.get_coins_added_at_height(height)
removals: list[CoinRecord] = await self.service.coin_store.get_coins_removed_at_height(height)

return {
"additions": [coin_record_dict_backwards_compat(cr.to_json_dict()) for cr in additions],
Expand Down
Loading