From 49422b798340a1abfaa92716317ac47b38e812b6 Mon Sep 17 00:00:00 2001 From: Richard Kiss Date: Wed, 9 Jul 2025 17:15:05 -0700 Subject: [PATCH 01/16] BlockStoreProtocol --- chia/consensus/block_store_protocol.py | 72 ++++++++++++++++++++++++++ chia/full_node/block_store.py | 1 + chia/full_node/full_node.py | 3 +- 3 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 chia/consensus/block_store_protocol.py diff --git a/chia/consensus/block_store_protocol.py b/chia/consensus/block_store_protocol.py new file mode 100644 index 000000000000..ca8f371d7f3e --- /dev/null +++ b/chia/consensus/block_store_protocol.py @@ -0,0 +1,72 @@ +from __future__ import annotations + +from typing import Optional, Protocol + +from chia_rs import BlockRecord, FullBlock, SubEpochChallengeSegment +from chia_rs.sized_bytes import bytes32 +from chia_rs.sized_ints import uint32 + +from chia.full_node.full_block_utils import GeneratorBlockInfo + + +class BlockStoreProtocol(Protocol): + async def add_full_block(self, header_hash: bytes32, block: FullBlock, block_record: BlockRecord) -> None: ... + + async def get_full_block(self, header_hash: bytes32) -> Optional[FullBlock]: ... + + async def get_full_block_bytes(self, header_hash: bytes32) -> Optional[bytes]: ... + + async def get_full_blocks_at(self, heights: list[uint32]) -> list[FullBlock]: ... + + async def get_block_info(self, header_hash: bytes32) -> Optional[GeneratorBlockInfo]: ... + + async def get_generator(self, header_hash: bytes32) -> Optional[bytes]: ... + + async def get_generators_at(self, heights: set[uint32]) -> dict[uint32, bytes]: ... + + async def get_block_record(self, header_hash: bytes32) -> Optional[BlockRecord]: ... + + async def get_block_records_in_range( + self, + start: int, + stop: int, + ) -> dict[bytes32, BlockRecord]: ... + + async def get_block_records_by_hash(self, header_hashes: list[bytes32]) -> list[BlockRecord]: ... + + async def get_block_bytes_by_hash(self, header_hashes: list[bytes32]) -> list[bytes]: ... + + async def get_blocks_by_hash(self, header_hashes: list[bytes32]) -> list[FullBlock]: ... + + async def get_peak(self) -> Optional[tuple[bytes32, uint32]]: ... + + async def get_block_bytes_in_range( + self, + start: int, + stop: int, + ) -> list[bytes]: ... + + async def get_random_not_compactified(self, number: int) -> list[int]: ... + + async def persist_sub_epoch_challenge_segments( + self, ses_block_hash: bytes32, segments: list[SubEpochChallengeSegment] + ) -> None: ... + + async def get_sub_epoch_challenge_segments( + self, + ses_block_hash: bytes32, + ) -> Optional[list[SubEpochChallengeSegment]]: ... + + async def rollback(self, height: int) -> None: ... + + async def set_in_chain(self, header_hashes: list[tuple[bytes32]]) -> None: ... + + async def set_peak(self, header_hash: bytes32) -> None: ... + + async def is_fully_compactified(self, header_hash: bytes32) -> Optional[bool]: ... + + async def replace_proof(self, header_hash: bytes32, block: FullBlock) -> None: ... + + async def count_compactified_blocks(self) -> int: ... + + async def count_uncompactified_blocks(self) -> int: ... diff --git a/chia/full_node/block_store.py b/chia/full_node/block_store.py index 4fef5b9a4006..e611cd496051 100644 --- a/chia/full_node/block_store.py +++ b/chia/full_node/block_store.py @@ -13,6 +13,7 @@ from chia_rs.sized_bytes import bytes32 from chia_rs.sized_ints import uint32 +from chia.consensus.block_store_protocol import BlockStoreProtocol from chia.full_node.full_block_utils import GeneratorBlockInfo, block_info_from_block, generator_from_block from chia.util.batches import to_batches from chia.util.db_wrapper import DBWrapper2, execute_fetchone diff --git a/chia/full_node/full_node.py b/chia/full_node/full_node.py index 28a8c9c397d2..5d297ed89a6a 100644 --- a/chia/full_node/full_node.py +++ b/chia/full_node/full_node.py @@ -40,6 +40,7 @@ from chia.consensus.block_body_validation import ForkInfo from chia.consensus.block_creation import unfinished_block_to_full_block from chia.consensus.block_height_map import BlockHeightMap +from chia.consensus.block_store_protocol import BlockStoreProtocol from chia.consensus.blockchain import AddBlockResult, Blockchain, BlockchainMutexPriority, StateChangeSummary from chia.consensus.blockchain_interface import BlockchainInterface from chia.consensus.coin_store_protocol import CoinStoreProtocol @@ -399,7 +400,7 @@ async def manage(self) -> AsyncIterator[None]: await asyncio.gather(*self._segment_task_list, return_exceptions=True) @property - def block_store(self) -> BlockStore: + def block_store(self) -> BlockStoreProtocol: assert self._block_store is not None return self._block_store From 165f6282245f24b141b067702879f2f2d67203d4 Mon Sep 17 00:00:00 2001 From: Richard Kiss Date: Fri, 11 Jul 2025 17:33:14 -0700 Subject: [PATCH 02/16] Deal with `.db_wrapper` --- chia/consensus/blockchain.py | 6 +++--- chia/full_node/block_store.py | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/chia/consensus/blockchain.py b/chia/consensus/blockchain.py index 2117f963f308..899affb655cc 100644 --- a/chia/consensus/blockchain.py +++ b/chia/consensus/blockchain.py @@ -27,6 +27,7 @@ from chia.consensus.block_body_validation import ForkInfo, validate_block_body from chia.consensus.block_header_validation import validate_unfinished_header_block from chia.consensus.block_height_map import BlockHeightMap +from chia.consensus.block_store_protocol import BlockStoreProtocol from chia.consensus.coin_store_protocol import CoinStoreProtocol from chia.consensus.cost_calculator import NPCResult from chia.consensus.difficulty_adjustment import get_next_sub_slot_iters_and_difficulty @@ -35,7 +36,6 @@ from chia.consensus.generator_tools import get_block_header from chia.consensus.get_block_generator import get_block_generator from chia.consensus.multiprocess_validation import PreValidationResult -from chia.full_node.block_store import BlockStore from chia.types.blockchain_format.coin import Coin from chia.types.blockchain_format.vdf import VDFInfo from chia.types.coin_record import CoinRecord @@ -104,7 +104,7 @@ class Blockchain: # Unspent Store coin_store: CoinStoreProtocol # Store - block_store: BlockStore + block_store: BlockStoreProtocol # Used to verify blocks in parallel pool: Executor # Set holding seen compact proofs, in order to avoid duplicates. @@ -122,7 +122,7 @@ class Blockchain: @staticmethod async def create( coin_store: CoinStoreProtocol, - block_store: BlockStore, + block_store: BlockStoreProtocol, height_map: BlockHeightMap, consensus_constants: ConsensusConstants, reserved_cores: int, diff --git a/chia/full_node/block_store.py b/chia/full_node/block_store.py index e611cd496051..4624aeddc7e4 100644 --- a/chia/full_node/block_store.py +++ b/chia/full_node/block_store.py @@ -13,7 +13,6 @@ from chia_rs.sized_bytes import bytes32 from chia_rs.sized_ints import uint32 -from chia.consensus.block_store_protocol import BlockStoreProtocol from chia.full_node.full_block_utils import GeneratorBlockInfo, block_info_from_block, generator_from_block from chia.util.batches import to_batches from chia.util.db_wrapper import DBWrapper2, execute_fetchone @@ -479,7 +478,8 @@ async def get_block_bytes_in_range( No orphan blocks. """ - assert self.db_wrapper.db_version == 2 + if self.db_wrapper.db_version != 2: + raise NotImplementedError("get_block_bytes_in_range requires DB version 2") async with self.db_wrapper.reader_no_transaction() as conn: async with conn.execute( "SELECT block FROM full_blocks WHERE height >= ? AND height <= ? AND in_main_chain=1", From 8046b01dfdc11e6ce91459aac0237bc61ed021c4 Mon Sep 17 00:00:00 2001 From: Richard Kiss Date: Fri, 11 Jul 2025 18:49:05 -0700 Subject: [PATCH 03/16] mypy --- chia/consensus/block_store_protocol.py | 42 +++++----------------- chia/consensus/blockchain.py | 2 ++ chia/full_node/block_store.py | 48 +++++++++++++++++++++++++- 3 files changed, 58 insertions(+), 34 deletions(-) diff --git a/chia/consensus/block_store_protocol.py b/chia/consensus/block_store_protocol.py index ca8f371d7f3e..d6108df39f68 100644 --- a/chia/consensus/block_store_protocol.py +++ b/chia/consensus/block_store_protocol.py @@ -1,5 +1,6 @@ from __future__ import annotations +from contextlib import AbstractAsyncContextManager from typing import Optional, Protocol from chia_rs import BlockRecord, FullBlock, SubEpochChallengeSegment @@ -11,62 +12,37 @@ class BlockStoreProtocol(Protocol): async def add_full_block(self, header_hash: bytes32, block: FullBlock, block_record: BlockRecord) -> None: ... - + def get_block_from_cache(self, header_hash: bytes32) -> Optional[FullBlock]: ... async def get_full_block(self, header_hash: bytes32) -> Optional[FullBlock]: ... - async def get_full_block_bytes(self, header_hash: bytes32) -> Optional[bytes]: ... - async def get_full_blocks_at(self, heights: list[uint32]) -> list[FullBlock]: ... - async def get_block_info(self, header_hash: bytes32) -> Optional[GeneratorBlockInfo]: ... - async def get_generator(self, header_hash: bytes32) -> Optional[bytes]: ... - async def get_generators_at(self, heights: set[uint32]) -> dict[uint32, bytes]: ... - async def get_block_record(self, header_hash: bytes32) -> Optional[BlockRecord]: ... - - async def get_block_records_in_range( - self, - start: int, - stop: int, - ) -> dict[bytes32, BlockRecord]: ... - + async def get_block_records_in_range(self, start: int, stop: int) -> dict[bytes32, BlockRecord]: ... async def get_block_records_by_hash(self, header_hashes: list[bytes32]) -> list[BlockRecord]: ... - async def get_block_bytes_by_hash(self, header_hashes: list[bytes32]) -> list[bytes]: ... - async def get_blocks_by_hash(self, header_hashes: list[bytes32]) -> list[FullBlock]: ... - async def get_peak(self) -> Optional[tuple[bytes32, uint32]]: ... - - async def get_block_bytes_in_range( - self, - start: int, - stop: int, - ) -> list[bytes]: ... - + async def get_block_bytes_in_range(self, start: int, stop: int) -> list[bytes]: ... async def get_random_not_compactified(self, number: int) -> list[int]: ... - async def persist_sub_epoch_challenge_segments( self, ses_block_hash: bytes32, segments: list[SubEpochChallengeSegment] ) -> None: ... - async def get_sub_epoch_challenge_segments( self, ses_block_hash: bytes32, ) -> Optional[list[SubEpochChallengeSegment]]: ... - async def rollback(self, height: int) -> None: ... - + def rollback_cache_block(self, header_hash: bytes32) -> None: ... async def set_in_chain(self, header_hashes: list[tuple[bytes32]]) -> None: ... - async def set_peak(self, header_hash: bytes32) -> None: ... - async def is_fully_compactified(self, header_hash: bytes32) -> Optional[bool]: ... - async def replace_proof(self, header_hash: bytes32, block: FullBlock) -> None: ... - async def count_compactified_blocks(self) -> int: ... - async def count_uncompactified_blocks(self) -> int: ... + async def get_block_records_close_to_peak(self, blocks_n: int) -> tuple[dict[bytes32, BlockRecord], Optional[bytes32]]: ... + def get_host_parameter_limit(self) -> int: ... + async def get_prev_hash(self, header_hash: bytes32) -> bytes32: ... + def transaction(self) -> AbstractAsyncContextManager[None]: ... \ No newline at end of file diff --git a/chia/consensus/blockchain.py b/chia/consensus/blockchain.py index 899affb655cc..6a7b2e030ff3 100644 --- a/chia/consensus/blockchain.py +++ b/chia/consensus/blockchain.py @@ -421,6 +421,7 @@ async def add_block( try: # Always add the block to the database + async with self.block_store.transaction(): async with self.block_store.transaction(): # Perform the DB operations to update the state, and rollback if something goes wrong await self.block_store.add_full_block(header_hash, block, block_record) @@ -931,6 +932,7 @@ async def get_block_records_at(self, heights: list[uint32]) -> list[BlockRecord] gets block records by height (only blocks that are part of the chain) """ hashes: list[bytes32] = [] + assert batch_size < self.block_store.get_host_parameter_limit() for height in heights: header_hash: Optional[bytes32] = self.height_to_hash(height) if header_hash is None: diff --git a/chia/full_node/block_store.py b/chia/full_node/block_store.py index 4624aeddc7e4..43b7ae892f46 100644 --- a/chia/full_node/block_store.py +++ b/chia/full_node/block_store.py @@ -192,12 +192,39 @@ async def get_sub_epoch_challenge_segments( return challenge_segments return None - def transaction(self) -> AbstractAsyncContextManager[aiosqlite.Connection]: + def get_host_parameter_limit(self) -> int: + return self.db_wrapper.host_parameter_limit + + def transaction(self) -> AbstractAsyncContextManager[None]: return self.db_wrapper.writer() def get_block_from_cache(self, header_hash: bytes32) -> Optional[FullBlock]: return self.block_cache.get(header_hash) + async def get_block_records_close_to_peak( + self, blocks_n: int + ) -> tuple[dict[bytes32, BlockRecord], Optional[bytes32]]: + """ + Returns a dictionary with all blocks that have height >= peak height - blocks_n, as well as the + peak header hash. Only blocks that are part of the main chain/current peak are included. + """ + + peak = await self.get_peak() + if peak is None: + return {}, None + + ret: dict[bytes32, BlockRecord] = {} + async with self.db_wrapper.reader_no_transaction() as conn: + async with conn.execute( + "SELECT header_hash, block_record FROM full_blocks WHERE height >= ? AND in_main_chain=1", + (peak[1] - blocks_n,), + ) as cursor: + for row in await cursor.fetchall(): + header_hash = bytes32(row[0]) + ret[header_hash] = BlockRecord.from_bytes(row[1]) + + return ret, peak[0] + def rollback_cache_block(self, header_hash: bytes32) -> None: try: self.block_cache.remove(header_hash) @@ -206,6 +233,25 @@ def rollback_cache_block(self, header_hash: bytes32) -> None: # block to the cache yet pass + async def get_prev_hash(self, header_hash: bytes32) -> bytes32: + """ + Returns the header hash preceeding the input header hash. + Throws an exception if the block is not present + """ + cached = self.block_cache.get(header_hash) + if cached is not None: + return cached.prev_header_hash + + async with self.db_wrapper.reader_no_transaction() as conn: + async with conn.execute( + "SELECT prev_hash FROM full_blocks WHERE header_hash=?", + (header_hash,), + ) as cursor: + row = await cursor.fetchone() + if row is None: + raise KeyError("missing block in chain") + return bytes32(row[0]) + async def get_full_block(self, header_hash: bytes32) -> Optional[FullBlock]: cached: Optional[FullBlock] = self.block_cache.get(header_hash) if cached is not None: From 67986f991e8d1ef82a15d1e0078141da753e0585 Mon Sep 17 00:00:00 2001 From: Richard Kiss Date: Fri, 11 Jul 2025 19:03:43 -0700 Subject: [PATCH 04/16] ruff --- chia/consensus/block_store_protocol.py | 6 ++-- chia/full_node/block_store.py | 43 -------------------------- 2 files changed, 4 insertions(+), 45 deletions(-) diff --git a/chia/consensus/block_store_protocol.py b/chia/consensus/block_store_protocol.py index d6108df39f68..fa6c27a76422 100644 --- a/chia/consensus/block_store_protocol.py +++ b/chia/consensus/block_store_protocol.py @@ -42,7 +42,9 @@ async def is_fully_compactified(self, header_hash: bytes32) -> Optional[bool]: . async def replace_proof(self, header_hash: bytes32, block: FullBlock) -> None: ... async def count_compactified_blocks(self) -> int: ... async def count_uncompactified_blocks(self) -> int: ... - async def get_block_records_close_to_peak(self, blocks_n: int) -> tuple[dict[bytes32, BlockRecord], Optional[bytes32]]: ... + async def get_block_records_close_to_peak( + self, blocks_n: int + ) -> tuple[dict[bytes32, BlockRecord], Optional[bytes32]]: ... def get_host_parameter_limit(self) -> int: ... async def get_prev_hash(self, header_hash: bytes32) -> bytes32: ... - def transaction(self) -> AbstractAsyncContextManager[None]: ... \ No newline at end of file + def transaction(self) -> AbstractAsyncContextManager[None]: ... diff --git a/chia/full_node/block_store.py b/chia/full_node/block_store.py index 43b7ae892f46..428ae3d4031e 100644 --- a/chia/full_node/block_store.py +++ b/chia/full_node/block_store.py @@ -400,25 +400,6 @@ async def get_block_records_by_hash(self, header_hashes: list[bytes32]) -> list[ ret.append(all_blocks[hh]) return ret - async def get_prev_hash(self, header_hash: bytes32) -> bytes32: - """ - Returns the header hash preceeding the input header hash. - Throws an exception if the block is not present - """ - cached = self.block_cache.get(header_hash) - if cached is not None: - return cached.prev_header_hash - - async with self.db_wrapper.reader_no_transaction() as conn: - async with conn.execute( - "SELECT prev_hash FROM full_blocks WHERE header_hash=?", - (header_hash,), - ) as cursor: - row = await cursor.fetchone() - if row is None: - raise KeyError("missing block in chain") - return bytes32(row[0]) - async def get_block_bytes_by_hash(self, header_hashes: list[bytes32]) -> list[bytes]: """ Returns a list of Full Blocks block blobs, ordered by the same order in which header_hashes are passed in. @@ -549,30 +530,6 @@ async def get_peak(self) -> Optional[tuple[bytes32, uint32]]: return None return bytes32(peak_row[0]), uint32(peak_height[0]) - async def get_block_records_close_to_peak( - self, blocks_n: int - ) -> tuple[dict[bytes32, BlockRecord], Optional[bytes32]]: - """ - Returns a dictionary with all blocks that have height >= peak height - blocks_n, as well as the - peak header hash. Only blocks that are part of the main chain/current peak are included. - """ - - peak = await self.get_peak() - if peak is None: - return {}, None - - ret: dict[bytes32, BlockRecord] = {} - async with self.db_wrapper.reader_no_transaction() as conn: - async with conn.execute( - "SELECT header_hash, block_record FROM full_blocks WHERE height >= ? AND in_main_chain=1", - (peak[1] - blocks_n,), - ) as cursor: - for row in await cursor.fetchall(): - header_hash = bytes32(row[0]) - ret[header_hash] = BlockRecord.from_bytes(row[1]) - - return ret, peak[0] - async def set_peak(self, header_hash: bytes32) -> None: # We need to be in a sqlite transaction here. # Note: we do not commit this to the database yet, as we need to also change the coin store From edb6d3fcd495be36f1b6ed1215ec62131a8f0bc7 Mon Sep 17 00:00:00 2001 From: Richard Kiss Date: Fri, 11 Jul 2025 19:12:07 -0700 Subject: [PATCH 05/16] ruff --- chia/_tests/blockchain/blockchain_test_utils.py | 9 +++++---- chia/consensus/block_store_protocol.py | 4 ++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/chia/_tests/blockchain/blockchain_test_utils.py b/chia/_tests/blockchain/blockchain_test_utils.py index 3de9ff90553d..ba9ce2605663 100644 --- a/chia/_tests/blockchain/blockchain_test_utils.py +++ b/chia/_tests/blockchain/blockchain_test_utils.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import Optional +from typing import Optional, cast from chia_rs import FullBlock, SpendBundleConditions from chia_rs.sized_ints import uint32, uint64 @@ -10,19 +10,20 @@ from chia.consensus.blockchain import AddBlockResult, Blockchain from chia.consensus.difficulty_adjustment import get_next_sub_slot_iters_and_difficulty from chia.consensus.multiprocess_validation import PreValidationResult, pre_validate_block +from chia.full_node.block_store import BlockStore from chia.types.validation_state import ValidationState from chia.util.errors import Err async def check_block_store_invariant(bc: Blockchain): - db_wrapper = bc.block_store.db_wrapper + block_store = cast(BlockStore, bc.block_store) - if db_wrapper.db_version == 1: + if block_store.db_wrapper.db_version == 1: return in_chain = set() max_height = -1 - async with bc.block_store.transaction() as conn: + async with block_store.transaction() as conn: async with conn.execute("SELECT height, in_main_chain FROM full_blocks") as cursor: rows = await cursor.fetchall() for row in rows: diff --git a/chia/consensus/block_store_protocol.py b/chia/consensus/block_store_protocol.py index fa6c27a76422..312791b93a93 100644 --- a/chia/consensus/block_store_protocol.py +++ b/chia/consensus/block_store_protocol.py @@ -1,7 +1,7 @@ from __future__ import annotations from contextlib import AbstractAsyncContextManager -from typing import Optional, Protocol +from typing import Any, Optional, Protocol from chia_rs import BlockRecord, FullBlock, SubEpochChallengeSegment from chia_rs.sized_bytes import bytes32 @@ -47,4 +47,4 @@ async def get_block_records_close_to_peak( ) -> tuple[dict[bytes32, BlockRecord], Optional[bytes32]]: ... def get_host_parameter_limit(self) -> int: ... async def get_prev_hash(self, header_hash: bytes32) -> bytes32: ... - def transaction(self) -> AbstractAsyncContextManager[None]: ... + def transaction(self) -> AbstractAsyncContextManager[Any]: ... From f1f195e51a6d951a4c3e2c8eaee51db25f8c35be Mon Sep 17 00:00:00 2001 From: Richard Kiss Date: Fri, 11 Jul 2025 20:58:23 -0700 Subject: [PATCH 06/16] mypy --- chia/full_node/block_store.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chia/full_node/block_store.py b/chia/full_node/block_store.py index 428ae3d4031e..a9bef3b489cb 100644 --- a/chia/full_node/block_store.py +++ b/chia/full_node/block_store.py @@ -195,7 +195,7 @@ async def get_sub_epoch_challenge_segments( def get_host_parameter_limit(self) -> int: return self.db_wrapper.host_parameter_limit - def transaction(self) -> AbstractAsyncContextManager[None]: + def transaction(self) -> AbstractAsyncContextManager[Any]: return self.db_wrapper.writer() def get_block_from_cache(self, header_hash: bytes32) -> Optional[FullBlock]: From f1a3dc0bc94c7590193500b56ccdd5116cf61bee Mon Sep 17 00:00:00 2001 From: Richard Kiss Date: Fri, 11 Jul 2025 21:13:14 -0700 Subject: [PATCH 07/16] ruff --- chia/full_node/block_store.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chia/full_node/block_store.py b/chia/full_node/block_store.py index a9bef3b489cb..f830d40fbc41 100644 --- a/chia/full_node/block_store.py +++ b/chia/full_node/block_store.py @@ -4,7 +4,7 @@ import logging import sqlite3 from contextlib import AbstractAsyncContextManager -from typing import Optional +from typing import Any, Optional import aiosqlite import typing_extensions From 6a4f2a1e89337facc24f7059ea215dbea53b3bd4 Mon Sep 17 00:00:00 2001 From: Richard Kiss Date: Fri, 11 Jul 2025 23:20:41 -0700 Subject: [PATCH 08/16] minimize diffs --- chia/full_node/block_store.py | 38 +++++++++++++++++------------------ 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/chia/full_node/block_store.py b/chia/full_node/block_store.py index f830d40fbc41..8f91ac2ab589 100644 --- a/chia/full_node/block_store.py +++ b/chia/full_node/block_store.py @@ -233,25 +233,6 @@ def rollback_cache_block(self, header_hash: bytes32) -> None: # block to the cache yet pass - async def get_prev_hash(self, header_hash: bytes32) -> bytes32: - """ - Returns the header hash preceeding the input header hash. - Throws an exception if the block is not present - """ - cached = self.block_cache.get(header_hash) - if cached is not None: - return cached.prev_header_hash - - async with self.db_wrapper.reader_no_transaction() as conn: - async with conn.execute( - "SELECT prev_hash FROM full_blocks WHERE header_hash=?", - (header_hash,), - ) as cursor: - row = await cursor.fetchone() - if row is None: - raise KeyError("missing block in chain") - return bytes32(row[0]) - async def get_full_block(self, header_hash: bytes32) -> Optional[FullBlock]: cached: Optional[FullBlock] = self.block_cache.get(header_hash) if cached is not None: @@ -400,6 +381,25 @@ async def get_block_records_by_hash(self, header_hashes: list[bytes32]) -> list[ ret.append(all_blocks[hh]) return ret + async def get_prev_hash(self, header_hash: bytes32) -> bytes32: + """ + Returns the header hash preceeding the input header hash. + Throws an exception if the block is not present + """ + cached = self.block_cache.get(header_hash) + if cached is not None: + return cached.prev_header_hash + + async with self.db_wrapper.reader_no_transaction() as conn: + async with conn.execute( + "SELECT prev_hash FROM full_blocks WHERE header_hash=?", + (header_hash,), + ) as cursor: + row = await cursor.fetchone() + if row is None: + raise KeyError("missing block in chain") + return bytes32(row[0]) + async def get_block_bytes_by_hash(self, header_hashes: list[bytes32]) -> list[bytes]: """ Returns a list of Full Blocks block blobs, ordered by the same order in which header_hashes are passed in. From 2458f01404dd057f22e83c055440760ac8893c57 Mon Sep 17 00:00:00 2001 From: Richard Kiss Date: Fri, 11 Jul 2025 23:24:42 -0700 Subject: [PATCH 09/16] mindiffs --- chia/full_node/block_store.py | 48 +++++++++++++++++------------------ 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/chia/full_node/block_store.py b/chia/full_node/block_store.py index 8f91ac2ab589..8d422699830c 100644 --- a/chia/full_node/block_store.py +++ b/chia/full_node/block_store.py @@ -201,30 +201,6 @@ def transaction(self) -> AbstractAsyncContextManager[Any]: def get_block_from_cache(self, header_hash: bytes32) -> Optional[FullBlock]: return self.block_cache.get(header_hash) - async def get_block_records_close_to_peak( - self, blocks_n: int - ) -> tuple[dict[bytes32, BlockRecord], Optional[bytes32]]: - """ - Returns a dictionary with all blocks that have height >= peak height - blocks_n, as well as the - peak header hash. Only blocks that are part of the main chain/current peak are included. - """ - - peak = await self.get_peak() - if peak is None: - return {}, None - - ret: dict[bytes32, BlockRecord] = {} - async with self.db_wrapper.reader_no_transaction() as conn: - async with conn.execute( - "SELECT header_hash, block_record FROM full_blocks WHERE height >= ? AND in_main_chain=1", - (peak[1] - blocks_n,), - ) as cursor: - for row in await cursor.fetchall(): - header_hash = bytes32(row[0]) - ret[header_hash] = BlockRecord.from_bytes(row[1]) - - return ret, peak[0] - def rollback_cache_block(self, header_hash: bytes32) -> None: try: self.block_cache.remove(header_hash) @@ -530,6 +506,30 @@ async def get_peak(self) -> Optional[tuple[bytes32, uint32]]: return None return bytes32(peak_row[0]), uint32(peak_height[0]) + async def get_block_records_close_to_peak( + self, blocks_n: int + ) -> tuple[dict[bytes32, BlockRecord], Optional[bytes32]]: + """ + Returns a dictionary with all blocks that have height >= peak height - blocks_n, as well as the + peak header hash. Only blocks that are part of the main chain/current peak are included. + """ + + peak = await self.get_peak() + if peak is None: + return {}, None + + ret: dict[bytes32, BlockRecord] = {} + async with self.db_wrapper.reader_no_transaction() as conn: + async with conn.execute( + "SELECT header_hash, block_record FROM full_blocks WHERE height >= ? AND in_main_chain=1", + (peak[1] - blocks_n,), + ) as cursor: + for row in await cursor.fetchall(): + header_hash = bytes32(row[0]) + ret[header_hash] = BlockRecord.from_bytes(row[1]) + + return ret, peak[0] + async def set_peak(self, header_hash: bytes32) -> None: # We need to be in a sqlite transaction here. # Note: we do not commit this to the database yet, as we need to also change the coin store From 7fbc2f7552d14ccad501406c8df1cc6145a7366d Mon Sep 17 00:00:00 2001 From: Richard Kiss Date: Sat, 12 Jul 2025 12:19:54 -0700 Subject: [PATCH 10/16] Move batching and simplify protocol. --- chia/consensus/block_store_protocol.py | 2 +- chia/consensus/blockchain.py | 1 - chia/full_node/block_store.py | 3 --- 3 files changed, 1 insertion(+), 5 deletions(-) diff --git a/chia/consensus/block_store_protocol.py b/chia/consensus/block_store_protocol.py index 312791b93a93..d71be3f6fd50 100644 --- a/chia/consensus/block_store_protocol.py +++ b/chia/consensus/block_store_protocol.py @@ -45,6 +45,6 @@ async def count_uncompactified_blocks(self) -> int: ... async def get_block_records_close_to_peak( self, blocks_n: int ) -> tuple[dict[bytes32, BlockRecord], Optional[bytes32]]: ... - def get_host_parameter_limit(self) -> int: ... + async def get_prev_hash(self, header_hash: bytes32) -> bytes32: ... def transaction(self) -> AbstractAsyncContextManager[Any]: ... diff --git a/chia/consensus/blockchain.py b/chia/consensus/blockchain.py index 6a7b2e030ff3..28a2821ea62b 100644 --- a/chia/consensus/blockchain.py +++ b/chia/consensus/blockchain.py @@ -932,7 +932,6 @@ async def get_block_records_at(self, heights: list[uint32]) -> list[BlockRecord] gets block records by height (only blocks that are part of the chain) """ hashes: list[bytes32] = [] - assert batch_size < self.block_store.get_host_parameter_limit() for height in heights: header_hash: Optional[bytes32] = self.height_to_hash(height) if header_hash is None: diff --git a/chia/full_node/block_store.py b/chia/full_node/block_store.py index 8d422699830c..9ade7e7076c9 100644 --- a/chia/full_node/block_store.py +++ b/chia/full_node/block_store.py @@ -192,9 +192,6 @@ async def get_sub_epoch_challenge_segments( return challenge_segments return None - def get_host_parameter_limit(self) -> int: - return self.db_wrapper.host_parameter_limit - def transaction(self) -> AbstractAsyncContextManager[Any]: return self.db_wrapper.writer() From 6a92c7a71d237a2a1843c81b77765f84704dcb82 Mon Sep 17 00:00:00 2001 From: Richard Kiss Date: Mon, 14 Jul 2025 16:15:20 -0700 Subject: [PATCH 11/16] Move `GeneratorBlockInfo`. --- .../core/full_node/stores/test_block_store.py | 2 +- chia/consensus/block_store_protocol.py | 2 +- chia/consensus/generator_block_info.py | 16 ++++++++++++++++ chia/full_node/block_store.py | 3 ++- chia/full_node/full_block_utils.py | 10 +--------- tach.toml | 1 - 6 files changed, 21 insertions(+), 13 deletions(-) create mode 100644 chia/consensus/generator_block_info.py diff --git a/chia/_tests/core/full_node/stores/test_block_store.py b/chia/_tests/core/full_node/stores/test_block_store.py index 0bf2ba048f5a..2be105678d94 100644 --- a/chia/_tests/core/full_node/stores/test_block_store.py +++ b/chia/_tests/core/full_node/stores/test_block_store.py @@ -22,9 +22,9 @@ from chia.consensus.blockchain import AddBlockResult, Blockchain from chia.consensus.default_constants import DEFAULT_CONSTANTS from chia.consensus.full_block_to_block_record import header_block_to_sub_block_record +from chia.consensus.generator_block_info import GeneratorBlockInfo from chia.full_node.block_store import BlockStore from chia.full_node.coin_store import CoinStore -from chia.full_node.full_block_utils import GeneratorBlockInfo from chia.simulator.block_tools import BlockTools from chia.simulator.wallet_tools import WalletTool from chia.types.blockchain_format.serialized_program import SerializedProgram diff --git a/chia/consensus/block_store_protocol.py b/chia/consensus/block_store_protocol.py index d71be3f6fd50..327a9c2463e5 100644 --- a/chia/consensus/block_store_protocol.py +++ b/chia/consensus/block_store_protocol.py @@ -7,7 +7,7 @@ from chia_rs.sized_bytes import bytes32 from chia_rs.sized_ints import uint32 -from chia.full_node.full_block_utils import GeneratorBlockInfo +from chia.consensus.generator_block_info import GeneratorBlockInfo class BlockStoreProtocol(Protocol): diff --git a/chia/consensus/generator_block_info.py b/chia/consensus/generator_block_info.py new file mode 100644 index 000000000000..d0758408ceb1 --- /dev/null +++ b/chia/consensus/generator_block_info.py @@ -0,0 +1,16 @@ +from __future__ import annotations + +from dataclasses import dataclass +from typing import Optional + +from chia_rs.sized_bytes import bytes32 +from chia_rs.sized_ints import uint32 + +from chia.types.blockchain_format.serialized_program import SerializedProgram + + +@dataclass(frozen=True) +class GeneratorBlockInfo: + prev_header_hash: bytes32 + transactions_generator: Optional[SerializedProgram] + transactions_generator_ref_list: list[uint32] diff --git a/chia/full_node/block_store.py b/chia/full_node/block_store.py index 9ade7e7076c9..61174e977e77 100644 --- a/chia/full_node/block_store.py +++ b/chia/full_node/block_store.py @@ -13,7 +13,8 @@ from chia_rs.sized_bytes import bytes32 from chia_rs.sized_ints import uint32 -from chia.full_node.full_block_utils import GeneratorBlockInfo, block_info_from_block, generator_from_block +from chia.consensus.generator_block_info import GeneratorBlockInfo +from chia.full_node.full_block_utils import block_info_from_block, generator_from_block from chia.util.batches import to_batches from chia.util.db_wrapper import DBWrapper2, execute_fetchone from chia.util.errors import Err diff --git a/chia/full_node/full_block_utils.py b/chia/full_node/full_block_utils.py index 97f52fd23202..2c267ce777ae 100644 --- a/chia/full_node/full_block_utils.py +++ b/chia/full_node/full_block_utils.py @@ -1,6 +1,5 @@ from __future__ import annotations -from dataclasses import dataclass from typing import Callable, Optional from chia_rs import G1Element, G2Element, TransactionsInfo, serialized_length @@ -8,6 +7,7 @@ from chia_rs.sized_ints import uint32 from chiabip158 import PyBIP158 +from chia.consensus.generator_block_info import GeneratorBlockInfo from chia.types.blockchain_format.coin import Coin from chia.types.blockchain_format.serialized_program import SerializedProgram @@ -224,14 +224,6 @@ def generator_from_block(buf: memoryview) -> Optional[bytes]: return bytes(buf[:length]) -# this implements the BlockInfo protocol -@dataclass(frozen=True) -class GeneratorBlockInfo: - prev_header_hash: bytes32 - 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 diff --git a/tach.toml b/tach.toml index 3bcd7dd9d269..b15eac2220b0 100644 --- a/tach.toml +++ b/tach.toml @@ -22,7 +22,6 @@ path = "chia.consensus" depends_on = [ "chia.types", "chia.util", - { path = "chia.full_node", deprecated = false }, ] [[modules]] From 6cee19346fb7c8339f63ddb780f5078b4ea3385f Mon Sep 17 00:00:00 2001 From: Richard Kiss Date: Wed, 16 Jul 2025 15:43:06 -0700 Subject: [PATCH 12/16] Simplify protocol --- chia/consensus/block_store_protocol.py | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/chia/consensus/block_store_protocol.py b/chia/consensus/block_store_protocol.py index 327a9c2463e5..57cd83e7dd90 100644 --- a/chia/consensus/block_store_protocol.py +++ b/chia/consensus/block_store_protocol.py @@ -7,26 +7,17 @@ from chia_rs.sized_bytes import bytes32 from chia_rs.sized_ints import uint32 -from chia.consensus.generator_block_info import GeneratorBlockInfo - class BlockStoreProtocol(Protocol): async def add_full_block(self, header_hash: bytes32, block: FullBlock, block_record: BlockRecord) -> None: ... def get_block_from_cache(self, header_hash: bytes32) -> Optional[FullBlock]: ... async def get_full_block(self, header_hash: bytes32) -> Optional[FullBlock]: ... - async def get_full_block_bytes(self, header_hash: bytes32) -> Optional[bytes]: ... - async def get_full_blocks_at(self, heights: list[uint32]) -> list[FullBlock]: ... - async def get_block_info(self, header_hash: bytes32) -> Optional[GeneratorBlockInfo]: ... async def get_generator(self, header_hash: bytes32) -> Optional[bytes]: ... async def get_generators_at(self, heights: set[uint32]) -> dict[uint32, bytes]: ... async def get_block_record(self, header_hash: bytes32) -> Optional[BlockRecord]: ... async def get_block_records_in_range(self, start: int, stop: int) -> dict[bytes32, BlockRecord]: ... async def get_block_records_by_hash(self, header_hashes: list[bytes32]) -> list[BlockRecord]: ... - async def get_block_bytes_by_hash(self, header_hashes: list[bytes32]) -> list[bytes]: ... async def get_blocks_by_hash(self, header_hashes: list[bytes32]) -> list[FullBlock]: ... - async def get_peak(self) -> Optional[tuple[bytes32, uint32]]: ... - async def get_block_bytes_in_range(self, start: int, stop: int) -> list[bytes]: ... - async def get_random_not_compactified(self, number: int) -> list[int]: ... async def persist_sub_epoch_challenge_segments( self, ses_block_hash: bytes32, segments: list[SubEpochChallengeSegment] ) -> None: ... @@ -38,13 +29,8 @@ async def rollback(self, height: int) -> None: ... def rollback_cache_block(self, header_hash: bytes32) -> None: ... async def set_in_chain(self, header_hashes: list[tuple[bytes32]]) -> None: ... async def set_peak(self, header_hash: bytes32) -> None: ... - async def is_fully_compactified(self, header_hash: bytes32) -> Optional[bool]: ... - async def replace_proof(self, header_hash: bytes32, block: FullBlock) -> None: ... - async def count_compactified_blocks(self) -> int: ... - async def count_uncompactified_blocks(self) -> int: ... async def get_block_records_close_to_peak( self, blocks_n: int ) -> tuple[dict[bytes32, BlockRecord], Optional[bytes32]]: ... - async def get_prev_hash(self, header_hash: bytes32) -> bytes32: ... def transaction(self) -> AbstractAsyncContextManager[Any]: ... From 2c7c8845b40ae8c754b88beba18e45698f585ad7 Mon Sep 17 00:00:00 2001 From: Richard Kiss Date: Wed, 16 Jul 2025 16:17:09 -0700 Subject: [PATCH 13/16] Use `BlockStore` locally, for now --- chia/full_node/full_node.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/chia/full_node/full_node.py b/chia/full_node/full_node.py index 5d297ed89a6a..28a8c9c397d2 100644 --- a/chia/full_node/full_node.py +++ b/chia/full_node/full_node.py @@ -40,7 +40,6 @@ from chia.consensus.block_body_validation import ForkInfo from chia.consensus.block_creation import unfinished_block_to_full_block from chia.consensus.block_height_map import BlockHeightMap -from chia.consensus.block_store_protocol import BlockStoreProtocol from chia.consensus.blockchain import AddBlockResult, Blockchain, BlockchainMutexPriority, StateChangeSummary from chia.consensus.blockchain_interface import BlockchainInterface from chia.consensus.coin_store_protocol import CoinStoreProtocol @@ -400,7 +399,7 @@ async def manage(self) -> AsyncIterator[None]: await asyncio.gather(*self._segment_task_list, return_exceptions=True) @property - def block_store(self) -> BlockStoreProtocol: + def block_store(self) -> BlockStore: assert self._block_store is not None return self._block_store From 4ac3d583f1461df4876ebba75301e0ccad3472b1 Mon Sep 17 00:00:00 2001 From: Richard Kiss Date: Thu, 17 Jul 2025 15:03:36 -0700 Subject: [PATCH 14/16] Revert "Move `GeneratorBlockInfo`." This reverts commit f077801fc954e74ca3e5eedc2f7264f427e9332b. --- .../core/full_node/stores/test_block_store.py | 2 +- chia/consensus/generator_block_info.py | 16 ---------------- chia/full_node/block_store.py | 3 +-- chia/full_node/full_block_utils.py | 10 +++++++++- 4 files changed, 11 insertions(+), 20 deletions(-) delete mode 100644 chia/consensus/generator_block_info.py diff --git a/chia/_tests/core/full_node/stores/test_block_store.py b/chia/_tests/core/full_node/stores/test_block_store.py index 2be105678d94..0bf2ba048f5a 100644 --- a/chia/_tests/core/full_node/stores/test_block_store.py +++ b/chia/_tests/core/full_node/stores/test_block_store.py @@ -22,9 +22,9 @@ from chia.consensus.blockchain import AddBlockResult, Blockchain from chia.consensus.default_constants import DEFAULT_CONSTANTS from chia.consensus.full_block_to_block_record import header_block_to_sub_block_record -from chia.consensus.generator_block_info import GeneratorBlockInfo from chia.full_node.block_store import BlockStore from chia.full_node.coin_store import CoinStore +from chia.full_node.full_block_utils import GeneratorBlockInfo from chia.simulator.block_tools import BlockTools from chia.simulator.wallet_tools import WalletTool from chia.types.blockchain_format.serialized_program import SerializedProgram diff --git a/chia/consensus/generator_block_info.py b/chia/consensus/generator_block_info.py deleted file mode 100644 index d0758408ceb1..000000000000 --- a/chia/consensus/generator_block_info.py +++ /dev/null @@ -1,16 +0,0 @@ -from __future__ import annotations - -from dataclasses import dataclass -from typing import Optional - -from chia_rs.sized_bytes import bytes32 -from chia_rs.sized_ints import uint32 - -from chia.types.blockchain_format.serialized_program import SerializedProgram - - -@dataclass(frozen=True) -class GeneratorBlockInfo: - prev_header_hash: bytes32 - transactions_generator: Optional[SerializedProgram] - transactions_generator_ref_list: list[uint32] diff --git a/chia/full_node/block_store.py b/chia/full_node/block_store.py index 61174e977e77..9ade7e7076c9 100644 --- a/chia/full_node/block_store.py +++ b/chia/full_node/block_store.py @@ -13,8 +13,7 @@ from chia_rs.sized_bytes import bytes32 from chia_rs.sized_ints import uint32 -from chia.consensus.generator_block_info import GeneratorBlockInfo -from chia.full_node.full_block_utils import block_info_from_block, generator_from_block +from chia.full_node.full_block_utils import GeneratorBlockInfo, block_info_from_block, generator_from_block from chia.util.batches import to_batches from chia.util.db_wrapper import DBWrapper2, execute_fetchone from chia.util.errors import Err diff --git a/chia/full_node/full_block_utils.py b/chia/full_node/full_block_utils.py index 2c267ce777ae..97f52fd23202 100644 --- a/chia/full_node/full_block_utils.py +++ b/chia/full_node/full_block_utils.py @@ -1,5 +1,6 @@ from __future__ import annotations +from dataclasses import dataclass from typing import Callable, Optional from chia_rs import G1Element, G2Element, TransactionsInfo, serialized_length @@ -7,7 +8,6 @@ from chia_rs.sized_ints import uint32 from chiabip158 import PyBIP158 -from chia.consensus.generator_block_info import GeneratorBlockInfo from chia.types.blockchain_format.coin import Coin from chia.types.blockchain_format.serialized_program import SerializedProgram @@ -224,6 +224,14 @@ def generator_from_block(buf: memoryview) -> Optional[bytes]: return bytes(buf[:length]) +# this implements the BlockInfo protocol +@dataclass(frozen=True) +class GeneratorBlockInfo: + prev_header_hash: bytes32 + 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 From 2c71a1fd52ece4733c53edfcf1abf241f6b4033e Mon Sep 17 00:00:00 2001 From: Richard Kiss Date: Fri, 18 Jul 2025 15:41:14 -0700 Subject: [PATCH 15/16] Improve exceptions --- chia/full_node/block_store.py | 7 ++++++- chia/full_node/full_node_api.py | 1 + 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/chia/full_node/block_store.py b/chia/full_node/block_store.py index 9ade7e7076c9..6a88d86d4970 100644 --- a/chia/full_node/block_store.py +++ b/chia/full_node/block_store.py @@ -19,6 +19,11 @@ from chia.util.errors import Err from chia.util.lru_cache import LRUCache + +class UnsupportedDatabaseVersionError(Exception): + """Raised when a method is called with an unsupported database version.""" + + log = logging.getLogger(__name__) @@ -479,7 +484,7 @@ async def get_block_bytes_in_range( """ if self.db_wrapper.db_version != 2: - raise NotImplementedError("get_block_bytes_in_range requires DB version 2") + raise UnsupportedDatabaseVersionError("get_block_bytes_in_range requires DB version 2") async with self.db_wrapper.reader_no_transaction() as conn: async with conn.execute( "SELECT block FROM full_blocks WHERE height >= ? AND height <= ? AND in_main_chain=1", diff --git a/chia/full_node/full_node_api.py b/chia/full_node/full_node_api.py index dc435c436ced..7614d46817f6 100644 --- a/chia/full_node/full_node_api.py +++ b/chia/full_node/full_node_api.py @@ -41,6 +41,7 @@ from chia.consensus.get_block_generator import get_block_generator from chia.consensus.pot_iterations import calculate_ip_iters, calculate_iterations_quality, calculate_sp_iters from chia.consensus.signage_point import SignagePoint +from chia.full_node.block_store import UnsupportedDatabaseVersionError from chia.full_node.coin_store import CoinStore from chia.full_node.fee_estimator_interface import FeeEstimatorInterface from chia.full_node.full_block_utils import get_height_and_tx_status_from_block, header_block_from_block From c97a28225dc669ff87538ed996b0b25990d98834 Mon Sep 17 00:00:00 2001 From: Richard Kiss Date: Fri, 1 Aug 2025 14:52:00 -0700 Subject: [PATCH 16/16] fixup --- chia/consensus/blockchain.py | 1 - chia/full_node/block_store.py | 1 - chia/full_node/full_node_api.py | 1 - 3 files changed, 3 deletions(-) diff --git a/chia/consensus/blockchain.py b/chia/consensus/blockchain.py index 28a2821ea62b..899affb655cc 100644 --- a/chia/consensus/blockchain.py +++ b/chia/consensus/blockchain.py @@ -421,7 +421,6 @@ async def add_block( try: # Always add the block to the database - async with self.block_store.transaction(): async with self.block_store.transaction(): # Perform the DB operations to update the state, and rollback if something goes wrong await self.block_store.add_full_block(header_hash, block, block_record) diff --git a/chia/full_node/block_store.py b/chia/full_node/block_store.py index 6a88d86d4970..c299c151ed14 100644 --- a/chia/full_node/block_store.py +++ b/chia/full_node/block_store.py @@ -6,7 +6,6 @@ from contextlib import AbstractAsyncContextManager from typing import Any, Optional -import aiosqlite import typing_extensions import zstd from chia_rs import BlockRecord, FullBlock, SubEpochChallengeSegment, SubEpochSegments diff --git a/chia/full_node/full_node_api.py b/chia/full_node/full_node_api.py index 7614d46817f6..dc435c436ced 100644 --- a/chia/full_node/full_node_api.py +++ b/chia/full_node/full_node_api.py @@ -41,7 +41,6 @@ from chia.consensus.get_block_generator import get_block_generator from chia.consensus.pot_iterations import calculate_ip_iters, calculate_iterations_quality, calculate_sp_iters from chia.consensus.signage_point import SignagePoint -from chia.full_node.block_store import UnsupportedDatabaseVersionError from chia.full_node.coin_store import CoinStore from chia.full_node.fee_estimator_interface import FeeEstimatorInterface from chia.full_node.full_block_utils import get_height_and_tx_status_from_block, header_block_from_block