Skip to main content

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:

ValueFork-sensitive assumptions to verify
PIVOT_SLOTActivation slot at which the second set of proof paths becomes valid.
GI_*SSZ generalized indices for the corresponding fields used in proofs.
SLOTS_PER_EPOCHEpoch calculation used when checking validator withdrawability.
BEACON_ROOTSEIP-4788 system-contract address and lookup semantics.
SSZ leaf schemasLayouts 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

NameTypeDescription
dataProcessSlashedInput@see ProcessSlashedInput

processWithdrawalProof

Verify withdrawal proof and report withdrawal to the module for valid proofs

function processWithdrawalProof(ProcessWithdrawalInput calldata data) external whenResumed;

Parameters

NameTypeDescription
dataProcessWithdrawalInput@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

NameTypeDescription
dataProcessHistoricalWithdrawalInput@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

NameTypeDescription
dataProcessBalanceProofInputThe 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

NameTypeDescription
dataProcessHistoricalBalanceProofInputThe 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

NameTypeDescription
balanceGweiuint64Validator's balance in gwei.

_getValidatorBalanceNodeInfo

function _getValidatorBalanceNodeInfo(bytes32 balanceNode, uint256 validatorIndex, Slot stateSlot)
internal
view
returns (GIndex gI, uint64 balanceGwei);

Returns

NameTypeDescription
gIGIndexGeneralized index of the node for the validatorIndex and stateSlot.
balanceGweiuint64Balance 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;