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 new file mode 100644 index 000000000000..57cd83e7dd90 --- /dev/null +++ b/chia/consensus/block_store_protocol.py @@ -0,0 +1,36 @@ +from __future__ import annotations + +from contextlib import AbstractAsyncContextManager +from typing import Any, Optional, Protocol + +from chia_rs import BlockRecord, FullBlock, SubEpochChallengeSegment +from chia_rs.sized_bytes import bytes32 +from chia_rs.sized_ints import uint32 + + +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_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_blocks_by_hash(self, header_hashes: list[bytes32]) -> list[FullBlock]: ... + 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 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]: ... 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 4fef5b9a4006..c299c151ed14 100644 --- a/chia/full_node/block_store.py +++ b/chia/full_node/block_store.py @@ -4,9 +4,8 @@ import logging import sqlite3 from contextlib import AbstractAsyncContextManager -from typing import Optional +from typing import Any, Optional -import aiosqlite import typing_extensions import zstd from chia_rs import BlockRecord, FullBlock, SubEpochChallengeSegment, SubEpochSegments @@ -19,6 +18,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__) @@ -192,7 +196,7 @@ async def get_sub_epoch_challenge_segments( return challenge_segments return None - def transaction(self) -> AbstractAsyncContextManager[aiosqlite.Connection]: + def transaction(self) -> AbstractAsyncContextManager[Any]: return self.db_wrapper.writer() def get_block_from_cache(self, header_hash: bytes32) -> Optional[FullBlock]: @@ -478,7 +482,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 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/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]]