mirror of
https://github.com/Chia-Network/chia-blockchain.git
synced 2026-08-24 10:05:29 -05:00
Slim CoinStoreProtocol down to the methods consensus uses (#21168)
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -15,6 +15,7 @@ import pytest
|
||||
from chia_rs import (
|
||||
AugSchemeMPL,
|
||||
BlockRecord,
|
||||
CoinRecord,
|
||||
ConsensusConstants,
|
||||
EndOfSubSlotBundle,
|
||||
FullBlock,
|
||||
@@ -54,6 +55,7 @@ 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, pre_validate_block
|
||||
from chia.consensus.pot_iterations import is_overflow_block
|
||||
from chia.full_node.coin_store import CoinStore
|
||||
from chia.simulator.block_tools import BlockTools, create_block_tools_async
|
||||
from chia.simulator.keyring import TempKeyring
|
||||
from chia.simulator.vdf_prover import get_vdf_info_and_proof
|
||||
@@ -86,6 +88,14 @@ log = logging.getLogger(__name__)
|
||||
bad_element = ClassgroupElement.create(b"\x00")
|
||||
|
||||
|
||||
async def get_coin_record(b: Blockchain, coin_id: bytes32) -> CoinRecord | None:
|
||||
# single-record lookup is not part of the consensus coin store protocol,
|
||||
# but tests know the concrete store
|
||||
coin_store = b.coin_store
|
||||
assert isinstance(coin_store, CoinStore)
|
||||
return await coin_store.get_coin_record(coin_id)
|
||||
|
||||
|
||||
@asynccontextmanager
|
||||
async def make_empty_blockchain(constants: ConsensusConstants) -> AsyncIterator[Blockchain]:
|
||||
"""
|
||||
@@ -2166,7 +2176,7 @@ class TestBodyValidation:
|
||||
|
||||
if expected == AddBlockResult.NEW_PEAK:
|
||||
# ensure coin was in fact spent
|
||||
c = await b.coin_store.get_coin_record(coin.name())
|
||||
c = await get_coin_record(b, coin.name())
|
||||
assert c is not None and c.spent
|
||||
|
||||
@pytest.mark.anyio
|
||||
@@ -2372,10 +2382,10 @@ class TestBodyValidation:
|
||||
|
||||
if expected == AddBlockResult.NEW_PEAK:
|
||||
# ensure coin1 was in fact spent
|
||||
c = await b.coin_store.get_coin_record(coin1.name())
|
||||
c = await get_coin_record(b, coin1.name())
|
||||
assert c is not None and c.spent
|
||||
# ensure coin2 was NOT spent
|
||||
c = await b.coin_store.get_coin_record(coin2.name())
|
||||
c = await get_coin_record(b, coin2.name())
|
||||
assert c is not None and not c.spent
|
||||
|
||||
@pytest.mark.anyio
|
||||
@@ -3182,9 +3192,9 @@ class TestBodyValidation:
|
||||
)
|
||||
|
||||
# ephemeral coin is spent
|
||||
first_coin = await b.coin_store.get_coin_record(new_coin.name())
|
||||
first_coin = await get_coin_record(b, new_coin.name())
|
||||
assert first_coin is not None and first_coin.spent
|
||||
second_coin = await b.coin_store.get_coin_record(tx_2.additions()[0].name())
|
||||
second_coin = await get_coin_record(b, tx_2.additions()[0].name())
|
||||
assert second_coin is not None and not second_coin.spent
|
||||
|
||||
farmer_coin = create_farmer_coin(
|
||||
@@ -3200,7 +3210,7 @@ class TestBodyValidation:
|
||||
)
|
||||
await _validate_and_add_block(b, blocks_reorg[-1])
|
||||
|
||||
farmer_coin_record = await b.coin_store.get_coin_record(farmer_coin.name())
|
||||
farmer_coin_record = await get_coin_record(b, farmer_coin.name())
|
||||
assert farmer_coin_record is not None and farmer_coin_record.spent
|
||||
|
||||
@pytest.mark.anyio
|
||||
@@ -4059,11 +4069,11 @@ async def test_chain_failed_rollback(empty_blockchain: Blockchain, bt: BlockTool
|
||||
await _validate_and_add_block(b, block, expected_result=AddBlockResult.ADDED_AS_ORPHAN, fork_info=fork_info)
|
||||
|
||||
# Incorrectly set the height as spent in DB to trigger an error
|
||||
print(f"{await b.coin_store.get_coin_record(spend_bundle.coin_spends[0].coin.name())}")
|
||||
print(f"{await get_coin_record(b, spend_bundle.coin_spends[0].coin.name())}")
|
||||
print(spend_bundle.coin_spends[0].coin.name())
|
||||
# await b.coin_store._set_spent([spend_bundle.coin_spends[0].coin.name()], 8)
|
||||
await b.coin_store.rollback_to_block(2)
|
||||
print(f"{await b.coin_store.get_coin_record(spend_bundle.coin_spends[0].coin.name())}")
|
||||
print(f"{await get_coin_record(b, spend_bundle.coin_spends[0].coin.name())}")
|
||||
|
||||
fork_block = blocks_reorg_chain[10 - 1]
|
||||
# fork_info = ForkInfo(fork_block.height, fork_block.height, fork_block.header_hash)
|
||||
|
||||
@@ -3,18 +3,21 @@ from __future__ import annotations
|
||||
from collections.abc import Collection
|
||||
from typing import Protocol
|
||||
|
||||
from chia_rs import CoinRecord, CoinState
|
||||
from chia_rs import CoinRecord
|
||||
from chia_rs.sized_bytes import bytes32
|
||||
from chia_rs.sized_ints import uint32, uint64
|
||||
|
||||
from chia.types.blockchain_format.coin import Coin
|
||||
from chia.types.mempool_item import UnspentLineageInfo
|
||||
|
||||
|
||||
class CoinStoreProtocol(Protocol):
|
||||
"""
|
||||
Protocol defining the interface for CoinStore.
|
||||
The coin store interface used by `chia.consensus`.
|
||||
This is a substitute for importing from chia.full_node.coin_store directly.
|
||||
|
||||
The concrete `CoinStore` has a much larger surface (puzzle hash queries,
|
||||
coin states, lineage lookups, etc.), but those methods serve RPCs and the
|
||||
wallet protocol, not consensus, so they are not part of this protocol.
|
||||
"""
|
||||
|
||||
async def new_block(
|
||||
@@ -29,11 +32,6 @@ class CoinStoreProtocol(Protocol):
|
||||
Add a new block to the coin store
|
||||
"""
|
||||
|
||||
async def get_coin_record(self, coin_id: bytes32) -> CoinRecord | None:
|
||||
"""
|
||||
Returns the coin record for the specified coin id
|
||||
"""
|
||||
|
||||
async def get_coin_records(self, coin_ids: Collection[bytes32]) -> list[CoinRecord]:
|
||||
"""
|
||||
Returns the coin records for the specified coin ids
|
||||
@@ -49,104 +47,7 @@ class CoinStoreProtocol(Protocol):
|
||||
Returns the coins removed at a specific height
|
||||
"""
|
||||
|
||||
async def get_coin_records_by_puzzle_hash(
|
||||
self,
|
||||
include_spent_coins: bool,
|
||||
puzzle_hash: bytes32,
|
||||
start_height: uint32 = ...,
|
||||
end_height: uint32 = ...,
|
||||
) -> list[CoinRecord]:
|
||||
"""
|
||||
Returns the coin records for a specific puzzle hash
|
||||
"""
|
||||
|
||||
async def get_coin_records_by_puzzle_hashes(
|
||||
self,
|
||||
coins: bool,
|
||||
puzzle_hashes: list[bytes32],
|
||||
start_height: uint32 = ...,
|
||||
end_height: uint32 = ...,
|
||||
) -> list[CoinRecord]:
|
||||
"""
|
||||
Returns the coin records for a list of puzzle hashes
|
||||
"""
|
||||
|
||||
async def get_coin_records_by_names(
|
||||
self,
|
||||
include_spent_coins: bool,
|
||||
names: list[bytes32],
|
||||
start_height: uint32 = ...,
|
||||
end_height: uint32 = ...,
|
||||
) -> list[CoinRecord]:
|
||||
"""
|
||||
Returns the coin records for a list of coin names
|
||||
"""
|
||||
|
||||
async def get_coin_states_by_puzzle_hashes(
|
||||
self,
|
||||
include_spent_coins: bool,
|
||||
puzzle_hashes: set[bytes32],
|
||||
min_height: uint32 = uint32(0),
|
||||
*,
|
||||
max_items: int = ...,
|
||||
) -> set[CoinState]:
|
||||
"""
|
||||
Returns the coin states for a set of puzzle hashes
|
||||
"""
|
||||
|
||||
async def get_coin_records_by_parent_ids(
|
||||
self,
|
||||
include_spent_coins: bool,
|
||||
parent_ids: list[bytes32],
|
||||
start_height: uint32 = ...,
|
||||
end_height: uint32 = ...,
|
||||
*,
|
||||
max_items: int = ...,
|
||||
) -> list[CoinRecord]:
|
||||
"""
|
||||
Returns the coin records for a list of parent ids
|
||||
"""
|
||||
|
||||
async def get_coin_states_by_ids(
|
||||
self,
|
||||
include_spent_coins: bool,
|
||||
coin_ids: Collection[bytes32],
|
||||
min_height: uint32 = uint32(0),
|
||||
*,
|
||||
max_height: uint32 = ...,
|
||||
max_items: int = ...,
|
||||
) -> list[CoinState]:
|
||||
"""
|
||||
Returns the coin states for a collection of coin ids
|
||||
"""
|
||||
|
||||
async def batch_coin_states_by_puzzle_hashes(
|
||||
self,
|
||||
puzzle_hashes: list[bytes32],
|
||||
*,
|
||||
min_height: uint32 = ...,
|
||||
include_spent: bool = ...,
|
||||
include_unspent: bool = ...,
|
||||
include_hinted: bool = ...,
|
||||
min_amount: uint64 = ...,
|
||||
max_items: int = ...,
|
||||
) -> tuple[list[CoinState], uint32 | None]:
|
||||
"""
|
||||
Returns the coin states, as well as the next block height (or `None` if finished).
|
||||
"""
|
||||
|
||||
async def get_unspent_lineage_info_for_puzzle_hash(self, puzzle_hash: bytes32) -> UnspentLineageInfo | None:
|
||||
"""
|
||||
Lookup the most recent unspent lineage that matches a puzzle hash
|
||||
"""
|
||||
|
||||
async def rollback_to_block(self, block_index: int) -> dict[bytes32, CoinRecord]:
|
||||
"""
|
||||
Rolls back the blockchain to the specified block index
|
||||
"""
|
||||
|
||||
# DEPRECATED: do not use in new code
|
||||
async def is_empty(self) -> bool:
|
||||
"""
|
||||
Returns True if the coin store is empty
|
||||
"""
|
||||
|
||||
@@ -44,7 +44,6 @@ from chia.consensus.block_creation import unfinished_block_to_full_block_with_mm
|
||||
from chia.consensus.block_height_map import BlockHeightMap
|
||||
from chia.consensus.blockchain import AddBlockResult, Blockchain, BlockchainMutexPriority, StateChangeSummary
|
||||
from chia.consensus.blockchain_interface import BlockchainInterface
|
||||
from chia.consensus.coin_store_protocol import CoinStoreProtocol
|
||||
from chia.consensus.condition_tools import pkm_pairs
|
||||
from chia.consensus.difficulty_adjustment import get_next_sub_slot_iters_and_difficulty
|
||||
from chia.consensus.get_block_challenge import post_hard_fork2
|
||||
@@ -171,7 +170,7 @@ class FullNode:
|
||||
_db_wrapper: DBWrapper2 | None = None
|
||||
_hint_store: HintStore | None = None
|
||||
_block_store: BlockStore | None = None
|
||||
_coin_store: CoinStoreProtocol | None = None
|
||||
_coin_store: CoinStore | None = None
|
||||
_mempool_manager: MempoolManager | None = None
|
||||
_init_weight_proof: asyncio.Task[None] | None = None
|
||||
_blockchain: Blockchain | None = None
|
||||
@@ -456,7 +455,7 @@ class FullNode:
|
||||
return self._pool
|
||||
|
||||
@property
|
||||
def coin_store(self) -> CoinStoreProtocol:
|
||||
def coin_store(self) -> CoinStore:
|
||||
assert self._coin_store is not None
|
||||
return self._coin_store
|
||||
|
||||
|
||||
@@ -705,7 +705,7 @@ class FullNodeRpcApi:
|
||||
if "include_spent_coins" in request:
|
||||
kwargs["include_spent_coins"] = request["include_spent_coins"]
|
||||
|
||||
coin_records = await self.service.blockchain.coin_store.get_coin_records_by_puzzle_hash(**kwargs)
|
||||
coin_records = await self.service.coin_store.get_coin_records_by_puzzle_hash(**kwargs)
|
||||
|
||||
return {"coin_records": [coin_record_dict_backwards_compat(cr.to_json_dict()) for cr in coin_records]}
|
||||
|
||||
@@ -727,7 +727,7 @@ class FullNodeRpcApi:
|
||||
if "include_spent_coins" in request:
|
||||
kwargs["include_spent_coins"] = request["include_spent_coins"]
|
||||
|
||||
coin_records = await self.service.blockchain.coin_store.get_coin_records_by_puzzle_hashes(**kwargs)
|
||||
coin_records = await self.service.coin_store.get_coin_records_by_puzzle_hashes(**kwargs)
|
||||
|
||||
return {"coin_records": [coin_record_dict_backwards_compat(cr.to_json_dict()) for cr in coin_records]}
|
||||
|
||||
@@ -739,7 +739,7 @@ class FullNodeRpcApi:
|
||||
raise RpcError.simple(RpcErrorCodes.NAME_NOT_IN_REQUEST, "Name not in request")
|
||||
name = bytes32.from_hexstr(request["name"])
|
||||
|
||||
coin_record: CoinRecord | None = await self.service.blockchain.coin_store.get_coin_record(name)
|
||||
coin_record: CoinRecord | None = await self.service.coin_store.get_coin_record(name)
|
||||
if coin_record is None:
|
||||
raise RpcError(
|
||||
RpcErrorCodes.COIN_RECORD_NOT_FOUND,
|
||||
@@ -768,7 +768,7 @@ class FullNodeRpcApi:
|
||||
if "include_spent_coins" in request:
|
||||
kwargs["include_spent_coins"] = request["include_spent_coins"]
|
||||
|
||||
coin_records = await self.service.blockchain.coin_store.get_coin_records_by_names(**kwargs)
|
||||
coin_records = await self.service.coin_store.get_coin_records_by_names(**kwargs)
|
||||
|
||||
return {"coin_records": [coin_record_dict_backwards_compat(cr.to_json_dict()) for cr in coin_records]}
|
||||
|
||||
@@ -790,7 +790,7 @@ class FullNodeRpcApi:
|
||||
if "include_spent_coins" in request:
|
||||
kwargs["include_spent_coins"] = request["include_spent_coins"]
|
||||
|
||||
coin_records = await self.service.blockchain.coin_store.get_coin_records_by_parent_ids(**kwargs)
|
||||
coin_records = await self.service.coin_store.get_coin_records_by_parent_ids(**kwargs)
|
||||
|
||||
return {"coin_records": [coin_record_dict_backwards_compat(cr.to_json_dict()) for cr in coin_records]}
|
||||
|
||||
@@ -819,7 +819,7 @@ class FullNodeRpcApi:
|
||||
if "include_spent_coins" in request:
|
||||
kwargs["include_spent_coins"] = request["include_spent_coins"]
|
||||
|
||||
coin_records = await self.service.blockchain.coin_store.get_coin_records_by_names(**kwargs)
|
||||
coin_records = await self.service.coin_store.get_coin_records_by_names(**kwargs)
|
||||
|
||||
return {"coin_records": [coin_record_dict_backwards_compat(cr.to_json_dict()) for cr in coin_records]}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user