mirror of
https://github.com/Chia-Network/chia-blockchain.git
synced 2026-08-24 02:24:23 -05:00
Add BlockStoreProtocol; drop consensus dependency on chia.full_node (#21169)
* Add BlockStoreProtocol; drop consensus dependency on chia.full_node Co-authored-by: Cursor <cursoragent@cursor.com> * Assert isinstance directly on bc.block_store Review feedback: drop the intermediate block_store assignment; mypy narrows the attribute expression after the isinstance assert. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -8,11 +8,14 @@ from chia.consensus.block_body_validation import ForkInfo
|
||||
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) -> None:
|
||||
# this checks sqlite-level invariants, so it needs the concrete store
|
||||
assert isinstance(bc.block_store, BlockStore)
|
||||
db_wrapper = bc.block_store.db_wrapper
|
||||
|
||||
if db_wrapper.db_version == 1:
|
||||
@@ -20,7 +23,7 @@ async def check_block_store_invariant(bc: Blockchain) -> None:
|
||||
|
||||
in_chain = set()
|
||||
max_height = -1
|
||||
async with bc.block_store.transaction() as conn:
|
||||
async with db_wrapper.writer() as conn:
|
||||
async with conn.execute("SELECT height, in_main_chain FROM full_blocks") as cursor:
|
||||
rows = await cursor.fetchall()
|
||||
for row in rows:
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from contextlib import AbstractAsyncContextManager
|
||||
from typing import 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):
|
||||
"""
|
||||
The block store interface used by `chia.consensus`.
|
||||
This is a substitute for importing from chia.full_node.block_store directly.
|
||||
|
||||
The concrete `BlockStore` has a larger surface (block blobs by range,
|
||||
compactification queries, etc.), but those methods serve peer sync and
|
||||
RPCs, not consensus, so they are not part of this protocol.
|
||||
"""
|
||||
|
||||
async def add_full_block(self, header_hash: bytes32, block: FullBlock, block_record: BlockRecord) -> None: ...
|
||||
|
||||
async def get_block_record(self, header_hash: bytes32) -> BlockRecord | None: ...
|
||||
|
||||
async def get_block_records_by_hash(self, header_hashes: list[bytes32]) -> list[BlockRecord]: ...
|
||||
|
||||
async def get_block_records_in_range(self, start: int, stop: int) -> dict[bytes32, BlockRecord]: ...
|
||||
|
||||
async def get_block_records_close_to_peak(
|
||||
self, blocks_n: int
|
||||
) -> tuple[dict[bytes32, BlockRecord], bytes32 | None]: ...
|
||||
|
||||
async def get_prev_hash(self, header_hash: bytes32) -> bytes32: ...
|
||||
|
||||
async def get_full_block(self, header_hash: bytes32) -> FullBlock | None: ...
|
||||
|
||||
async def get_blocks_by_hash(self, header_hashes: list[bytes32]) -> list[FullBlock]: ...
|
||||
|
||||
async def get_generator(self, header_hash: bytes32) -> bytes | None: ...
|
||||
|
||||
async def get_generators_at(self, heights: set[uint32]) -> dict[uint32, bytes]: ...
|
||||
|
||||
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: ...
|
||||
|
||||
def transaction(self) -> AbstractAsyncContextManager[None]:
|
||||
"""
|
||||
A write transaction scope. Store methods called within the scope are
|
||||
atomic. The context manager deliberately yields None: the underlying
|
||||
database connection is an implementation detail of the store.
|
||||
"""
|
||||
...
|
||||
|
||||
def get_block_from_cache(self, header_hash: bytes32) -> FullBlock | None: ...
|
||||
|
||||
def rollback_cache_block(self, header_hash: bytes32) -> None: ...
|
||||
|
||||
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
|
||||
) -> list[SubEpochChallengeSegment] | None: ...
|
||||
@@ -28,6 +28,7 @@ from chia_rs.sized_ints import uint16, uint32, uint64, uint128
|
||||
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.blockchain_interface import MMRManagerProtocol
|
||||
from chia.consensus.blockchain_mmr import BlockchainMMRManager
|
||||
from chia.consensus.coin_store_protocol import CoinStoreProtocol
|
||||
@@ -38,7 +39,6 @@ from chia.consensus.generator_tools import get_block_header
|
||||
from chia.consensus.get_block_challenge import pre_sp_tx_block_height
|
||||
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.generator_types import BlockGenerator
|
||||
@@ -105,7 +105,7 @@ class Blockchain:
|
||||
# Unspent Store
|
||||
coin_store: CoinStoreProtocol
|
||||
# Store
|
||||
block_store: BlockStore
|
||||
block_store: BlockStoreProtocol
|
||||
mmr_manager: MMRManagerProtocol
|
||||
# Used to verify blocks in parallel
|
||||
pool: Executor
|
||||
@@ -124,7 +124,7 @@ class Blockchain:
|
||||
@staticmethod
|
||||
async def create(
|
||||
coin_store: CoinStoreProtocol,
|
||||
block_store: BlockStore,
|
||||
block_store: BlockStoreProtocol,
|
||||
height_map: BlockHeightMap,
|
||||
consensus_constants: ConsensusConstants,
|
||||
pool: Executor,
|
||||
|
||||
@@ -3,15 +3,17 @@ from __future__ import annotations
|
||||
import dataclasses
|
||||
import logging
|
||||
import sqlite3
|
||||
from contextlib import AbstractAsyncContextManager
|
||||
from collections.abc import AsyncIterator
|
||||
from contextlib import asynccontextmanager
|
||||
from typing import TYPE_CHECKING, ClassVar, cast
|
||||
|
||||
import aiosqlite
|
||||
import typing_extensions
|
||||
import zstd
|
||||
from chia_rs import BlockRecord, FullBlock, SubEpochChallengeSegment, SubEpochSegments
|
||||
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
|
||||
@@ -38,6 +40,9 @@ def decompress_blob(block_bytes: bytes) -> bytes:
|
||||
@typing_extensions.final
|
||||
@dataclasses.dataclass
|
||||
class BlockStore:
|
||||
if TYPE_CHECKING:
|
||||
_protocol_check: ClassVar[BlockStoreProtocol] = cast("BlockStore", None)
|
||||
|
||||
block_cache: LRUCache[bytes32, FullBlock]
|
||||
db_wrapper: DBWrapper2
|
||||
ses_challenge_cache: LRUCache[bytes32, list[SubEpochChallengeSegment]]
|
||||
@@ -191,8 +196,12 @@ class BlockStore:
|
||||
return challenge_segments
|
||||
return None
|
||||
|
||||
def transaction(self) -> AbstractAsyncContextManager[aiosqlite.Connection]:
|
||||
return self.db_wrapper.writer()
|
||||
@asynccontextmanager
|
||||
async def transaction(self) -> AsyncIterator[None]:
|
||||
# the database connection is deliberately not exposed: callers only
|
||||
# get an atomic scope, not access to the underlying database
|
||||
async with self.db_wrapper.writer():
|
||||
yield
|
||||
|
||||
def get_block_from_cache(self, header_hash: bytes32) -> FullBlock | None:
|
||||
return self.block_cache.get(header_hash)
|
||||
|
||||
Reference in New Issue
Block a user