diff --git a/benchmarks/block_ref.py b/benchmarks/block_ref.py index beb5a900d8f0..a5b0e034e582 100644 --- a/benchmarks/block_ref.py +++ b/benchmarks/block_ref.py @@ -19,6 +19,7 @@ from chia.consensus.get_block_generator import get_block_generator from chia.full_node.block_store import BlockStore from chia.full_node.coin_store import CoinStore +from chia.full_node.consensus_store_sqlite3 import ConsensusStoreSQLite3 from chia.types.blockchain_format.serialized_program import SerializedProgram from chia.util.db_version import lookup_db_version from chia.util.db_wrapper import DBWrapper2 @@ -70,7 +71,8 @@ async def main(db_path: Path) -> None: # make configurable reserved_cores = 4 height_map = await BlockHeightMap.create(db_path.parent, db_wrapper) - blockchain = await Blockchain.create(coin_store, block_store, height_map, DEFAULT_CONSTANTS, reserved_cores) + consensus_store = ConsensusStoreSQLite3(block_store, coin_store, height_map) + blockchain = await Blockchain.create(consensus_store, DEFAULT_CONSTANTS, reserved_cores) peak = blockchain.get_peak() assert peak is not None diff --git a/chia/_tests/blockchain/blockchain_test_utils.py b/chia/_tests/blockchain/blockchain_test_utils.py index b2cd67e2d258..5ccac820cc30 100644 --- a/chia/_tests/blockchain/blockchain_test_utils.py +++ b/chia/_tests/blockchain/blockchain_test_utils.py @@ -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.consensus_store_sqlite3 import ConsensusStoreSQLite3 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 + assert isinstance(bc.consensus_store, ConsensusStoreSQLite3) - if db_wrapper.db_version == 1: + if bc.consensus_store.block_store.db_wrapper == 1: return in_chain = set() max_height = -1 - async with bc.block_store.transaction() as conn: + async with bc.consensus_store.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/_tests/blockchain/test_blockchain.py b/chia/_tests/blockchain/test_blockchain.py index e0de389d4d3a..bf4a71511c50 100644 --- a/chia/_tests/blockchain/test_blockchain.py +++ b/chia/_tests/blockchain/test_blockchain.py @@ -2076,7 +2076,7 @@ async def test_timelock_conditions( if expected == AddBlockResult.NEW_PEAK: # ensure coin was in fact spent - c = await b.coin_store.get_coin_record(coin.name()) + c = await b.consensus_store.get_coin_record(coin.name()) assert c is not None and c.spent @pytest.mark.anyio @@ -2286,10 +2286,10 @@ async def test_ephemeral_timelock( if expected == AddBlockResult.NEW_PEAK: # ensure coin1 was in fact spent - c = await b.coin_store.get_coin_record(coin1.name()) + c = await b.consensus_store.get_coin_record(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 b.consensus_store.get_coin_record(coin2.name()) assert c is not None and not c.spent @pytest.mark.anyio @@ -3103,9 +3103,9 @@ async def test_double_spent_in_reorg(self, empty_blockchain: Blockchain, bt: Blo ) # ephemeral coin is spent - first_coin = await b.coin_store.get_coin_record(new_coin.name()) + first_coin = await b.consensus_store.get_coin_record(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 b.consensus_store.get_coin_record(tx_2.additions()[0].name()) assert second_coin is not None and not second_coin.spent farmer_coin = create_farmer_coin( @@ -3121,7 +3121,7 @@ async def test_double_spent_in_reorg(self, empty_blockchain: Blockchain, bt: Blo ) 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 b.consensus_store.get_coin_record(farmer_coin.name()) assert farmer_coin_record is not None and farmer_coin_record.spent @pytest.mark.anyio @@ -3876,11 +3876,12 @@ 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 b.consensus_store.get_coin_record(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())}") + # await b.consensus_store._set_spent([spend_bundle.coin_spends[0].coin.name()], 8) + async with b.consensus_store.writer() as writer: + await writer.rollback_to_block(2) + print(f"{await b.consensus_store.get_coin_record(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) @@ -4209,7 +4210,7 @@ async def get_fork_info(blockchain: Blockchain, block: FullBlock, peak: BlockRec counter = 0 start = time.monotonic() for height in range(fork_info.fork_height + 1, block.height): - fork_block: Optional[FullBlock] = await blockchain.block_store.get_full_block(fork_chain[uint32(height)]) + fork_block: Optional[FullBlock] = await blockchain.consensus_store.get_full_block(fork_chain[uint32(height)]) assert fork_block is not None assert fork_block.height - 1 == fork_info.peak_height assert fork_block.height == 0 or fork_block.prev_header_hash == fork_info.peak_hash diff --git a/chia/_tests/core/full_node/ram_db.py b/chia/_tests/core/full_node/ram_db.py index c33cc50d233c..8555b25cc64c 100644 --- a/chia/_tests/core/full_node/ram_db.py +++ b/chia/_tests/core/full_node/ram_db.py @@ -7,10 +7,8 @@ from chia_rs import ConsensusConstants -from chia.consensus.block_height_map import BlockHeightMap from chia.consensus.blockchain import Blockchain -from chia.full_node.block_store import BlockStore -from chia.full_node.coin_store import CoinStore +from chia.full_node.consensus_store_sqlite3 import ConsensusStoreSQLite3 from chia.util.db_wrapper import DBWrapper2 @@ -20,10 +18,8 @@ async def create_ram_blockchain( ) -> AsyncIterator[tuple[DBWrapper2, Blockchain]]: uri = f"file:db_{random.randint(0, 99999999)}?mode=memory&cache=shared" async with DBWrapper2.managed(database=uri, uri=True, reader_count=1, db_version=2) as db_wrapper: - block_store = await BlockStore.create(db_wrapper) - coin_store = await CoinStore.create(db_wrapper) - height_map = await BlockHeightMap.create(Path("."), db_wrapper) - blockchain = await Blockchain.create(coin_store, block_store, height_map, consensus_constants, 2) + consensus_store = await ConsensusStoreSQLite3.create(db_wrapper, Path(".")) + blockchain = await Blockchain.create(consensus_store, consensus_constants, 2) try: yield db_wrapper, blockchain finally: 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 5cb22d158f7d..defd4fbe58c1 100644 --- a/chia/_tests/core/full_node/stores/test_block_store.py +++ b/chia/_tests/core/full_node/stores/test_block_store.py @@ -18,12 +18,11 @@ from chia._tests.blockchain.blockchain_test_utils import _validate_and_add_block from chia._tests.util.db_connection import DBConnection, PathDBConnection from chia.consensus.block_body_validation import ForkInfo -from chia.consensus.block_height_map import BlockHeightMap 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.full_node.block_store import BlockStore -from chia.full_node.coin_store import CoinStore +from chia.full_node.consensus_store_sqlite3 import ConsensusStoreSQLite3 from chia.full_node.full_block_utils import GeneratorBlockInfo from chia.simulator.block_tools import BlockTools from chia.simulator.wallet_tools import WalletTool @@ -71,10 +70,8 @@ async def test_block_store(tmp_dir: Path, db_version: int, bt: BlockTools, use_c async with DBConnection(db_version) as db_wrapper, DBConnection(db_version) as db_wrapper_2: # Use a different file for the blockchain - coin_store_2 = await CoinStore.create(db_wrapper_2) - store_2 = await BlockStore.create(db_wrapper_2, use_cache=use_cache) - height_map = await BlockHeightMap.create(tmp_dir, db_wrapper_2) - bc = await Blockchain.create(coin_store_2, store_2, height_map, bt.constants, 2) + consensus_store = await ConsensusStoreSQLite3.create(db_wrapper_2, tmp_dir, use_cache=use_cache) + bc = await Blockchain.create(consensus_store, bt.constants, 2) store = await BlockStore.create(db_wrapper, use_cache=use_cache) await BlockStore.create(db_wrapper_2) @@ -147,10 +144,9 @@ async def test_get_full_blocks_at( async with DBConnection(2) as db_wrapper: # Use a different file for the blockchain - coin_store = await CoinStore.create(db_wrapper) - block_store = await BlockStore.create(db_wrapper, use_cache=use_cache) - height_map = await BlockHeightMap.create(tmp_dir, db_wrapper) - bc = await Blockchain.create(coin_store, block_store, height_map, bt.constants, 2) + consensus_store = await ConsensusStoreSQLite3.create(db_wrapper, tmp_dir, use_cache=use_cache) + block_store = consensus_store.block_store + bc = await Blockchain.create(consensus_store, bt.constants, 2) count = 0 fork_info = ForkInfo(-1, -1, bt.constants.GENESIS_CHALLENGE) @@ -175,10 +171,9 @@ async def test_get_block_records_in_range( async with DBConnection(2) as db_wrapper: # Use a different file for the blockchain - coin_store = await CoinStore.create(db_wrapper) - block_store = await BlockStore.create(db_wrapper, use_cache=use_cache) - height_map = await BlockHeightMap.create(tmp_dir, db_wrapper) - bc = await Blockchain.create(coin_store, block_store, height_map, bt.constants, 2) + consensus_store = await ConsensusStoreSQLite3.create(db_wrapper, tmp_dir, use_cache=use_cache) + block_store = consensus_store.block_store + bc = await Blockchain.create(consensus_store, bt.constants, 2) count = 0 fork_info = ForkInfo(-1, -1, bt.constants.GENESIS_CHALLENGE) @@ -205,10 +200,9 @@ async def test_get_block_bytes_in_range_in_main_chain( async with DBConnection(2) as db_wrapper: # Use a different file for the blockchain - coin_store = await CoinStore.create(db_wrapper) - block_store = await BlockStore.create(db_wrapper, use_cache=use_cache) - height_map = await BlockHeightMap.create(tmp_dir, db_wrapper) - bc = await Blockchain.create(coin_store, block_store, height_map, bt.constants, 2) + consensus_store = await ConsensusStoreSQLite3.create(db_wrapper, tmp_dir, use_cache=use_cache) + block_store = consensus_store.block_store + bc = await Blockchain.create(consensus_store, bt.constants, 2) count = 0 fork_info = ForkInfo(-1, -1, bt.constants.GENESIS_CHALLENGE) for b1, b2 in zip(blocks, alt_blocks): @@ -234,10 +228,8 @@ async def test_deadlock(tmp_dir: Path, db_version: int, bt: BlockTools, use_cach async with PathDBConnection(db_version) as wrapper, PathDBConnection(db_version) as wrapper_2: store = await BlockStore.create(wrapper, use_cache=use_cache) - coin_store_2 = await CoinStore.create(wrapper_2) - store_2 = await BlockStore.create(wrapper_2) - height_map = await BlockHeightMap.create(tmp_dir, wrapper_2) - bc = await Blockchain.create(coin_store_2, store_2, height_map, bt.constants, 2) + consensus_store = await ConsensusStoreSQLite3.create(wrapper_2, tmp_dir, use_cache=use_cache) + bc = await Blockchain.create(consensus_store, bt.constants, 2) block_records = [] for block in blocks: await _validate_and_add_block(bc, block) @@ -265,10 +257,9 @@ async def test_rollback(bt: BlockTools, tmp_dir: Path, use_cache: bool, default_ async with DBConnection(2) as db_wrapper: # Use a different file for the blockchain - coin_store = await CoinStore.create(db_wrapper) - block_store = await BlockStore.create(db_wrapper, use_cache=use_cache) - height_map = await BlockHeightMap.create(tmp_dir, db_wrapper) - bc = await Blockchain.create(coin_store, block_store, height_map, bt.constants, 2) + consensus_store = await ConsensusStoreSQLite3.create(db_wrapper, tmp_dir, use_cache=use_cache) + block_store = consensus_store.block_store + bc = await Blockchain.create(consensus_store, bt.constants, 2) # insert all blocks count = 0 @@ -328,10 +319,9 @@ async def test_count_compactified_blocks(bt: BlockTools, tmp_dir: Path, db_versi blocks = bt.get_consecutive_blocks(10) async with DBConnection(db_version) as db_wrapper: - coin_store = await CoinStore.create(db_wrapper) - block_store = await BlockStore.create(db_wrapper, use_cache=use_cache) - height_map = await BlockHeightMap.create(tmp_dir, db_wrapper) - bc = await Blockchain.create(coin_store, block_store, height_map, bt.constants, 2) + consensus_store = await ConsensusStoreSQLite3.create(db_wrapper, tmp_dir, use_cache=use_cache) + block_store = consensus_store.block_store + bc = await Blockchain.create(consensus_store, bt.constants, 2) count = await block_store.count_compactified_blocks() assert count == 0 @@ -349,10 +339,9 @@ async def test_count_uncompactified_blocks(bt: BlockTools, tmp_dir: Path, db_ver blocks = bt.get_consecutive_blocks(10) async with DBConnection(db_version) as db_wrapper: - coin_store = await CoinStore.create(db_wrapper) - block_store = await BlockStore.create(db_wrapper, use_cache=use_cache) - height_map = await BlockHeightMap.create(tmp_dir, db_wrapper) - bc = await Blockchain.create(coin_store, block_store, height_map, bt.constants, 2) + consensus_store = await ConsensusStoreSQLite3.create(db_wrapper, tmp_dir, use_cache=use_cache) + block_store = consensus_store.block_store + bc = await Blockchain.create(consensus_store, bt.constants, 2) count = await block_store.count_uncompactified_blocks() assert count == 0 @@ -377,10 +366,9 @@ def rand_vdf_proof() -> VDFProof: ) async with DBConnection(db_version) as db_wrapper: - coin_store = await CoinStore.create(db_wrapper) - block_store = await BlockStore.create(db_wrapper, use_cache=use_cache) - height_map = await BlockHeightMap.create(tmp_dir, db_wrapper) - bc = await Blockchain.create(coin_store, block_store, height_map, bt.constants, 2) + consensus_store = await ConsensusStoreSQLite3.create(db_wrapper, tmp_dir, use_cache=use_cache) + block_store = consensus_store.block_store + bc = await Blockchain.create(consensus_store, bt.constants, 2) for block in blocks: await _validate_and_add_block(bc, block) @@ -400,7 +388,7 @@ def rand_vdf_proof() -> VDFProof: # make sure we get the same result when we hit the database # itself (and not just the block cache) - block_store.rollback_cache_block(block.header_hash) + consensus_store.rollback_cache_block(block.header_hash) b = await block_store.get_full_block(block.header_hash) assert b is not None assert b.challenge_chain_ip_proof == proof @@ -458,10 +446,8 @@ async def test_get_blocks_by_hash(tmp_dir: Path, bt: BlockTools, db_version: int async with DBConnection(db_version) as db_wrapper, DBConnection(db_version) as db_wrapper_2: # Use a different file for the blockchain - coin_store_2 = await CoinStore.create(db_wrapper_2) - store_2 = await BlockStore.create(db_wrapper_2, use_cache=use_cache) - height_map = await BlockHeightMap.create(tmp_dir, db_wrapper_2) - bc = await Blockchain.create(coin_store_2, store_2, height_map, bt.constants, 2) + consensus_store = await ConsensusStoreSQLite3.create(db_wrapper_2, tmp_dir, use_cache=use_cache) + bc = await Blockchain.create(consensus_store, bt.constants, 2) store = await BlockStore.create(db_wrapper, use_cache=use_cache) await BlockStore.create(db_wrapper_2) @@ -498,10 +484,9 @@ async def test_get_block_bytes_in_range(tmp_dir: Path, bt: BlockTools, db_versio async with DBConnection(db_version) as db_wrapper_2: # Use a different file for the blockchain - coin_store_2 = await CoinStore.create(db_wrapper_2) - store_2 = await BlockStore.create(db_wrapper_2, use_cache=use_cache) - height_map = await BlockHeightMap.create(tmp_dir, db_wrapper_2) - bc = await Blockchain.create(coin_store_2, store_2, height_map, bt.constants, 2) + consensus_store = await ConsensusStoreSQLite3.create(db_wrapper_2, tmp_dir, use_cache=use_cache) + store_2 = consensus_store.block_store + bc = await Blockchain.create(consensus_store, bt.constants, 2) await BlockStore.create(db_wrapper_2) @@ -571,10 +556,8 @@ async def test_get_prev_hash(tmp_dir: Path, bt: BlockTools, db_version: int, use async with DBConnection(db_version) as db_wrapper, DBConnection(db_version) as db_wrapper_2: # Use a different file for the blockchain - coin_store_2 = await CoinStore.create(db_wrapper_2) - store_2 = await BlockStore.create(db_wrapper_2, use_cache=use_cache) - height_map = await BlockHeightMap.create(tmp_dir, db_wrapper_2) - bc = await Blockchain.create(coin_store_2, store_2, height_map, bt.constants, 2) + consensus_store = await ConsensusStoreSQLite3.create(db_wrapper_2, tmp_dir, use_cache=use_cache) + bc = await Blockchain.create(consensus_store, bt.constants, 2) store = await BlockStore.create(db_wrapper, use_cache=use_cache) await BlockStore.create(db_wrapper_2) diff --git a/chia/_tests/core/full_node/stores/test_coin_store.py b/chia/_tests/core/full_node/stores/test_coin_store.py index 1b92f1bfd73a..b486f6819596 100644 --- a/chia/_tests/core/full_node/stores/test_coin_store.py +++ b/chia/_tests/core/full_node/stores/test_coin_store.py @@ -16,12 +16,11 @@ from chia._tests.util.db_connection import DBConnection from chia._tests.util.misc import Marks, datacases from chia.consensus.block_body_validation import ForkInfo -from chia.consensus.block_height_map import BlockHeightMap from chia.consensus.block_rewards import calculate_base_farmer_reward, calculate_pool_reward from chia.consensus.blockchain import AddBlockResult, Blockchain from chia.consensus.coinbase import create_farmer_coin, create_pool_coin -from chia.full_node.block_store import BlockStore from chia.full_node.coin_store import CoinStore +from chia.full_node.consensus_store_sqlite3 import ConsensusStoreSQLite3 from chia.full_node.hint_store import HintStore from chia.simulator.block_tools import BlockTools, test_constants from chia.simulator.wallet_tools import WalletTool @@ -315,10 +314,9 @@ async def test_basic_reorg(tmp_dir: Path, db_version: int, bt: BlockTools) -> No initial_block_count = 30 reorg_length = 15 blocks = bt.get_consecutive_blocks(initial_block_count) - coin_store = await CoinStore.create(db_wrapper) - store = await BlockStore.create(db_wrapper) - height_map = await BlockHeightMap.create(tmp_dir, db_wrapper) - b: Blockchain = await Blockchain.create(coin_store, store, height_map, bt.constants, 2) + consensus_store = await ConsensusStoreSQLite3.create(db_wrapper, tmp_dir) + coin_store = consensus_store.coin_store + b: Blockchain = await Blockchain.create(consensus_store, bt.constants, 2) try: records: list[Optional[CoinRecord]] = [] @@ -382,10 +380,9 @@ async def test_get_puzzle_hash(tmp_dir: Path, db_version: int, bt: BlockTools) - pool_reward_puzzle_hash=pool_ph, guarantee_transaction_block=True, ) - coin_store = await CoinStore.create(db_wrapper) - store = await BlockStore.create(db_wrapper) - height_map = await BlockHeightMap.create(tmp_dir, db_wrapper) - b: Blockchain = await Blockchain.create(coin_store, store, height_map, bt.constants, 2) + consensus_store = await ConsensusStoreSQLite3.create(db_wrapper, tmp_dir) + coin_store = consensus_store.coin_store + b: Blockchain = await Blockchain.create(consensus_store, bt.constants, 2) for block in blocks: await _validate_and_add_block(b, block) peak = b.get_peak() diff --git a/chia/_tests/core/full_node/test_conditions.py b/chia/_tests/core/full_node/test_conditions.py index 729b2fe67ab1..842fe477e4e6 100644 --- a/chia/_tests/core/full_node/test_conditions.py +++ b/chia/_tests/core/full_node/test_conditions.py @@ -83,8 +83,8 @@ async def check_spend_bundle_validity( if expected_err is None: await _validate_and_add_block(blockchain, newest_block) - coins_added = await blockchain.coin_store.get_coins_added_at_height(uint32(len(blocks))) - coins_removed = await blockchain.coin_store.get_coins_removed_at_height(uint32(len(blocks))) + coins_added = await blockchain.consensus_store.get_coins_added_at_height(uint32(len(blocks))) + coins_removed = await blockchain.consensus_store.get_coins_removed_at_height(uint32(len(blocks))) else: await _validate_and_add_block(blockchain, newest_block, expected_error=expected_err) coins_added = [] diff --git a/chia/_tests/core/full_node/test_full_node.py b/chia/_tests/core/full_node/test_full_node.py index 9b70e3c23ee5..dda002af5bb6 100644 --- a/chia/_tests/core/full_node/test_full_node.py +++ b/chia/_tests/core/full_node/test_full_node.py @@ -47,7 +47,7 @@ from chia.consensus.augmented_chain import AugmentedBlockchain from chia.consensus.block_body_validation import ForkInfo from chia.consensus.blockchain import Blockchain -from chia.consensus.coin_store_protocol import CoinStoreProtocol +from chia.consensus.consensus_store_protocol import ConsensusStoreProtocol from chia.consensus.get_block_challenge import get_block_challenge from chia.consensus.multiprocess_validation import PreValidationResult, pre_validate_block from chia.consensus.pot_iterations import is_overflow_block @@ -2505,7 +2505,7 @@ def print_coin_records(records: dict[bytes32, CoinRecord]) -> None: # pragma: n print(f"{rec}") -async def validate_coin_set(coin_store: CoinStoreProtocol, blocks: list[FullBlock]) -> None: +async def validate_coin_set(consensus_store: ConsensusStoreProtocol, blocks: list[FullBlock]) -> None: prev_height = blocks[0].height - 1 prev_hash = blocks[0].prev_header_hash for block in blocks: @@ -2514,7 +2514,7 @@ async def validate_coin_set(coin_store: CoinStoreProtocol, blocks: list[FullBloc prev_height = int(block.height) prev_hash = block.header_hash rewards = block.get_included_reward_coins() - records = {rec.coin.name(): rec for rec in await coin_store.get_coins_added_at_height(block.height)} + records = {rec.coin.name(): rec for rec in await consensus_store.get_coins_added_at_height(block.height)} # validate reward coins for reward in rewards: @@ -2550,7 +2550,7 @@ async def validate_coin_set(coin_store: CoinStoreProtocol, blocks: list[FullBloc print_coin_records(records) assert records == {} - records = {rec.coin.name(): rec for rec in await coin_store.get_coins_removed_at_height(block.height)} + records = {rec.coin.name(): rec for rec in await consensus_store.get_coins_removed_at_height(block.height)} for name, rem in removals: rec = records.pop(name) assert rec is not None @@ -2598,8 +2598,8 @@ async def test_long_reorg( assert reorg_blocks[fork_point] == default_10000_blocks[fork_point] assert reorg_blocks[fork_point + 1] != default_10000_blocks[fork_point + 1] - assert node.full_node._coin_store is not None - await validate_coin_set(node.full_node._coin_store, blocks) + assert node.full_node.blockchain.consensus_store is not None + await validate_coin_set(node.full_node.blockchain.consensus_store, blocks) # one aspect of this test is to make sure we can reorg blocks that are # not in the cache. We need to explicitly prune the cache to get that @@ -2614,7 +2614,7 @@ async def test_long_reorg( chain_2_weight = peak.weight chain_2_peak = peak.header_hash - await validate_coin_set(node.full_node._coin_store, reorg_blocks) + await validate_coin_set(node.full_node.blockchain.consensus_store, reorg_blocks) # if the reorg chain has lighter blocks, once we've re-orged onto it, we # have a greater block height. If the reorg chain has heavier blocks, we @@ -2639,7 +2639,7 @@ async def test_long_reorg( assert peak.header_hash != chain_2_peak assert peak.weight > chain_2_weight - await validate_coin_set(node.full_node._coin_store, blocks) + await validate_coin_set(node.full_node.blockchain.consensus_store, blocks) @pytest.mark.anyio @@ -2665,9 +2665,9 @@ async def test_long_reorg_nodes( ) -> None: full_node_1, full_node_2, full_node_3 = three_nodes - assert full_node_1.full_node._coin_store is not None - assert full_node_2.full_node._coin_store is not None - assert full_node_3.full_node._coin_store is not None + assert full_node_1.full_node.blockchain.consensus_store is not None + assert full_node_2.full_node.blockchain.consensus_store is not None + assert full_node_3.full_node.blockchain.consensus_store is not None if light_blocks: if fork_point == 1500: @@ -2724,8 +2724,8 @@ def check_nodes_in_sync() -> bool: assert p2 is not None assert p2.header_hash == reorg_blocks[-1].header_hash - await validate_coin_set(full_node_1.full_node._coin_store, reorg_blocks) - await validate_coin_set(full_node_2.full_node._coin_store, reorg_blocks) + await validate_coin_set(full_node_1.full_node.blockchain.consensus_store, reorg_blocks) + await validate_coin_set(full_node_2.full_node.blockchain.consensus_store, reorg_blocks) blocks = default_10000_blocks[:reorg_height] @@ -2765,9 +2765,9 @@ def check_nodes_in_sync2() -> bool: print(f"reorg1 timing: {reorg1_timing:0.2f}s") print(f"reorg2 timing: {reorg2_timing:0.2f}s") - await validate_coin_set(full_node_1.full_node._coin_store, blocks) - await validate_coin_set(full_node_2.full_node._coin_store, blocks) - await validate_coin_set(full_node_3.full_node._coin_store, blocks) + await validate_coin_set(full_node_1.full_node.blockchain.consensus_store, blocks) + await validate_coin_set(full_node_2.full_node.blockchain.consensus_store, blocks) + await validate_coin_set(full_node_3.full_node.blockchain.consensus_store, blocks) @pytest.mark.anyio @@ -2804,8 +2804,8 @@ def check_nodes_in_sync() -> bool: return p1 == p2 await time_out_assert(10, check_nodes_in_sync) - await validate_coin_set(full_node_1.full_node.blockchain.coin_store, chain) - await validate_coin_set(full_node_2.full_node.blockchain.coin_store, chain) + await validate_coin_set(full_node_1.full_node.blockchain.consensus_store, chain) + await validate_coin_set(full_node_2.full_node.blockchain.consensus_store, chain) # we spend a coin in the next block spend_bundle = wallet_a.generate_signed_transaction(uint64(1_000), receiver_puzzlehash, all_coins.pop()) @@ -2837,8 +2837,8 @@ def check_nodes_in_sync() -> bool: await add_blocks_in_batches(chain_a[-1:], full_node_1.full_node) await time_out_assert(10, check_nodes_in_sync) - await validate_coin_set(full_node_1.full_node.blockchain.coin_store, chain_a) - await validate_coin_set(full_node_2.full_node.blockchain.coin_store, chain_a) + await validate_coin_set(full_node_1.full_node.blockchain.consensus_store, chain_a) + await validate_coin_set(full_node_2.full_node.blockchain.consensus_store, chain_a) await add_blocks_in_batches(chain_b[-1:], full_node_1.full_node) @@ -2848,8 +2848,8 @@ def check_nodes_in_sync() -> bool: assert peak.header_hash == chain_b[-1].header_hash await time_out_assert(10, check_nodes_in_sync) - await validate_coin_set(full_node_1.full_node.blockchain.coin_store, chain_b) - await validate_coin_set(full_node_2.full_node.blockchain.coin_store, chain_b) + await validate_coin_set(full_node_1.full_node.blockchain.consensus_store, chain_b) + await validate_coin_set(full_node_2.full_node.blockchain.consensus_store, chain_b) # now continue building the chain on top of B # since spend_bundle was supposed to have been reorged-out, we should be @@ -2884,8 +2884,8 @@ def check_nodes_in_sync() -> bool: await add_blocks_in_batches(chain[-4:], full_node_1.full_node) await time_out_assert(10, check_nodes_in_sync) - await validate_coin_set(full_node_1.full_node.blockchain.coin_store, chain) - await validate_coin_set(full_node_2.full_node.blockchain.coin_store, chain) + await validate_coin_set(full_node_1.full_node.blockchain.consensus_store, chain) + await validate_coin_set(full_node_2.full_node.blockchain.consensus_store, chain) @pytest.mark.anyio diff --git a/chia/_tests/core/test_db_conversion.py b/chia/_tests/core/test_db_conversion.py index d65d9a215e70..e721eb40cb19 100644 --- a/chia/_tests/core/test_db_conversion.py +++ b/chia/_tests/core/test_db_conversion.py @@ -10,11 +10,9 @@ from chia._tests.util.temp_file import TempFile from chia.cmds.db_upgrade_func import convert_v1_to_v2 from chia.consensus.block_body_validation import ForkInfo -from chia.consensus.block_height_map import BlockHeightMap from chia.consensus.blockchain import Blockchain from chia.consensus.multiprocess_validation import PreValidationResult -from chia.full_node.block_store import BlockStore -from chia.full_node.coin_store import CoinStore +from chia.full_node.consensus_store_sqlite3 import ConsensusStoreSQLite3 from chia.full_node.hint_store import HintStore from chia.simulator.block_tools import test_constants from chia.util.db_wrapper import DBWrapper2 @@ -54,15 +52,12 @@ async def test_blocks(default_1000_blocks, with_hints: bool): journal_mode="OFF", synchronous="OFF", ) as db_wrapper1: - block_store1 = await BlockStore.create(db_wrapper1) - coin_store1 = await CoinStore.create(db_wrapper1) + consensus_store = await ConsensusStoreSQLite3.create(db_wrapper1, Path(".")) hint_store1 = await HintStore.create(db_wrapper1) if with_hints: for h in hints: await hint_store1.add_hints([(h[0], h[1])]) - - height_map = await BlockHeightMap.create(Path("."), db_wrapper1) - bc = await Blockchain.create(coin_store1, block_store1, height_map, test_constants, reserved_cores=0) + bc = await Blockchain.create(consensus_store, test_constants, reserved_cores=0) sub_slot_iters = test_constants.SUB_SLOT_ITERS_STARTING for block in blocks: if block.height != 0 and len(block.finished_sub_slots) > 0: @@ -79,12 +74,14 @@ async def test_blocks(default_1000_blocks, with_hints: bool): async with DBWrapper2.managed(database=in_file, reader_count=1, db_version=1) as db_wrapper1: async with DBWrapper2.managed(database=out_file, reader_count=1, db_version=2) as db_wrapper2: - block_store1 = await BlockStore.create(db_wrapper1) - coin_store1 = await CoinStore.create(db_wrapper1) + consensus_store1 = await ConsensusStoreSQLite3.create(db_wrapper1, Path(".")) + block_store1 = consensus_store1.block_store + coin_store1 = consensus_store1.coin_store hint_store1 = await HintStore.create(db_wrapper1) - block_store2 = await BlockStore.create(db_wrapper2) - coin_store2 = await CoinStore.create(db_wrapper2) + consensus_store2 = await ConsensusStoreSQLite3.create(db_wrapper2, Path(".")) + block_store2 = consensus_store2.block_store + coin_store2 = consensus_store2.coin_store hint_store2 = await HintStore.create(db_wrapper2) if with_hints: diff --git a/chia/_tests/core/test_db_validation.py b/chia/_tests/core/test_db_validation.py index fef9eb86de10..e48cc39a3efd 100644 --- a/chia/_tests/core/test_db_validation.py +++ b/chia/_tests/core/test_db_validation.py @@ -13,12 +13,10 @@ from chia._tests.util.temp_file import TempFile from chia.cmds.db_validate_func import validate_v2 from chia.consensus.block_body_validation import ForkInfo -from chia.consensus.block_height_map import BlockHeightMap from chia.consensus.blockchain import Blockchain from chia.consensus.default_constants import DEFAULT_CONSTANTS from chia.consensus.multiprocess_validation import PreValidationResult -from chia.full_node.block_store import BlockStore -from chia.full_node.coin_store import CoinStore +from chia.full_node.consensus_store_sqlite3 import ConsensusStoreSQLite3 from chia.simulator.block_tools import test_constants from chia.util.db_wrapper import DBWrapper2 @@ -138,11 +136,8 @@ async def make_db(db_file: Path, blocks: list[FullBlock]) -> None: await conn.execute("CREATE TABLE database_version(version int)") await conn.execute("INSERT INTO database_version VALUES (2)") - block_store = await BlockStore.create(db_wrapper) - coin_store = await CoinStore.create(db_wrapper) - height_map = await BlockHeightMap.create(Path("."), db_wrapper) - - bc = await Blockchain.create(coin_store, block_store, height_map, test_constants, reserved_cores=0) + consensus_store = await ConsensusStoreSQLite3.create(db_wrapper, Path(".")) + bc = await Blockchain.create(consensus_store, test_constants, reserved_cores=0) sub_slot_iters = test_constants.SUB_SLOT_ITERS_STARTING for block in blocks: if block.height != 0 and len(block.finished_sub_slots) > 0: diff --git a/chia/_tests/util/blockchain.py b/chia/_tests/util/blockchain.py index 58953062102c..cc344a7a8aab 100644 --- a/chia/_tests/util/blockchain.py +++ b/chia/_tests/util/blockchain.py @@ -10,10 +10,8 @@ from chia_rs import ConsensusConstants, FullBlock from chia_rs.sized_ints import uint64 -from chia.consensus.block_height_map import BlockHeightMap from chia.consensus.blockchain import Blockchain -from chia.full_node.block_store import BlockStore -from chia.full_node.coin_store import CoinStore +from chia.full_node.consensus_store_sqlite3 import ConsensusStoreSQLite3 from chia.simulator.block_tools import BlockTools from chia.util.db_wrapper import DBWrapper2, generate_in_memory_db_uri from chia.util.default_root import DEFAULT_ROOT_PATH @@ -25,11 +23,8 @@ async def create_blockchain( ) -> AsyncIterator[tuple[Blockchain, DBWrapper2]]: db_uri = generate_in_memory_db_uri() async with DBWrapper2.managed(database=db_uri, uri=True, reader_count=1, db_version=db_version) as wrapper: - coin_store = await CoinStore.create(wrapper) - store = await BlockStore.create(wrapper) - path = Path(".") - height_map = await BlockHeightMap.create(path, wrapper) - bc1 = await Blockchain.create(coin_store, store, height_map, constants, 3, single_threaded=True, log_coins=True) + consensus_store = await ConsensusStoreSQLite3.create(wrapper, Path(".")) + bc1 = await Blockchain.create(consensus_store, constants, 3, single_threaded=True, log_coins=True) try: assert bc1.get_peak() is None yield bc1, wrapper diff --git a/chia/_tests/util/coin_store.py b/chia/_tests/util/coin_store.py index b5eb266ec825..f0e356d1fc2d 100644 --- a/chia/_tests/util/coin_store.py +++ b/chia/_tests/util/coin_store.py @@ -1,11 +1,11 @@ from __future__ import annotations -from chia.consensus.coin_store_protocol import CoinStoreProtocol +from chia.full_node.coin_store import CoinStore from chia.types.coin_record import CoinRecord from chia.util.db_wrapper import DBWrapper2 -async def add_coin_records_to_db(coin_store: CoinStoreProtocol, records: list[CoinRecord]) -> None: +async def add_coin_records_to_db(coin_store: CoinStore, records: list[CoinRecord]) -> None: if len(records) == 0: return db_wrapper = getattr(coin_store, "db_wrapper", None) diff --git a/chia/consensus/blockchain.py b/chia/consensus/blockchain.py index 00d9df5d1a4c..5a8ffc90541e 100644 --- a/chia/consensus/blockchain.py +++ b/chia/consensus/blockchain.py @@ -26,8 +26,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.coin_store_protocol import CoinStoreProtocol +from chia.consensus.consensus_store_protocol import ConsensusStoreProtocol, ConsensusStoreWriteProtocol from chia.consensus.cost_calculator import NPCResult from chia.consensus.difficulty_adjustment import get_next_sub_slot_iters_and_difficulty from chia.consensus.find_fork_point import lookup_fork_chain @@ -35,7 +34,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 @@ -95,16 +93,12 @@ class Blockchain: # peak of the blockchain _peak_height: Optional[uint32] # All blocks in peak path are guaranteed to be included, can include orphan blocks - __block_records: dict[bytes32, BlockRecord] + _block_records: dict[bytes32, BlockRecord] # all hashes of blocks in block_record by height, used for garbage collection - __heights_in_cache: dict[uint32, set[bytes32]] + _heights_in_cache: dict[uint32, set[bytes32]] # maps block height (of the current heaviest chain) to block hash and sub # epoch summaries - __height_map: BlockHeightMap - # Unspent Store - coin_store: CoinStoreProtocol - # Store - block_store: BlockStore + consensus_store: ConsensusStoreProtocol # Used to verify blocks in parallel pool: Executor # Set holding seen compact proofs, in order to avoid duplicates. @@ -121,9 +115,7 @@ class Blockchain: @staticmethod async def create( - coin_store: CoinStoreProtocol, - block_store: BlockStore, - height_map: BlockHeightMap, + consensus_store: ConsensusStoreProtocol, consensus_constants: ConsensusConstants, reserved_cores: int, *, @@ -153,10 +145,9 @@ async def create( log.info(f"Started {num_workers} processes for block validation") self.constants = consensus_constants - self.coin_store = coin_store - self.block_store = block_store + self.consensus_store = consensus_store self._shut_down = False - await self._load_chain_from_store(height_map) + await self._load_chain_from_store() self._seen_compact_proofs = set() return self @@ -164,14 +155,15 @@ def shut_down(self) -> None: self._shut_down = True self.pool.shutdown(wait=True) - async def _load_chain_from_store(self, height_map: BlockHeightMap) -> None: + async def _load_chain_from_store(self) -> None: """ Initializes the state of the Blockchain class from the database. """ - self.__height_map = height_map - self.__block_records = {} - self.__heights_in_cache = {} - block_records, peak = await self.block_store.get_block_records_close_to_peak(self.constants.BLOCKS_CACHE_SIZE) + self._block_records = {} + self._heights_in_cache = {} + block_records, peak = await self.consensus_store.get_block_records_close_to_peak( + self.constants.BLOCKS_CACHE_SIZE + ) for block in block_records.values(): self.add_block_record(block) @@ -182,8 +174,8 @@ async def _load_chain_from_store(self, height_map: BlockHeightMap) -> None: assert peak is not None self._peak_height = self.block_record(peak).height - assert self.__height_map.contains_height(self._peak_height) - assert not self.__height_map.contains_height(uint32(self._peak_height + 1)) + assert self.consensus_store.contains_height(self._peak_height) + assert not self.consensus_store.contains_height(uint32(self._peak_height + 1)) def get_peak(self) -> Optional[BlockRecord]: """ @@ -219,12 +211,12 @@ async def get_full_peak(self) -> Optional[FullBlock]: """ Return list of FullBlocks that are peaks""" peak_hash: Optional[bytes32] = self.height_to_hash(self._peak_height) assert peak_hash is not None # Since we must have the peak block - block = await self.block_store.get_full_block(peak_hash) + block = await self.consensus_store.get_full_block(peak_hash) assert block is not None return block async def get_full_block(self, header_hash: bytes32) -> Optional[FullBlock]: - return await self.block_store.get_full_block(header_hash) + return await self.consensus_store.get_full_block(header_hash) async def advance_fork_info(self, block: FullBlock, fork_info: ForkInfo) -> None: """ @@ -258,7 +250,7 @@ async def advance_fork_info(self, block: FullBlock, fork_info: ForkInfo) -> None assert len(chain) == block.height - fork_info.peak_height - 1 for height in range(fork_info.peak_height + 1, block.height): - fork_block: Optional[FullBlock] = await self.block_store.get_full_block(chain[uint32(height)]) + fork_block: Optional[FullBlock] = await self.consensus_store.get_full_block(chain[uint32(height)]) assert fork_block is not None await self.run_single_block(fork_block, fork_info) @@ -384,7 +376,7 @@ async def add_block( error_code = await validate_block_body( self.constants, self, - self.coin_store.get_coin_records, + self.consensus_store.get_coin_records, block, block.height, pre_validation_result.conds, @@ -421,9 +413,9 @@ async def add_block( try: # Always add the block to the database - async with self.block_store.transaction(): + async with self.consensus_store.writer() as writer: # 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) + await writer.add_full_block(header_hash, block, block_record) records, state_change_summary = await self._reconsider_peak(block_record, genesis, fork_info) # Then update the memory cache. It is important that this is not cancelled and does not throw @@ -436,9 +428,9 @@ async def add_block( # make sure to update _peak_height after the transaction is committed, # otherwise other tasks may go look for this block before it's available if state_change_summary is not None: - self.__height_map.rollback(state_change_summary.fork_height) + self.consensus_store.rollback_height_map(state_change_summary.fork_height) for fetched_block_record in records: - self.__height_map.update_height( + self.consensus_store.update_height_map( fetched_block_record.height, fetched_block_record.header_hash, fetched_block_record.sub_epoch_summary_included, @@ -456,7 +448,7 @@ async def add_block( pass # restore fork_info to the state before adding the block fork_info.rollback(prev_fork_peak[1], prev_fork_peak[0]) - self.block_store.rollback_cache_block(header_hash) + self.consensus_store.rollback_cache_block(header_hash) self._peak_height = previous_peak_height log.error( f"Error while adding block {header_hash} height {block.height}," @@ -465,7 +457,7 @@ async def add_block( raise # This is done outside the try-except in case it fails, since we do not want to revert anything if it does - await self.__height_map.maybe_flush() + await self.consensus_store.maybe_flush_height_map() if state_change_summary is not None: # new coin records added @@ -488,6 +480,20 @@ async def _reconsider_peak( and the new chain, or returns None if there was no update to the heaviest chain. """ + async with self.consensus_store.writer() as writer: + return await self._perform_db_operations_for_peak(writer, block_record, genesis, fork_info) + + async def _perform_db_operations_for_peak( + self, + writer: ConsensusStoreWriteProtocol, + block_record: BlockRecord, + genesis: bool, + fork_info: ForkInfo, + ) -> tuple[list[BlockRecord], Optional[StateChangeSummary]]: + """ + Perform database operations to consider a new peak. + Creates and returns the records to add and the complete StateChangeSummary. + """ peak = self.get_peak() rolled_back_state: dict[bytes32, CoinRecord] = {} @@ -510,7 +516,7 @@ async def _reconsider_peak( ) if block_record.prev_hash != peak.header_hash: - rolled_back_state = await self.coin_store.rollback_to_block(fork_info.fork_height) + rolled_back_state = await writer.rollback_to_block(fork_info.fork_height) if self._log_coins and len(rolled_back_state) > 0: log.info(f"rolled back {len(rolled_back_state)} coins, to fork height {fork_info.fork_height}") log.info( @@ -544,7 +550,7 @@ async def _reconsider_peak( # for that here to avoid an unnecessary database lookup. records_to_add = [block_record] else: - records_to_add = await self.block_store.get_block_records_by_hash(fork_info.block_hashes) + records_to_add = await self.consensus_store.get_block_records_by_hash(fork_info.block_hashes) for fetched_block_record in records_to_add: if not fetched_block_record.is_transaction_block: @@ -572,7 +578,7 @@ async def _reconsider_peak( coin_id for coin_id, fork_rem in fork_info.removals_since_fork.items() if fork_rem.height == height ] assert fetched_block_record.timestamp is not None - await self.coin_store.new_block( + await writer.new_block( height, fetched_block_record.timestamp, included_reward_coins, @@ -591,11 +597,11 @@ async def _reconsider_peak( # we made it to the end successfully # Rollback sub_epoch_summaries - await self.block_store.rollback(fork_info.fork_height) - await self.block_store.set_in_chain([(br.header_hash,) for br in records_to_add]) + await writer.rollback(fork_info.fork_height) + await writer.set_in_chain([(br.header_hash,) for br in records_to_add]) # Changes the peak to be the new peak - await self.block_store.set_peak(block_record.header_hash) + await writer.set_peak(block_record.header_hash) return records_to_add, StateChangeSummary( block_record, @@ -621,7 +627,7 @@ def get_next_sub_slot_iters_and_difficulty(self, header_hash: bytes32, new_slot: async def get_sp_and_ip_sub_slots( self, header_hash: bytes32 ) -> Optional[tuple[Optional[EndOfSubSlotBundle], Optional[EndOfSubSlotBundle]]]: - block: Optional[FullBlock] = await self.block_store.get_full_block(header_hash) + block: Optional[FullBlock] = await self.consensus_store.get_full_block(header_hash) if block is None: return None curr_br: BlockRecord = self.block_record(block.header_hash) @@ -631,7 +637,7 @@ async def get_sp_and_ip_sub_slots( assert curr is not None while True: if curr_br.first_in_sub_slot: - curr = await self.block_store.get_full_block(curr_br.header_hash) + curr = await self.consensus_store.get_full_block(curr_br.header_hash) assert curr is not None break if curr_br.height == 0: @@ -652,7 +658,7 @@ async def get_sp_and_ip_sub_slots( # Have both sub-slots return curr.finished_sub_slots[-2], ip_sub_slot - prev_curr: Optional[FullBlock] = await self.block_store.get_full_block(curr.prev_header_hash) + prev_curr: Optional[FullBlock] = await self.consensus_store.get_full_block(curr.prev_header_hash) if prev_curr is None: assert curr.height == 0 prev_curr = curr @@ -662,7 +668,7 @@ async def get_sp_and_ip_sub_slots( assert prev_curr_br is not None while prev_curr_br.height > 0: if prev_curr_br.first_in_sub_slot: - prev_curr = await self.block_store.get_full_block(prev_curr_br.header_hash) + prev_curr = await self.consensus_store.get_full_block(prev_curr_br.header_hash) assert prev_curr is not None break prev_curr_br = self.block_record(prev_curr_br.prev_hash) @@ -769,7 +775,7 @@ async def validate_unfinished_block( error_code = await validate_block_body( self.constants, self, - self.coin_store.get_coin_records, + self.consensus_store.get_coin_records, block, uint32(prev_height + 1), conds, @@ -789,7 +795,7 @@ def contains_block(self, header_hash: bytes32, height: uint32) -> bool: return True def block_record(self, header_hash: bytes32) -> BlockRecord: - return self.__block_records[header_hash] + return self._block_records[header_hash] def height_to_block_record(self, height: uint32) -> BlockRecord: # Precondition: height is in the blockchain @@ -799,18 +805,18 @@ def height_to_block_record(self, height: uint32) -> BlockRecord: return self.block_record(header_hash) def get_ses_heights(self) -> list[uint32]: - return self.__height_map.get_ses_heights() + return self.consensus_store.get_ses_heights() def get_ses(self, height: uint32) -> SubEpochSummary: - return self.__height_map.get_ses(height) + return self.consensus_store.get_ses(height) def height_to_hash(self, height: uint32) -> Optional[bytes32]: - if not self.__height_map.contains_height(height): + if not self.consensus_store.contains_height(height): return None - return self.__height_map.get_hash(height) + return self.consensus_store.get_hash(height) def contains_height(self, height: uint32) -> bool: - return self.__height_map.contains_height(height) + return self.consensus_store.contains_height(height) def get_peak_height(self) -> Optional[uint32]: return self._peak_height @@ -826,7 +832,7 @@ async def warmup(self, fork_point: uint32) -> None: """ if self._peak_height is None: return None - block_records = await self.block_store.get_block_records_in_range( + block_records = await self.consensus_store.get_block_records_in_range( max(fork_point - self.constants.BLOCKS_CACHE_SIZE, uint32(0)), fork_point ) for block_record in block_records.values(): @@ -842,16 +848,16 @@ def clean_block_record(self, height: int) -> None: height = self._peak_height - self.constants.BLOCKS_CACHE_SIZE if height < 0: return None - blocks_to_remove = self.__heights_in_cache.get(uint32(height), None) + blocks_to_remove = self._heights_in_cache.get(uint32(height), None) while blocks_to_remove is not None and height >= 0: for header_hash in blocks_to_remove: - del self.__block_records[header_hash] # remove from blocks - del self.__heights_in_cache[uint32(height)] # remove height from heights in cache + del self._block_records[header_hash] # remove from blocks + del self._heights_in_cache[uint32(height)] # remove height from heights in cache if height == 0: break height -= 1 - blocks_to_remove = self.__heights_in_cache.get(uint32(height), None) + blocks_to_remove = self._heights_in_cache.get(uint32(height), None) def clean_block_records(self) -> None: """ @@ -860,7 +866,7 @@ def clean_block_records(self) -> None: These blocks are necessary for calculating future difficulty adjustments. """ - if len(self.__block_records) < self.constants.BLOCKS_CACHE_SIZE: + if len(self._block_records) < self.constants.BLOCKS_CACHE_SIZE: return None assert self._peak_height is not None @@ -869,7 +875,7 @@ def clean_block_records(self) -> None: self.clean_block_record(self._peak_height - self.constants.BLOCKS_CACHE_SIZE) async def get_block_records_in_range(self, start: int, stop: int) -> dict[bytes32, BlockRecord]: - return await self.block_store.get_block_records_in_range(start, stop) + return await self.consensus_store.get_block_records_in_range(start, stop) async def get_header_blocks_in_range( self, start: int, stop: int, tx_filter: bool = True @@ -882,11 +888,11 @@ async def get_header_blocks_in_range( blocks: list[FullBlock] = [] for hash in hashes.copy(): - block = self.block_store.get_block_from_cache(hash) + block = self.consensus_store.get_block_from_cache(hash) if block is not None: blocks.append(block) hashes.remove(hash) - blocks_on_disk: list[FullBlock] = await self.block_store.get_blocks_by_hash(hashes) + blocks_on_disk: list[FullBlock] = await self.consensus_store.get_blocks_by_hash(hashes) blocks.extend(blocks_on_disk) header_blocks: dict[bytes32, HeaderBlock] = {} @@ -897,8 +903,8 @@ async def get_header_blocks_in_range( header = get_block_header(block) elif block.transactions_generator is not None: added_coins_records, removed_coins_records = await asyncio.gather( - self.coin_store.get_coins_added_at_height(block.height), - self.coin_store.get_coins_removed_at_height(block.height), + self.consensus_store.get_coins_added_at_height(block.height), + self.consensus_store.get_coins_removed_at_height(block.height), ) tx_additions = [cr.coin for cr in added_coins_records if not cr.coinbase] removed = [cr.coin.name() for cr in removed_coins_records] @@ -936,18 +942,18 @@ async def get_block_records_at(self, heights: list[uint32]) -> list[BlockRecord] raise ValueError(f"Do not have block at height {height}") hashes.append(header_hash) - return await self.block_store.get_block_records_by_hash(hashes) + return await self.consensus_store.get_block_records_by_hash(hashes) def try_block_record(self, header_hash: bytes32) -> Optional[BlockRecord]: - if header_hash in self.__block_records: + if header_hash in self._block_records: return self.block_record(header_hash) return None async def get_block_record_from_db(self, header_hash: bytes32) -> Optional[BlockRecord]: - ret = self.__block_records.get(header_hash) + ret = self._block_records.get(header_hash) if ret is not None: return ret - return await self.block_store.get_block_record(header_hash) + return await self.consensus_store.get_block_record(header_hash) async def prev_block_hash(self, header_hashes: list[bytes32]) -> list[bytes32]: """ @@ -956,47 +962,48 @@ async def prev_block_hash(self, header_hashes: list[bytes32]) -> list[bytes32]: """ ret = [] for h in header_hashes: - b = self.__block_records.get(h) + b = self._block_records.get(h) if b is not None: ret.append(b.prev_hash) else: - ret.append(await self.block_store.get_prev_hash(h)) + ret.append(await self.consensus_store.get_prev_hash(h)) return ret async def contains_block_from_db(self, header_hash: bytes32) -> bool: - ret = header_hash in self.__block_records + ret = header_hash in self._block_records if ret: return True - return (await self.block_store.get_block_record(header_hash)) is not None + return (await self.consensus_store.get_block_record(header_hash)) is not None def remove_block_record(self, header_hash: bytes32) -> None: sbr = self.block_record(header_hash) - del self.__block_records[header_hash] - self.__heights_in_cache[sbr.height].remove(header_hash) + del self._block_records[header_hash] + self._heights_in_cache[sbr.height].remove(header_hash) def add_block_record(self, block_record: BlockRecord) -> None: """ Adds a block record to the cache. """ - self.__block_records[block_record.header_hash] = block_record - if block_record.height not in self.__heights_in_cache.keys(): - self.__heights_in_cache[block_record.height] = set() - self.__heights_in_cache[block_record.height].add(block_record.header_hash) + self._block_records[block_record.header_hash] = block_record + if block_record.height not in self._heights_in_cache.keys(): + self._heights_in_cache[block_record.height] = set() + self._heights_in_cache[block_record.height].add(block_record.header_hash) async def persist_sub_epoch_challenge_segments( self, ses_block_hash: bytes32, segments: list[SubEpochChallengeSegment] ) -> None: - await self.block_store.persist_sub_epoch_challenge_segments(ses_block_hash, segments) + async with self.consensus_store.writer() as writer: + await writer.persist_sub_epoch_challenge_segments(ses_block_hash, segments) async def get_sub_epoch_challenge_segments( self, ses_block_hash: bytes32, ) -> Optional[list[SubEpochChallengeSegment]]: - segments: Optional[list[SubEpochChallengeSegment]] = await self.block_store.get_sub_epoch_challenge_segments( - ses_block_hash - ) + segments: Optional[ + list[SubEpochChallengeSegment] + ] = await self.consensus_store.get_sub_epoch_challenge_segments(ses_block_hash) if segments is None: return None return segments @@ -1054,7 +1061,7 @@ async def lookup_block_generators(self, header_hash: bytes32, generator_refs: se remaining_refs = set() for ref_height in generator_refs: if ref_height in reorg_chain: - gen = await self.block_store.get_generator(reorg_chain[ref_height]) + gen = await self.consensus_store.get_generator(reorg_chain[ref_height]) if gen is None: raise ValueError(Err.GENERATOR_REF_HAS_NO_GENERATOR) generators[ref_height] = gen @@ -1066,6 +1073,6 @@ async def lookup_block_generators(self, header_hash: bytes32, generator_refs: se if len(remaining_refs) > 0: # any remaining references fall in the main chain, and can be looked up # in a single query - generators.update(await self.block_store.get_generators_at(remaining_refs)) + generators.update(await self.consensus_store.get_generators_at(remaining_refs)) return generators diff --git a/chia/consensus/coin_store_protocol.py b/chia/consensus/coin_store_protocol.py deleted file mode 100644 index 6d8617446605..000000000000 --- a/chia/consensus/coin_store_protocol.py +++ /dev/null @@ -1,151 +0,0 @@ -from __future__ import annotations - -from collections.abc import Collection -from typing import Optional, Protocol - -from chia_rs import CoinState -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.coin_record import CoinRecord -from chia.types.mempool_item import UnspentLineageInfo - - -class CoinStoreProtocol(Protocol): - """ - Protocol defining the interface for CoinStore. - This is a substitute for importing from chia.full_node.coin_store directly. - """ - - async def new_block( - self, - height: uint32, - timestamp: uint64, - included_reward_coins: Collection[Coin], - tx_additions: Collection[tuple[bytes32, Coin, bool]], - tx_removals: list[bytes32], - ) -> None: - """ - Add a new block to the coin store - """ - - async def get_coin_record(self, coin_id: bytes32) -> Optional[CoinRecord]: - """ - 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 - """ - - async def get_coins_added_at_height(self, height: uint32) -> list[CoinRecord]: - """ - Returns the coins added at a specific height - """ - - async def get_coins_removed_at_height(self, height: uint32) -> list[CoinRecord]: - """ - 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 = ..., - ) -> 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], Optional[uint32]]: - """ - 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) -> Optional[UnspentLineageInfo]: - """ - 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 - """ diff --git a/chia/consensus/consensus_store_protocol.py b/chia/consensus/consensus_store_protocol.py new file mode 100644 index 000000000000..f69ecc101193 --- /dev/null +++ b/chia/consensus/consensus_store_protocol.py @@ -0,0 +1,95 @@ +from __future__ import annotations + +from collections.abc import Collection +from typing import Optional, Protocol + +from chia_rs import BlockRecord, FullBlock, SubEpochChallengeSegment, SubEpochSummary +from chia_rs.sized_bytes import bytes32 +from chia_rs.sized_ints import uint32, uint64 +from typing_extensions import AsyncContextManager + +from chia.types.blockchain_format.coin import Coin +from chia.types.coin_record import CoinRecord + + +class ConsensusStoreWriteProtocol(Protocol): + """ + Protocol for performing mutating operations on the consensus store. + + Instances implementing this protocol should be acquired via the async + context manager on ConsensusStoreProtocol to ensure atomic write + operations (e.g., wrapping all writes in a single DB transaction). + """ + + # Block store writes + async def add_full_block(self, header_hash: bytes32, block: FullBlock, block_record: BlockRecord) -> None: ... + 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 persist_sub_epoch_challenge_segments( + self, ses_block_hash: bytes32, segments: list[SubEpochChallengeSegment] + ) -> None: ... + + # Coin store writes + async def rollback_to_block(self, block_index: int) -> dict[bytes32, CoinRecord]: ... + async def new_block( + self, + height: uint32, + timestamp: uint64, + included_reward_coins: Collection[Coin], + tx_additions: Collection[tuple[bytes32, Coin, bool]], + tx_removals: list[bytes32], + ) -> None: ... + + +class ConsensusStoreProtocol(Protocol): + """ + Read-only protocol for the consensus store. + + This protocol is callable and returns an async context manager. Entering the context + yields a ConsensusStoreWriteProtocol instance, which must be used for + performing write (mutating) operations. This ensures atomic writes and + makes it harder to accidentally perform writes outside a transaction. + + Example usage: + async with store.writer() as writer: + await writer.add_full_block(...) + await writer.set_peak(...) + + # Outside the context, only read methods are available + br = await store.get_block_record(header_hash) + """ + + # Writer method that returns async context manager + def writer(self) -> AsyncContextManager[ConsensusStoreWriteProtocol]: ... + + async def get_block_records_close_to_peak( + self, blocks_n: int + ) -> tuple[dict[bytes32, BlockRecord], Optional[bytes32]]: ... + async def get_full_block(self, header_hash: bytes32) -> Optional[FullBlock]: ... + 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]: ... + def get_block_from_cache(self, header_hash: bytes32) -> Optional[FullBlock]: ... + async def get_blocks_by_hash(self, header_hashes: list[bytes32]) -> list[FullBlock]: ... + async def get_block_record(self, header_hash: bytes32) -> Optional[BlockRecord]: ... + async def get_prev_hash(self, header_hash: bytes32) -> bytes32: ... + async def get_sub_epoch_challenge_segments( + self, ses_block_hash: bytes32 + ) -> Optional[list[SubEpochChallengeSegment]]: ... + 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_coin_records(self, names: Collection[bytes32]) -> list[CoinRecord]: ... + async def get_coin_record(self, coin_name: bytes32) -> Optional[CoinRecord]: ... + async def get_coins_added_at_height(self, height: uint32) -> list[CoinRecord]: ... + async def get_coins_removed_at_height(self, height: uint32) -> list[CoinRecord]: ... + + # Height map methods (kept here for now; non-async and maybe_flush remain on read protocol) + def get_ses_heights(self) -> list[uint32]: ... + def get_ses(self, height: uint32) -> SubEpochSummary: ... + def contains_height(self, height: uint32) -> bool: ... + def get_hash(self, height: uint32) -> bytes32: ... + def rollback_height_map(self, height: uint32) -> None: ... + def update_height_map(self, height: uint32, block_hash: bytes32, ses: Optional[SubEpochSummary]) -> None: ... + async def maybe_flush_height_map(self) -> None: ... + def rollback_cache_block(self, header_hash: bytes32) -> None: ... diff --git a/chia/full_node/consensus_store_sqlite3.py b/chia/full_node/consensus_store_sqlite3.py new file mode 100644 index 000000000000..4ba552f81073 --- /dev/null +++ b/chia/full_node/consensus_store_sqlite3.py @@ -0,0 +1,197 @@ +from __future__ import annotations + +from collections.abc import AsyncIterator, Collection +from contextlib import asynccontextmanager +from dataclasses import dataclass +from pathlib import Path +from typing import TYPE_CHECKING, Optional + +from chia_rs import BlockRecord, FullBlock, SubEpochChallengeSegment, SubEpochSummary +from chia_rs.sized_bytes import bytes32 +from chia_rs.sized_ints import uint32, uint64 +from typing_extensions import Self + +from chia.consensus.block_height_map import BlockHeightMap +from chia.full_node.block_store import BlockStore +from chia.full_node.coin_store import CoinStore +from chia.types.blockchain_format.coin import Coin +from chia.types.coin_record import CoinRecord +from chia.util.db_wrapper import DBWrapper2 + + +@dataclass +class ConsensusStoreSQLite3Writer: + block_store: BlockStore + coin_store: CoinStore + + async def add_full_block(self, header_hash: bytes32, block: FullBlock, block_record: BlockRecord) -> None: + await self.block_store.add_full_block(header_hash, block, block_record) + + async def rollback(self, height: int) -> None: + await self.block_store.rollback(height) + + async def set_in_chain(self, header_hashes: list[tuple[bytes32]]) -> None: + await self.block_store.set_in_chain(header_hashes) + + async def set_peak(self, header_hash: bytes32) -> None: + await self.block_store.set_peak(header_hash) + + async def persist_sub_epoch_challenge_segments( + self, ses_block_hash: bytes32, segments: list[SubEpochChallengeSegment] + ) -> None: + await self.block_store.persist_sub_epoch_challenge_segments(ses_block_hash, segments) + + async def rollback_to_block(self, block_index: int) -> dict[bytes32, CoinRecord]: + return await self.coin_store.rollback_to_block(block_index) + + async def new_block( + self, + height: uint32, + timestamp: uint64, + included_reward_coins: Collection[Coin], + tx_additions: Collection[tuple[bytes32, Coin, bool]], + tx_removals: list[bytes32], + ) -> None: + await self.coin_store.new_block(height, timestamp, included_reward_coins, tx_additions, tx_removals) + + @asynccontextmanager + async def writer(self) -> AsyncIterator[Self]: + # Return self as the writer facade + async with self.block_store.transaction(): + yield self + + +@dataclass +class ConsensusStoreSQLite3: + """ + Consensus store that combines block_store, coin_store, and height_map functionality. + """ + + block_store: BlockStore + coin_store: CoinStore + height_map: BlockHeightMap + + @classmethod + async def create( + cls, + db_wrapper: DBWrapper2, + blockchain_dir: Path, + *, + use_cache: bool = True, + selected_network: Optional[str] = None, + ) -> ConsensusStoreSQLite3: + """Create a new ConsensusStore instance, creating all underlying sub-stores internally. + + Args: + db_wrapper: Database wrapper to use for all stores + blockchain_dir: Directory path for blockchain data (used by BlockHeightMap) + use_cache: Whether to enable caching in BlockStore (default: True) + selected_network: Network selection for BlockHeightMap (default: None) + """ + # Create underlying stores + block_store = await BlockStore.create(db_wrapper, use_cache=use_cache) + coin_store = await CoinStore.create(db_wrapper) + height_map = await BlockHeightMap.create(blockchain_dir, db_wrapper, selected_network) + + return cls( + block_store=block_store, + coin_store=coin_store, + height_map=height_map, + ) + + @asynccontextmanager + async def writer(self) -> AsyncIterator[ConsensusStoreSQLite3Writer]: + """Async context manager that yields a writer facade for performing transactional writes.""" + csw = ConsensusStoreSQLite3Writer(self.block_store, self.coin_store) + async with csw.writer() as writer: + yield writer + + # Block store methods + + async def get_block_records_close_to_peak( + self, blocks_n: int + ) -> tuple[dict[bytes32, BlockRecord], Optional[bytes32]]: + return await self.block_store.get_block_records_close_to_peak(blocks_n) + + async def get_full_block(self, header_hash: bytes32) -> Optional[FullBlock]: + return await self.block_store.get_full_block(header_hash) + + async def get_block_records_by_hash(self, header_hashes: list[bytes32]) -> list[BlockRecord]: + return await self.block_store.get_block_records_by_hash(header_hashes) + + async def get_block_records_in_range(self, start: int, stop: int) -> dict[bytes32, BlockRecord]: + return await self.block_store.get_block_records_in_range(start, stop) + + def get_block_from_cache(self, header_hash: bytes32) -> Optional[FullBlock]: + return self.block_store.get_block_from_cache(header_hash) + + async def get_blocks_by_hash(self, header_hashes: list[bytes32]) -> list[FullBlock]: + return await self.block_store.get_blocks_by_hash(header_hashes) + + async def get_block_record(self, header_hash: bytes32) -> Optional[BlockRecord]: + return await self.block_store.get_block_record(header_hash) + + async def get_prev_hash(self, header_hash: bytes32) -> bytes32: + return await self.block_store.get_prev_hash(header_hash) + + async def get_sub_epoch_challenge_segments( + self, ses_block_hash: bytes32 + ) -> Optional[list[SubEpochChallengeSegment]]: + return await self.block_store.get_sub_epoch_challenge_segments(ses_block_hash) + + async def get_generator(self, header_hash: bytes32) -> Optional[bytes]: + return await self.block_store.get_generator(header_hash) + + async def get_generators_at(self, heights: set[uint32]) -> dict[uint32, bytes]: + return await self.block_store.get_generators_at(heights) + + # Coin store methods + async def get_coin_records(self, names: Collection[bytes32]) -> list[CoinRecord]: + return await self.coin_store.get_coin_records(names) + + async def get_coin_record(self, coin_name: bytes32) -> Optional[CoinRecord]: + return await self.coin_store.get_coin_record(coin_name) + + async def get_coins_added_at_height(self, height: uint32) -> list[CoinRecord]: + return await self.coin_store.get_coins_added_at_height(height) + + async def get_coins_removed_at_height(self, height: uint32) -> list[CoinRecord]: + return await self.coin_store.get_coins_removed_at_height(height) + + # Height map methods + def get_ses_heights(self) -> list[uint32]: + return self.height_map.get_ses_heights() + + def get_ses(self, height: uint32) -> SubEpochSummary: + return self.height_map.get_ses(height) + + def contains_height(self, height: uint32) -> bool: + return self.height_map.contains_height(height) + + def get_hash(self, height: uint32) -> bytes32: + return self.height_map.get_hash(height) + + def rollback_height_map(self, height: uint32) -> None: + # BlockHeightMap.rollback is synchronous + self.height_map.rollback(height) + + def update_height_map(self, height: uint32, block_hash: bytes32, ses: Optional[SubEpochSummary]) -> None: + # BlockHeightMap exposes update_height(height, header_hash, ses) + self.height_map.update_height(height, block_hash, ses) + + async def maybe_flush_height_map(self) -> None: + # BlockHeightMap.maybe_flush is asynchronous + await self.height_map.maybe_flush() + + def rollback_cache_block(self, header_hash: bytes32) -> None: + self.block_store.rollback_cache_block(header_hash) + + +if TYPE_CHECKING: + from typing import cast + + from chia.consensus.consensus_store_protocol import ConsensusStoreProtocol + + def _protocol_check(o: ConsensusStoreProtocol) -> None: ... + + _protocol_check(cast(ConsensusStoreSQLite3, None)) diff --git a/chia/full_node/full_node.py b/chia/full_node/full_node.py index 28a8c9c397d2..c9d5f304388b 100644 --- a/chia/full_node/full_node.py +++ b/chia/full_node/full_node.py @@ -42,7 +42,6 @@ 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.cost_calculator import NPCResult from chia.consensus.difficulty_adjustment import get_next_sub_slot_iters_and_difficulty @@ -53,6 +52,7 @@ from chia.full_node.block_store import BlockStore from chia.full_node.check_fork_next_block import check_fork_next_block from chia.full_node.coin_store import CoinStore +from chia.full_node.consensus_store_sqlite3 import ConsensusStoreSQLite3 from chia.full_node.full_node_api import FullNodeAPI from chia.full_node.full_node_store import FullNodeStore, FullNodeStorePeakResult, UnfinishedBlockEntry from chia.full_node.hint_management import get_hints_and_subscription_coin_ids @@ -157,7 +157,7 @@ class FullNode: _db_wrapper: Optional[DBWrapper2] = None _hint_store: Optional[HintStore] = None _block_store: Optional[BlockStore] = None - _coin_store: Optional[CoinStoreProtocol] = None + _coin_store: Optional[CoinStore] = None _mempool_manager: Optional[MempoolManager] = None _init_weight_proof: Optional[asyncio.Task[None]] = None _blockchain: Optional[Blockchain] = None @@ -267,11 +267,10 @@ async def manage(self) -> AsyncIterator[None]: self.multiprocessing_context = multiprocessing.get_context(method=multiprocessing_start_method) selected_network = self.config.get("selected_network") height_map = await BlockHeightMap.create(self.db_path.parent, self._db_wrapper, selected_network) + consensus_store = ConsensusStoreSQLite3(self.block_store, self.coin_store, height_map) self._blockchain = await Blockchain.create( - coin_store=self.coin_store, - block_store=self.block_store, + consensus_store=consensus_store, consensus_constants=self.constants, - height_map=height_map, reserved_cores=reserved_cores, single_threaded=single_threaded, log_coins=log_coins, @@ -419,7 +418,7 @@ def blockchain(self) -> Blockchain: return self._blockchain @property - def coin_store(self) -> CoinStoreProtocol: + def coin_store(self) -> CoinStore: assert self._coin_store is not None return self._coin_store diff --git a/chia/full_node/full_node_rpc_api.py b/chia/full_node/full_node_rpc_api.py index 3330583ee67a..875e2917db06 100644 --- a/chia/full_node/full_node_rpc_api.py +++ b/chia/full_node/full_node_rpc_api.py @@ -470,7 +470,7 @@ async def get_block_records(self, request: dict[str, Any]) -> EndpointResult: record: Optional[BlockRecord] = self.service.blockchain.try_block_record(header_hash) if record is None: # Fetch from DB - record = await self.service.blockchain.block_store.get_block_record(header_hash) + record = await self.service.block_store.get_block_record(header_hash) if record is None: raise ValueError(f"Block {header_hash.hex()} does not exist") @@ -538,7 +538,7 @@ async def get_block_record_by_height(self, request: dict[str, Any]) -> EndpointR record: Optional[BlockRecord] = self.service.blockchain.try_block_record(header_hash) if record is None: # Fetch from DB - record = await self.service.blockchain.block_store.get_block_record(header_hash) + record = await self.service.block_store.get_block_record(header_hash) if record is None: raise ValueError(f"Block {header_hash} does not exist") return {"block_record": record} @@ -551,7 +551,7 @@ async def get_block_record(self, request: dict[str, Any]) -> EndpointResult: record: Optional[BlockRecord] = self.service.blockchain.try_block_record(header_hash) if record is None: # Fetch from DB - record = await self.service.blockchain.block_store.get_block_record(header_hash) + record = await self.service.block_store.get_block_record(header_hash) if record is None: raise ValueError(f"Block {header_hash.hex()} does not exist") @@ -634,7 +634,7 @@ async def get_coin_records_by_puzzle_hash(self, request: dict[str, Any]) -> Endp 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]} @@ -656,7 +656,7 @@ async def get_coin_records_by_puzzle_hashes(self, request: dict[str, Any]) -> En 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]} @@ -668,7 +668,7 @@ async def get_coin_record_by_name(self, request: dict[str, Any]) -> EndpointResu raise ValueError("Name not in request") name = bytes32.from_hexstr(request["name"]) - coin_record: Optional[CoinRecord] = await self.service.blockchain.coin_store.get_coin_record(name) + coin_record: Optional[CoinRecord] = await self.service.coin_store.get_coin_record(name) if coin_record is None: raise ValueError(f"Coin record 0x{name.hex()} not found") @@ -692,7 +692,7 @@ async def get_coin_records_by_names(self, request: dict[str, Any]) -> EndpointRe 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]} @@ -714,7 +714,7 @@ async def get_coin_records_by_parent_ids(self, request: dict[str, Any]) -> Endpo 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]} @@ -743,7 +743,7 @@ async def get_coin_records_by_hint(self, request: dict[str, Any]) -> EndpointRes 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]} @@ -1018,7 +1018,7 @@ async def get_fee_estimate(self, request: dict[str, Any]) -> dict[str, Any]: assert last_peak_timestamp is not None # mypy assert last_tx_block.fees is not None # mypy - record = await self.service.blockchain.block_store.get_full_block(last_tx_block.header_hash) + record = await self.service.block_store.get_full_block(last_tx_block.header_hash) last_block_cost = 0 fee_rate_last_block = 0.0 diff --git a/chia/storage/__init__.py b/chia/storage/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tach.toml b/tach.toml index e05edea91695..c46041465137 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]]