Refactor BlockHeightMap Creation to Decouple It from Blockchain (#19711)

Create `BlockHeightMap` outside of `Blockchain` so `chia.consensus`
doesn't have to depend on a specific storage mechanism.
This commit is contained in:
Richard Kiss
2025-06-17 11:53:26 -07:00
committed by GitHub
parent 7c25640c3e
commit 3e30f67405
9 changed files with 54 additions and 28 deletions
+3 -1
View File
@@ -16,6 +16,7 @@ from chia_rs.sized_ints import uint32
from chia.consensus.blockchain import Blockchain
from chia.consensus.default_constants import DEFAULT_CONSTANTS
from chia.consensus.get_block_generator import get_block_generator
from chia.full_node.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.serialized_program import SerializedProgram
@@ -68,7 +69,8 @@ async def main(db_path: Path) -> None:
start_time = monotonic()
# make configurable
reserved_cores = 4
blockchain = await Blockchain.create(coin_store, block_store, DEFAULT_CONSTANTS, db_path.parent, reserved_cores)
height_map = await BlockHeightMap.create(db_path.parent, db_wrapper)
blockchain = await Blockchain.create(coin_store, block_store, height_map, DEFAULT_CONSTANTS, reserved_cores)
peak = blockchain.get_peak()
assert peak is not None
+3 -1
View File
@@ -8,6 +8,7 @@ from pathlib import Path
from chia_rs import ConsensusConstants
from chia.consensus.blockchain import Blockchain
from chia.full_node.block_height_map import BlockHeightMap
from chia.full_node.block_store import BlockStore
from chia.full_node.coin_store import CoinStore
from chia.util.db_wrapper import DBWrapper2
@@ -21,7 +22,8 @@ async def create_ram_blockchain(
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)
blockchain = await Blockchain.create(coin_store, block_store, consensus_constants, Path("."), 2)
height_map = await BlockHeightMap.create(Path("."), db_wrapper)
blockchain = await Blockchain.create(coin_store, block_store, height_map, consensus_constants, 2)
try:
yield db_wrapper, blockchain
finally:
@@ -22,6 +22,7 @@ from chia.consensus.block_body_validation import ForkInfo
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_height_map import BlockHeightMap
from chia.full_node.block_store import BlockStore
from chia.full_node.coin_store import CoinStore
from chia.full_node.full_block_utils import GeneratorBlockInfo
@@ -72,7 +73,8 @@ async def test_block_store(tmp_dir: Path, db_version: int, bt: BlockTools, use_c
# 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)
bc = await Blockchain.create(coin_store_2, store_2, bt.constants, tmp_dir, 2)
height_map = await BlockHeightMap.create(tmp_dir, db_wrapper_2)
bc = await Blockchain.create(coin_store_2, store_2, height_map, bt.constants, 2)
store = await BlockStore.create(db_wrapper, use_cache=use_cache)
await BlockStore.create(db_wrapper_2)
@@ -147,7 +149,8 @@ async def test_get_full_blocks_at(
# 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)
bc = await Blockchain.create(coin_store, block_store, bt.constants, tmp_dir, 2)
height_map = await BlockHeightMap.create(tmp_dir, db_wrapper)
bc = await Blockchain.create(coin_store, block_store, height_map, bt.constants, 2)
count = 0
fork_info = ForkInfo(-1, -1, bt.constants.GENESIS_CHALLENGE)
@@ -174,7 +177,8 @@ async def test_get_block_records_in_range(
# 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)
bc = await Blockchain.create(coin_store, block_store, bt.constants, tmp_dir, 2)
height_map = await BlockHeightMap.create(tmp_dir, db_wrapper)
bc = await Blockchain.create(coin_store, block_store, height_map, bt.constants, 2)
count = 0
fork_info = ForkInfo(-1, -1, bt.constants.GENESIS_CHALLENGE)
@@ -203,8 +207,8 @@ async def test_get_block_bytes_in_range_in_main_chain(
# 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)
bc = await Blockchain.create(coin_store, block_store, bt.constants, tmp_dir, 2)
height_map = await BlockHeightMap.create(tmp_dir, db_wrapper)
bc = await Blockchain.create(coin_store, block_store, height_map, bt.constants, 2)
count = 0
fork_info = ForkInfo(-1, -1, bt.constants.GENESIS_CHALLENGE)
for b1, b2 in zip(blocks, alt_blocks):
@@ -232,7 +236,8 @@ async def test_deadlock(tmp_dir: Path, db_version: int, bt: BlockTools, use_cach
store = await BlockStore.create(wrapper, use_cache=use_cache)
coin_store_2 = await CoinStore.create(wrapper_2)
store_2 = await BlockStore.create(wrapper_2)
bc = await Blockchain.create(coin_store_2, store_2, bt.constants, tmp_dir, 2)
height_map = await BlockHeightMap.create(tmp_dir, wrapper_2)
bc = await Blockchain.create(coin_store_2, store_2, height_map, bt.constants, 2)
block_records = []
for block in blocks:
await _validate_and_add_block(bc, block)
@@ -262,7 +267,8 @@ async def test_rollback(bt: BlockTools, tmp_dir: Path, use_cache: bool, default_
# 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)
bc = await Blockchain.create(coin_store, block_store, bt.constants, tmp_dir, 2)
height_map = await BlockHeightMap.create(tmp_dir, db_wrapper)
bc = await Blockchain.create(coin_store, block_store, height_map, bt.constants, 2)
# insert all blocks
count = 0
@@ -324,7 +330,8 @@ async def test_count_compactified_blocks(bt: BlockTools, tmp_dir: Path, db_versi
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)
bc = await Blockchain.create(coin_store, block_store, bt.constants, tmp_dir, 2)
height_map = await BlockHeightMap.create(tmp_dir, db_wrapper)
bc = await Blockchain.create(coin_store, block_store, height_map, bt.constants, 2)
count = await block_store.count_compactified_blocks()
assert count == 0
@@ -344,7 +351,8 @@ async def test_count_uncompactified_blocks(bt: BlockTools, tmp_dir: Path, db_ver
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)
bc = await Blockchain.create(coin_store, block_store, bt.constants, tmp_dir, 2)
height_map = await BlockHeightMap.create(tmp_dir, db_wrapper)
bc = await Blockchain.create(coin_store, block_store, height_map, bt.constants, 2)
count = await block_store.count_uncompactified_blocks()
assert count == 0
@@ -377,7 +385,8 @@ async def test_replace_proof(bt: BlockTools, tmp_dir: Path, db_version: int, use
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)
bc = await Blockchain.create(coin_store, block_store, bt.constants, tmp_dir, 2)
height_map = await BlockHeightMap.create(tmp_dir, db_wrapper)
bc = await Blockchain.create(coin_store, block_store, height_map, bt.constants, 2)
for block in blocks:
await _validate_and_add_block(bc, block)
@@ -457,7 +466,8 @@ async def test_get_blocks_by_hash(tmp_dir: Path, bt: BlockTools, db_version: int
# 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)
bc = await Blockchain.create(coin_store_2, store_2, bt.constants, tmp_dir, 2)
height_map = await BlockHeightMap.create(tmp_dir, db_wrapper_2)
bc = await Blockchain.create(coin_store_2, store_2, height_map, bt.constants, 2)
store = await BlockStore.create(db_wrapper, use_cache=use_cache)
await BlockStore.create(db_wrapper_2)
@@ -496,7 +506,8 @@ async def test_get_block_bytes_in_range(tmp_dir: Path, bt: BlockTools, db_versio
# 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)
bc = await Blockchain.create(coin_store_2, store_2, bt.constants, tmp_dir, 2)
height_map = await BlockHeightMap.create(tmp_dir, db_wrapper_2)
bc = await Blockchain.create(coin_store_2, store_2, height_map, bt.constants, 2)
await BlockStore.create(db_wrapper_2)
@@ -568,7 +579,8 @@ async def test_get_prev_hash(tmp_dir: Path, bt: BlockTools, db_version: int, use
# 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)
bc = await Blockchain.create(coin_store_2, store_2, bt.constants, tmp_dir, 2)
height_map = await BlockHeightMap.create(tmp_dir, db_wrapper_2)
bc = await Blockchain.create(coin_store_2, store_2, height_map, bt.constants, 2)
store = await BlockStore.create(db_wrapper, use_cache=use_cache)
await BlockStore.create(db_wrapper_2)
@@ -19,6 +19,7 @@ from chia.consensus.block_body_validation import ForkInfo
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_height_map import BlockHeightMap
from chia.full_node.block_store import BlockStore
from chia.full_node.coin_store import CoinStore
from chia.full_node.hint_store import HintStore
@@ -314,7 +315,8 @@ async def test_basic_reorg(tmp_dir: Path, db_version: int, bt: BlockTools) -> No
blocks = bt.get_consecutive_blocks(initial_block_count)
coin_store = await CoinStore.create(db_wrapper)
store = await BlockStore.create(db_wrapper)
b: Blockchain = await Blockchain.create(coin_store, store, bt.constants, tmp_dir, 2)
height_map = await BlockHeightMap.create(tmp_dir, db_wrapper)
b: Blockchain = await Blockchain.create(coin_store, store, height_map, bt.constants, 2)
try:
records: list[Optional[CoinRecord]] = []
@@ -380,7 +382,8 @@ async def test_get_puzzle_hash(tmp_dir: Path, db_version: int, bt: BlockTools) -
)
coin_store = await CoinStore.create(db_wrapper)
store = await BlockStore.create(db_wrapper)
b: Blockchain = await Blockchain.create(coin_store, store, bt.constants, tmp_dir, 2)
height_map = await BlockHeightMap.create(tmp_dir, db_wrapper)
b: Blockchain = await Blockchain.create(coin_store, store, height_map, bt.constants, 2)
for block in blocks:
await _validate_and_add_block(b, block)
peak = b.get_peak()
+3 -1
View File
@@ -12,6 +12,7 @@ from chia.cmds.db_upgrade_func import convert_v1_to_v2
from chia.consensus.block_body_validation import ForkInfo
from chia.consensus.blockchain import Blockchain
from chia.consensus.multiprocess_validation import PreValidationResult
from chia.full_node.block_height_map import BlockHeightMap
from chia.full_node.block_store import BlockStore
from chia.full_node.coin_store import CoinStore
from chia.full_node.hint_store import HintStore
@@ -67,7 +68,8 @@ async def test_blocks(default_1000_blocks, with_hints: bool):
for h in hints:
await hint_store1.add_hints([(h[0], h[1])])
bc = await Blockchain.create(coin_store1, block_store1, test_constants, Path("."), reserved_cores=0)
height_map = await BlockHeightMap.create(Path("."), db_wrapper1)
bc = await Blockchain.create(coin_store1, block_store1, height_map, 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:
+3 -1
View File
@@ -17,6 +17,7 @@ from chia.consensus.block_body_validation import ForkInfo
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_height_map import BlockHeightMap
from chia.full_node.block_store import BlockStore
from chia.full_node.coin_store import CoinStore
from chia.simulator.block_tools import test_constants
@@ -143,8 +144,9 @@ async def make_db(db_file: Path, blocks: list[FullBlock]) -> None:
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, test_constants, Path("."), reserved_cores=0)
bc = await Blockchain.create(coin_store, block_store, height_map, 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:
+4 -1
View File
@@ -11,6 +11,7 @@ from chia_rs import ConsensusConstants, FullBlock
from chia_rs.sized_ints import uint64
from chia.consensus.blockchain import Blockchain
from chia.full_node.block_height_map import BlockHeightMap
from chia.full_node.block_store import BlockStore
from chia.full_node.coin_store import CoinStore
from chia.simulator.block_tools import BlockTools
@@ -26,7 +27,9 @@ async def create_blockchain(
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)
bc1 = await Blockchain.create(coin_store, store, constants, Path("."), 2, single_threaded=True, log_coins=True)
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)
try:
assert bc1.get_peak() is None
yield bc1, wrapper
+4 -6
View File
@@ -7,7 +7,6 @@ import logging
import traceback
from concurrent.futures import Executor, ThreadPoolExecutor
from enum import Enum
from pathlib import Path
from typing import TYPE_CHECKING, ClassVar, Optional, cast
from chia_rs import (
@@ -124,13 +123,12 @@ class Blockchain:
async def create(
coin_store: CoinStore,
block_store: BlockStore,
height_map: BlockHeightMap,
consensus_constants: ConsensusConstants,
blockchain_dir: Path,
reserved_cores: int,
*,
single_threaded: bool = False,
log_coins: bool = False,
selected_network: Optional[str] = None,
) -> Blockchain:
"""
Initializes a blockchain with the BlockRecords from disk, assuming they have all been
@@ -158,7 +156,7 @@ class Blockchain:
self.coin_store = coin_store
self.block_store = block_store
self._shut_down = False
await self._load_chain_from_store(blockchain_dir, selected_network)
await self._load_chain_from_store(height_map)
self._seen_compact_proofs = set()
return self
@@ -166,11 +164,11 @@ class Blockchain:
self._shut_down = True
self.pool.shutdown(wait=True)
async def _load_chain_from_store(self, blockchain_dir: Path, selected_network: Optional[str] = None) -> None:
async def _load_chain_from_store(self, height_map: BlockHeightMap) -> None:
"""
Initializes the state of the Blockchain class from the database.
"""
self.__height_map = await BlockHeightMap.create(blockchain_dir, self.block_store.db_wrapper, selected_network)
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)
+4 -2
View File
@@ -48,6 +48,7 @@ from chia.consensus.make_sub_epoch_summary import next_sub_epoch_summary
from chia.consensus.multiprocess_validation import PreValidationResult, pre_validate_block
from chia.consensus.pot_iterations import calculate_sp_iters
from chia.consensus.signage_point import SignagePoint
from chia.full_node.block_height_map import BlockHeightMap
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
@@ -263,15 +264,16 @@ class FullNode:
log_coins = self.config.get("log_coins", False)
multiprocessing_start_method = process_config_start_method(config=self.config, log=self.log)
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)
self._blockchain = await Blockchain.create(
coin_store=self.coin_store,
block_store=self.block_store,
consensus_constants=self.constants,
blockchain_dir=self.db_path.parent,
height_map=height_map,
reserved_cores=reserved_cores,
single_threaded=single_threaded,
log_coins=log_coins,
selected_network=self.config.get("selected_network"),
)
self._mempool_manager = MempoolManager(