feat: implement deferred state updates for message validation#777
feat: implement deferred state updates for message validation#777diegomrsantos wants to merge 24 commits intosigp:unstablefrom
Conversation
Introduce a Fork enum representing SSV protocol forks: - Genesis: initial protocol version - Alan: current fork (committee_id % 128 subnet topology) - Boole: upcoming fork (MinHash subnet topology) The enum provides utilities for fork ordering, navigation, and serialization, enabling other components to query which fork applies at any given point. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Introduce ForkSchedule to manage fork activation epochs and transitions: - Tracks which fork is active at any epoch - Provides preparation window detection for dual-subscribe scenarios - Automatically activates predecessor forks when scheduling a fork Integrate ForkSchedule into SsvNetworkConfig: - Built-in networks default to Alan fork at epoch 0 - Custom configs can specify Boole epoch via ssv_boole_fork_epoch.txt This separates "when" (schedule) from "what" (fork-specific behavior), allowing subsystems to query the active fork independently. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Remove speculative functions not needed for current implementation: - Fork: next(), previous() - ForkSchedule: is_fork_active(), next_fork_after(), has_pending_transition() Keep only what's necessary: active_fork(), in_preparation_window(), fork_epoch()
- Create shared Arc<ForkSchedule> at client startup - Log fork configuration (current fork and Boole epoch) - Infrastructure ready for components to query active fork Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Create new `fork` crate under anchor/common/fork/ - Move Fork enum from ssv_types to fork crate - Move ForkSchedule from ssv_network_config to fork crate - Add fork monitor for logging fork transitions - Re-export fork types from ssv_types and ssv_network_config for compatibility The fork crate now contains all fork-related code: - fork.rs: Fork enum (Genesis, Alan, Boole) - schedule.rs: ForkSchedule for managing activation epochs - monitor.rs: Standalone task for logging fork transitions Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The early fork logging was incorrectly checking active_fork(epoch 0) instead of the current epoch. The fork_monitor already handles this correctly by using the actual current epoch from slot_clock. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Remove Fork::Genesis variant since Alan is the initial fork - Simplify ForkSchedule::new() to create Alan at epoch 0 - Simplify set_fork_epoch() to just insert without validation - Remove ForkScheduleError enum and with_fork() constructor - Use MinimalEthSpec and ChainSpec for test constants instead of magic numbers - Replace custom TestSlotClock with ManualSlotClock in monitor tests Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…refs - Fix Cargo.toml missing closing bracket in authors field - Update lib.rs doc comment to remove Genesis from fork list - Update test comment to remove Genesis reference Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Replace individual text files with a single YAML file for fork schedule configuration. This provides a cleaner and more maintainable approach for managing fork activations. - Add `from_fork_epochs()` to ForkSchedule for loading from config - Alan (genesis) is hardcoded at epoch 0 and rejected if in config - Validate chronological ordering of fork epochs - Add ssv_fork_schedule.yaml files for mainnet, holesky, and hoodi
Instead of waking up every epoch to check for state changes, the monitor now calculates when the next interesting event occurs (preparation window start or fork activation) and sleeps directly until that time. This is more efficient for forks scheduled far in the future and more precise since we wake up exactly when needed.
Remove redundant 'forks:' wrapper since the filename ssv_fork_schedule.yaml already indicates the content type.
Add new types to represent deferred state changes from message validation: - StateUpdate enum with None, Consensus, and PartialSignature variants - ConsensusStateUpdate captures signers, slot, epoch, round, message type - PartialSignatureStateUpdate captures signer, slot, epoch, kind This separation allows validation checks to be decoupled from state mutations, enabling callers to decide when/if to apply state changes. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add methods to DutyState, OperatorState, and SignerState to apply deferred state updates: - DutyState::apply_consensus_update() - DutyState::apply_partial_signature_update() - OperatorState::apply_consensus_update() - SignerState::apply_update() These methods mirror the existing update methods but take pre-computed StateUpdate values instead of raw messages, enabling deferred application. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Refactor validation functions to return StateUpdate alongside the validated message instead of mutating state directly: - validate_consensus_message returns (ValidatedSSVMessage, StateUpdate) - validate_partial_signature_message returns (ValidatedSSVMessage, StateUpdate) - ValidatedMessage now includes state_update field - Validator::commit() applies deferred state updates This enables callers to decide when to apply state changes, which is important for fork transitions where messages may be validated on multiple topics but should only update state once. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Call Validator::commit() to apply deferred state updates when a validated message is actually processed. The commit happens after all checks pass (ownership verification, doppelganger check) but before dispatching to QBFT or signature collector, ensuring state is only updated for messages we will process. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
|
Claude finished @diegomrsantos's task in 1m 41s —— View job Code Review CompleteI've completed a comprehensive review of PR #777. This PR implements deferred state updates for message validation and includes fork infrastructure (which appears to be a prerequisite for the validation changes). Architecture & DesignStrengths:
Code Quality Issues1. Missing Error Handling in
|
Issue Addressed
Improves message validation architecture by separating validation logic from state mutation.
Proposed Changes
Add StateUpdate types - New types to capture deferred state changes:
StateUpdateenum withNone,Consensus, andPartialSignaturevariantsConsensusStateUpdateandPartialSignatureStateUpdatestructsAdd apply methods - New methods to apply deferred state updates:
DutyState::apply_consensus_update()DutyState::apply_partial_signature_update()Refactor validation - Validation functions now return
(ValidatedSSVMessage, StateUpdate)instead of mutating state directlyExplicit commit -
Validator::commit()method to apply state updates when processingMotivation
Validation should be a pure operation that doesn't produce side effects. By returning a
StateUpdateinstead of mutating state during validation, callers have explicit control over when state changes are applied. This makes the code easier to reason about and test.Additional Info
update_*methods are kept for test compatibility#[must_use]toStateUpdateto prevent accidental omission of commit