mirror of
https://github.com/Chia-Network/chia-blockchain.git
synced 2026-08-24 10:05:29 -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.blockchain import AddBlockResult, Blockchain
|
||||||
from chia.consensus.difficulty_adjustment import get_next_sub_slot_iters_and_difficulty
|
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.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.types.validation_state import ValidationState
|
||||||
from chia.util.errors import Err
|
from chia.util.errors import Err
|
||||||
|
|
||||||
|
|
||||||
async def check_block_store_invariant(bc: Blockchain) -> None:
|
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
|
db_wrapper = bc.block_store.db_wrapper
|
||||||
|
|
||||||
if db_wrapper.db_version == 1:
|
if db_wrapper.db_version == 1:
|
||||||
@@ -20,7 +23,7 @@ async def check_block_store_invariant(bc: Blockchain) -> None:
|
|||||||
|
|
||||||
in_chain = set()
|
in_chain = set()
|
||||||
max_height = -1
|
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:
|
async with conn.execute("SELECT height, in_main_chain FROM full_blocks") as cursor:
|
||||||
rows = await cursor.fetchall()
|
rows = await cursor.fetchall()
|
||||||
for row in rows:
|
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_body_validation import ForkInfo, validate_block_body
|
||||||
from chia.consensus.block_header_validation import validate_unfinished_header_block
|
from chia.consensus.block_header_validation import validate_unfinished_header_block
|
||||||
from chia.consensus.block_height_map import BlockHeightMap
|
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_interface import MMRManagerProtocol
|
||||||
from chia.consensus.blockchain_mmr import BlockchainMMRManager
|
from chia.consensus.blockchain_mmr import BlockchainMMRManager
|
||||||
from chia.consensus.coin_store_protocol import CoinStoreProtocol
|
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_challenge import pre_sp_tx_block_height
|
||||||
from chia.consensus.get_block_generator import get_block_generator
|
from chia.consensus.get_block_generator import get_block_generator
|
||||||
from chia.consensus.multiprocess_validation import PreValidationResult
|
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.coin import Coin
|
||||||
from chia.types.blockchain_format.vdf import VDFInfo
|
from chia.types.blockchain_format.vdf import VDFInfo
|
||||||
from chia.types.generator_types import BlockGenerator
|
from chia.types.generator_types import BlockGenerator
|
||||||
@@ -105,7 +105,7 @@ class Blockchain:
|
|||||||
# Unspent Store
|
# Unspent Store
|
||||||
coin_store: CoinStoreProtocol
|
coin_store: CoinStoreProtocol
|
||||||
# Store
|
# Store
|
||||||
block_store: BlockStore
|
block_store: BlockStoreProtocol
|
||||||
mmr_manager: MMRManagerProtocol
|
mmr_manager: MMRManagerProtocol
|
||||||
# Used to verify blocks in parallel
|
# Used to verify blocks in parallel
|
||||||
pool: Executor
|
pool: Executor
|
||||||
@@ -124,7 +124,7 @@ class Blockchain:
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
async def create(
|
async def create(
|
||||||
coin_store: CoinStoreProtocol,
|
coin_store: CoinStoreProtocol,
|
||||||
block_store: BlockStore,
|
block_store: BlockStoreProtocol,
|
||||||
height_map: BlockHeightMap,
|
height_map: BlockHeightMap,
|
||||||
consensus_constants: ConsensusConstants,
|
consensus_constants: ConsensusConstants,
|
||||||
pool: Executor,
|
pool: Executor,
|
||||||
|
|||||||
@@ -3,15 +3,17 @@ from __future__ import annotations
|
|||||||
import dataclasses
|
import dataclasses
|
||||||
import logging
|
import logging
|
||||||
import sqlite3
|
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 typing_extensions
|
||||||
import zstd
|
import zstd
|
||||||
from chia_rs import BlockRecord, FullBlock, SubEpochChallengeSegment, SubEpochSegments
|
from chia_rs import BlockRecord, FullBlock, SubEpochChallengeSegment, SubEpochSegments
|
||||||
from chia_rs.sized_bytes import bytes32
|
from chia_rs.sized_bytes import bytes32
|
||||||
from chia_rs.sized_ints import uint32
|
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.full_node.full_block_utils import GeneratorBlockInfo, block_info_from_block, generator_from_block
|
||||||
from chia.util.batches import to_batches
|
from chia.util.batches import to_batches
|
||||||
from chia.util.db_wrapper import DBWrapper2, execute_fetchone
|
from chia.util.db_wrapper import DBWrapper2, execute_fetchone
|
||||||
@@ -38,6 +40,9 @@ def decompress_blob(block_bytes: bytes) -> bytes:
|
|||||||
@typing_extensions.final
|
@typing_extensions.final
|
||||||
@dataclasses.dataclass
|
@dataclasses.dataclass
|
||||||
class BlockStore:
|
class BlockStore:
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
_protocol_check: ClassVar[BlockStoreProtocol] = cast("BlockStore", None)
|
||||||
|
|
||||||
block_cache: LRUCache[bytes32, FullBlock]
|
block_cache: LRUCache[bytes32, FullBlock]
|
||||||
db_wrapper: DBWrapper2
|
db_wrapper: DBWrapper2
|
||||||
ses_challenge_cache: LRUCache[bytes32, list[SubEpochChallengeSegment]]
|
ses_challenge_cache: LRUCache[bytes32, list[SubEpochChallengeSegment]]
|
||||||
@@ -191,8 +196,12 @@ class BlockStore:
|
|||||||
return challenge_segments
|
return challenge_segments
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def transaction(self) -> AbstractAsyncContextManager[aiosqlite.Connection]:
|
@asynccontextmanager
|
||||||
return self.db_wrapper.writer()
|
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:
|
def get_block_from_cache(self, header_hash: bytes32) -> FullBlock | None:
|
||||||
return self.block_cache.get(header_hash)
|
return self.block_cache.get(header_hash)
|
||||||
|
|||||||
Reference in New Issue
Block a user