Verifier
Verifier.sol is a supplementary contract responsible for validating Consensus Layer (CL) data proofs against the beacon block root obtained via EIP-4788 and reporting the verified facts to the module. All of its proof-processing methods are permissionless - anyone (typically the prover bot or the Node Operator) can submit a valid proof.
The following proof types are supported:
- validator withdrawal (including historical);
- validator balance (including historical);
- validator slashing.
Hard-fork compatibility
Verifier does not inspect the Consensus Layer fork version directly. Instead, it supports two sets of SSZ generalized indices, separated by PIVOT_SLOT. Proofs for state slots before the pivot use one set of indices, while proofs for the pivot slot and later use the other.
FIRST_SUPPORTED_SLOT is the deployment's lower acceptance boundary and should not be advanced merely because a new fork occurs. If a fork changes any relevant proof path, the immutable constructor values cannot be updated. A replacement Verifier must be deployed, tested with proofs from both sides of the fork boundary, and authorized on the module before activation. A changed leaf schema may also require contract code changes rather than new gindices alone.
The Mainnet deployment sets both FIRST_SUPPORTED_SLOT and PIVOT_SLOT to the Electra activation slot and configures both sets of indices for the Electra layout. As a result, the deployment supports Electra and remains compatible with later hard forks while the relevant SSZ indices remain unchanged. A hard fork does not necessarily change these paths; nevertheless, the generalized indices must be revalidated against the finalized specification for each fork.
The following values and assumptions must be tracked:
| Value | Fork-sensitive assumptions to verify |
|---|---|
PIVOT_SLOT | Activation slot at which the second set of proof paths becomes valid. |
GI_* | SSZ generalized indices for the corresponding fields used in proofs. |
SLOTS_PER_EPOCH | Epoch calculation used when checking validator withdrawability. |
BEACON_ROOTS | EIP-4788 system-contract address and lookup semantics. |
| SSZ leaf schemas | Layouts of BeaconBlockHeader, Validator, and Withdrawal, whose roots are computed by the contract. |
Upgradability
The contract is immutable.
State Variables
BEACON_ROOTS
address public constant BEACON_ROOTS = 0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02
MAX_BP
uint256 internal constant MAX_BP = 10_000
MIN_WITHDRAWAL_RATIO
Minimum withdrawal amount as a ratio of the expected validator balance, expressed in basis points (10 000 = 100%).
uint256 public immutable MIN_WITHDRAWAL_RATIO
SLOTS_PER_EPOCH
uint64 public immutable SLOTS_PER_EPOCH
SLOTS_PER_HISTORICAL_ROOT
Count of historical roots per accumulator.
See https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#time-parameters
uint64 public constant SLOTS_PER_HISTORICAL_ROOT = 8192
GI_FIRST_WITHDRAWAL_PREV
This index is relative to a state like: BeaconState.latest_execution_payload_header.withdrawals[0].
GIndex public immutable GI_FIRST_WITHDRAWAL_PREV
GI_FIRST_WITHDRAWAL_CURR
This index is relative to a state like: BeaconState.latest_execution_payload_header.withdrawals[0].
GIndex public immutable GI_FIRST_WITHDRAWAL_CURR
GI_FIRST_VALIDATOR_PREV
This index is relative to a state like: BeaconState.validators[0].
GIndex public immutable GI_FIRST_VALIDATOR_PREV
GI_FIRST_VALIDATOR_CURR
This index is relative to a state like: BeaconState.validators[0].
GIndex public immutable GI_FIRST_VALIDATOR_CURR
GI_FIRST_HISTORICAL_SUMMARY_PREV
This index is relative to a state like: BeaconState.historical_summaries[0].
GIndex public immutable GI_FIRST_HISTORICAL_SUMMARY_PREV
GI_FIRST_HISTORICAL_SUMMARY_CURR
This index is relative to a state like: BeaconState.historical_summaries[0].
GIndex public immutable GI_FIRST_HISTORICAL_SUMMARY_CURR
GI_FIRST_BLOCK_ROOT_IN_SUMMARY
This index is relative to HistoricalSummary like: HistoricalSummary.blockRoots[0]. Considered constant across forks.
GIndex public constant GI_FIRST_BLOCK_ROOT_IN_SUMMARY =
GIndex.wrap(0x000000000000000000000000000000000000000000000000000000000040000d)
GI_FIRST_BALANCES_NODE_PREV
This index is relative to a state like: BeaconState.balances[0].
GIndex public immutable GI_FIRST_BALANCES_NODE_PREV
GI_FIRST_BALANCES_NODE_CURR
This index is relative to a state like: BeaconState.balances[0].
GIndex public immutable GI_FIRST_BALANCES_NODE_CURR
FIRST_SUPPORTED_SLOT
The very first slot the verifier is supposed to accept proofs for.
Slot public immutable FIRST_SUPPORTED_SLOT
PIVOT_SLOT
The first slot of the currently compatible fork.
Slot public immutable PIVOT_SLOT
CAPELLA_SLOT
Historical summaries started accumulating from the slot of Capella fork.
Slot public immutable CAPELLA_SLOT
WITHDRAWAL_ADDRESS
An address withdrawals are supposed to happen to (Lido withdrawal credentials).
address public immutable WITHDRAWAL_ADDRESS
MODULE
Staking module contract.
IBaseModule public immutable MODULE
Functions
constructor
The previous and current forks can be essentially the same.
constructor(
address withdrawalAddress,
address module,
uint64 slotsPerEpoch,
GIndices memory gindices,
Slot firstSupportedSlot,
Slot pivotSlot,
Slot capellaSlot,
uint256 minWithdrawalRatio,
address admin
) ;
processSlashedProof
Verify proof of a slashed validator and report it to the module
function processSlashedProof(ProcessSlashedInput calldata data) external whenResumed;
Parameters
| Name | Type | Description |
|---|---|---|
data | ProcessSlashedInput | @see ProcessSlashedInput |
processWithdrawalProof
Verify withdrawal proof and report withdrawal to the module for valid proofs
function processWithdrawalProof(ProcessWithdrawalInput calldata data) external whenResumed;
Parameters
| Name | Type | Description |
|---|---|---|
data | ProcessWithdrawalInput | @see ProcessWithdrawalInput |
processHistoricalWithdrawalProof
Verify withdrawal proof against historical summaries data and report withdrawal to the module for valid proofs
function processHistoricalWithdrawalProof(ProcessHistoricalWithdrawalInput calldata data) external whenResumed;
Parameters
| Name | Type | Description |
|---|---|---|
data | ProcessHistoricalWithdrawalInput | @see ProcessHistoricalWithdrawalInput |
processBalanceProof
Verify a validator's balance proof from a recent beacon block and sync the key added balance.
function processBalanceProof(ProcessBalanceProofInput calldata data) external whenResumed;
Parameters
| Name | Type | Description |
|---|---|---|
data | ProcessBalanceProofInput | The balance proof input containing recent block header, validator witness, and balance witness. |
processHistoricalBalanceProof
Verify a validator's balance proof from a historical beacon block and sync the key added balance. A historical proof is needed because the validator's balance may have increased at some point in the past and later decreased (e.g. due to inactivity leak or penalties). A recent proof alone would miss that peak, so a historical proof allows capturing the highest observed balance.
function processHistoricalBalanceProof(ProcessHistoricalBalanceProofInput calldata data) external whenResumed;
Parameters
| Name | Type | Description |
|---|---|---|
data | ProcessHistoricalBalanceProofInput | The balance proof input containing recent + historical block headers, validator witness, and balance witness. |
_reportSingleValidator
function _reportSingleValidator(WithdrawnValidatorInfo memory info) internal;
_getParentBlockRoot
function _getParentBlockRoot(uint64 blockTimestamp) internal view returns (bytes32);
_processWithdrawalProof
header MUST be trusted at this point.
function _processWithdrawalProof(
WithdrawalWitness calldata withdrawal,
ValidatorWitness calldata validator,
BeaconBlockHeader calldata header,
uint256 nodeOperatorId,
uint256 keyIndex
) internal view returns (uint256 withdrawalAmount);
_processBalanceProof
function _processBalanceProof(
ValidatorWitness calldata validator,
BalanceWitness calldata balance,
bytes32 stateRoot,
Slot stateSlot
) internal view returns (uint64 balanceGwei);
_verifyValidatorBalance
function _verifyValidatorBalance(
uint256 validatorIndex,
bytes32 balanceNode,
bytes32 stateRoot,
Slot stateSlot,
bytes32[] calldata proof
) internal view returns (uint64 balanceGwei);
Returns
| Name | Type | Description |
|---|---|---|
balanceGwei | uint64 | Validator's balance in gwei. |
_getValidatorBalanceNodeInfo
function _getValidatorBalanceNodeInfo(bytes32 balanceNode, uint256 validatorIndex, Slot stateSlot)
internal
view
returns (GIndex gI, uint64 balanceGwei);
Returns
| Name | Type | Description |
|---|---|---|
gI | GIndex | Generalized index of the node for the validatorIndex and stateSlot. |
balanceGwei | uint64 | Balance in gwei extracted from the balanceNode. |
_getValidatorGI
function _getValidatorGI(uint256 offset, Slot stateSlot) internal view returns (GIndex);
_getWithdrawalGI
function _getWithdrawalGI(uint256 offset, Slot stateSlot) internal view returns (GIndex);
_getValidatorBalanceGI
function _getValidatorBalanceGI(uint256 offset, Slot stateSlot) internal view returns (GIndex);
_getHistoricalBlockRootGI
function _getHistoricalBlockRootGI(Slot recentSlot, Slot targetSlot) internal view returns (GIndex gI);
_computeEpochAtSlot
function _computeEpochAtSlot(Slot slot) internal view returns (uint256);
__checkRole
function __checkRole(bytes32 role) internal view override;