Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 1 addition & 13 deletions src/lean_spec/subspecs/sync/backfill_sync.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,24 +55,12 @@


class StoreView(Protocol):
"""
Read-only view of forkchoice state used by backfill.

Used to skip blocks already in the Store and to find the highest known
canonical slot for gap detection.

Decouples backfill from the concrete Store class.
Lets tests supply a tiny in-memory implementation.
"""
"""Read-only view of forkchoice state used by backfill."""

def has_root(self, root: Bytes32) -> bool:
"""Return True if the block root is present in the Store."""
...

def finalized_slot(self) -> Slot:
"""Return the slot of the latest finalized checkpoint."""
...

def head_slot(self) -> Slot:
"""Return the slot of the current canonical head."""
...
Expand Down
40 changes: 12 additions & 28 deletions src/lean_spec/subspecs/sync/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,30 +66,6 @@
logger = logging.getLogger(__name__)


@dataclass(slots=True)
class _SyncStoreView:
"""StoreView adapter delegating to the live SyncService.store reference.

Wraps a getter so updates to ``SyncService.store`` (assigned after each
block is processed) are observed by backfill without re-wiring.
"""

_get_store: Callable[[], Store]

def has_root(self, root: Bytes32) -> bool:
"""Return True if the block root is present in the Store."""
return root in self._get_store().blocks

def finalized_slot(self) -> Slot:
"""Return the slot of the latest finalized checkpoint."""
return self._get_store().latest_finalized.slot

def head_slot(self) -> Slot:
"""Return the slot of the current canonical head."""
store = self._get_store()
return store.blocks[store.head].slot


def _ancestor_set(blocks: dict[Bytes32, Block], head: Bytes32) -> set[Bytes32]:
"""Walk parent links from head and collect every reachable block root."""
seen: set[Bytes32] = set()
Expand Down Expand Up @@ -303,14 +279,14 @@ def _init_components(self) -> None:
"""
# BackfillSync handles fetching missing parent blocks from peers.
#
# It needs network access to request blocks and the cache to store them.
# The store view is a thin adapter that always reads the current
# store reference, since we replace `self.store` after each block.
# SyncService implements the StoreView protocol directly.
# Backfill reads `self.store` through us, so the live reference is
# always observed even as we reassign it after each block.
self._backfill = BackfillSync(
peer_manager=self.peer_manager,
block_cache=self.block_cache,
network=self.network,
store_view=_SyncStoreView(_get_store=lambda: self.store),
store_view=self,
)

# HeadSync processes incoming gossip blocks and coordinates backfill.
Expand Down Expand Up @@ -406,6 +382,14 @@ def state(self) -> SyncState:
"""Current sync state."""
return self._state

def has_root(self, root: Bytes32) -> bool:
"""Return True if the block root is present in the current store."""
return root in self.store.blocks

def head_slot(self) -> Slot:
"""Return the slot of the current canonical head."""
return self.store.blocks[self.store.head].slot

def get_progress(self) -> SyncProgress:
"""
Get current sync progress.
Expand Down
11 changes: 2 additions & 9 deletions tests/lean_spec/subspecs/sync/test_backfill_sync.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,11 @@ class FakeStoreView:
"""In-memory StoreView used to drive backfill tests.

Concrete implementation. Avoids MagicMock so tests fail loudly when
fields drift. Tests mutate `known_roots`, `head`, and `finalized` directly.
fields drift. Tests mutate `known_roots` and `head` directly.
"""

known_roots: set[Bytes32] = field(default_factory=set)
head: Slot = field(default_factory=lambda: Slot(0))
finalized: Slot = field(default_factory=lambda: Slot(0))

def has_root(self, root: Bytes32) -> bool:
"""Return True if the root has been registered with this view."""
Expand All @@ -43,10 +42,6 @@ def head_slot(self) -> Slot:
"""Return the head slot stored on this view."""
return self.head

def finalized_slot(self) -> Slot:
"""Return the finalized slot stored on this view."""
return self.finalized


@pytest.fixture
def network() -> MockNetworkRequester:
Expand Down Expand Up @@ -376,7 +371,6 @@ async def test_store_awareness_skips_known_parents(
parent_root = Bytes32(b"\x01" * 32)
store_view.known_roots.add(parent_root)
store_view.head = Slot(10)
store_view.finalized = Slot(10)

# Child is received above the head.
child = make_signed_block(
Expand Down Expand Up @@ -406,9 +400,8 @@ async def test_range_sync_triggered_by_gap_above_head(
Floor is the head slot, not the finalized slot: slots above finalized
but at or below head are already canonical for us and are not refetched.
"""
# Store head is at slot 49, finalized is older at slot 10.
# Store head is at slot 49.
store_view.head = Slot(49)
store_view.finalized = Slot(10)
store_view.known_roots.add(Bytes32.zero())

# Pre-fill the parent in the network at slot 50.
Expand Down
Loading