LightClient
Inherits: Initializable, OwnableUpgradeable, UUPSUpgradeable
Title: Light Client Contract
This contract serves as an always-on client
that verifies HotShot’s state (Espresso’s consensus state) which can be used by
Rollup contracts on L1 (Ethereum).
This state is submitted by any state-prover with evidence which is
a SNARK proof that proves consensus.
This contract also keeps track of the current epoch.
For this version, the epoch is not used.
The light client state primarily consists of:
- the merkle root of finalized block commitments,
- the fee ledger commitment and
- the active stake table commitment
You can use this contract to keep track of its finalized states in safe, authenticated ways.
State Variables
genesisStakeTableState
genesis stake commitment
StakeTableState public genesisStakeTableState
genesisState
genesis block commitment
LightClientState public genesisState
finalizedState
Finalized HotShot’s light client state
LightClientState public finalizedState
permissionedProver
the address of the prover that can call the newFinalizedState function when the contract is in permissioned prover mode. This address is address(0) when the contract is not in permissioned prover mode
address public permissionedProver
stateHistoryRetentionPeriod
Max number of seconds worth of state commitments to record based on this block timestamp
uint32 public stateHistoryRetentionPeriod
stateHistoryFirstIndex
index of first block in block state series
use this instead of index 0 since old states would be set to zero to keep storage costs constant to stateHistoryRetentionPeriod
uint64 public stateHistoryFirstIndex
stateHistoryCommitments
an array to store the L1 block heights, HotShot Block Heights and their respective state history commitments
StateHistoryCommitment[] public stateHistoryCommitments
Functions
constructor
Constructor disables initializers to prevent the implementation contract from being initialized
This is standard practice for OpenZeppelin upgradeable contracts. Storage is on the proxy contract since it calls this cnotract via delegatecall
Note: oz-upgrades-unsafe-allow: constructor
constructor() ;
initialize
This contract is called by the proxy when you deploy this contract
function initialize(
LightClientState memory _genesis,
StakeTableState memory _genesisStakeTableState,
uint32 _stateHistoryRetentionPeriod,
address owner
) public initializer;
Parameters
| Name | Type | Description |
|---|---|---|
_genesis | LightClientState | The initial state of the light client |
_genesisStakeTableState | StakeTableState | |
_stateHistoryRetentionPeriod | uint32 | The maximum retention period (in seconds) for the state history. the min retention period allowed is 1 hour and max 365 days |
owner | address | The address of the contract owner |
currentBlockNumber
returns the current block number
function currentBlockNumber() public view virtual returns (uint256);
getVersion
Use this to get the implementation contract version
function getVersion()
public
pure
virtual
returns (uint8 majorVersion, uint8 minorVersion, uint8 patchVersion);
Returns
| Name | Type | Description |
|---|---|---|
majorVersion | uint8 | The major version of the contract |
minorVersion | uint8 | The minor version of the contract |
patchVersion | uint8 | The patch version of the contract |
_authorizeUpgrade
only the owner can authorize an upgrade
function _authorizeUpgrade(address newImplementation) internal virtual override onlyOwner;
renounceOwnership
Cannot renounce ownership
Override renounceOwnership() to revert, preventing accidental or malicious ownership renunciation
function renounceOwnership() public virtual override onlyOwner;
_initializeState
Initialization of contract variables happens in this method because the LightClient contract is upgradable and thus has its constructor method disabled.
function _initializeState(
LightClientState memory _genesis,
StakeTableState memory _genesisStakeTableState,
uint32 _stateHistoryRetentionPeriod
) internal;
Parameters
| Name | Type | Description |
|---|---|---|
_genesis | LightClientState | The initial state of the light client |
_genesisStakeTableState | StakeTableState | The initial stake table state of the light client |
_stateHistoryRetentionPeriod | uint32 | The maximum retention period (in seconds) for the state history. The min retention period allowed is 1 hour and the max is 365 days. |
newFinalizedState
Update the latest finalized light client state. It must be updated periodically, especially an update for the last block for every period has to be submitted before any newer state can be accepted since the stake table commitments of that block become the snapshots used for vote verifications later on.
While newState.stakeTable* refers to the (possibly) new stake table states,
the entire newState needs to be signed by stakers in finalizedState
if the permissionedProver is set, only the permissionedProver can call this function
the state history for stateHistoryRetentionPeriod L1 blocks are also recorded in the
stateHistoryCommitments array
function newFinalizedState(
LightClientState memory newState,
IPlonkVerifier.PlonkProof memory proof
) external virtual;
Parameters
| Name | Type | Description |
|---|---|---|
newState | LightClientState | new light client state |
proof | IPlonkVerifier.PlonkProof | PlonkProof |
_getVk
a technically unnecessary but luckily zero-cost indirection for the benefit of
having
IPlonkVerifier.VerifyingKey rust alloy bindings, included only if appear in a public func.
function _getVk() public pure virtual returns (IPlonkVerifier.VerifyingKey memory vk);
verifyProof
Verify the Plonk proof, marked as virtual for easier testing as we can swap VK
used in inherited contracts.
function verifyProof(LightClientState memory state, IPlonkVerifier.PlonkProof memory proof)
internal
virtual;
setPermissionedProver
set the permissionedProver to the non-zero address provided
this function can also be used to update the permissioned prover once it’s a different address to the current permissioned prover
function setPermissionedProver(address prover) public virtual onlyOwner;
disablePermissionedProverMode
set the permissionedProver to address(0)
if it was already disabled, then revert with the error, NoChangeRequired
function disablePermissionedProverMode() public virtual onlyOwner;
updateStateHistory
Updates the stateHistoryCommitments array when a new finalized state is added
and prunes the most outdated element starting from the first element if they fall outside
the
stateHistoryRetentionPeriod.
the block timestamp is used to determine if the stateHistoryCommitments array should be pruned, based on the stateHistoryRetentionPeriod (seconds).
A FIFO approach is used to remove the most outdated element from the start of the array. However, only one outdated element is removed per invocation of this function, even if multiple elements exceed the retention period. As a result, some outdated elements may remain in the array temporarily until subsequent invocations of this function.
the delete method does not reduce the array length but resets the value at the
specified index to zero. the stateHistoryFirstIndex variable acts as an offset to indicate
the starting point for reading the array, since the length of the array is not reduced
even after deletion.
function updateStateHistory(
uint64 blockNumber,
uint64 blockTimestamp,
LightClientState memory state
) internal;
Parameters
| Name | Type | Description |
|---|---|---|
blockNumber | uint64 | The block number of the new finalized state. |
blockTimestamp | uint64 | The block timestamp used to check the retention period. |
state | LightClientState | The new LightClientState being added to the array. |
lagOverEscapeHatchThreshold
checks if the state updates lag behind the specified block threshold based on the provided block number.
Reverts if there isn’t enough state history to make an accurate comparison. Reverts if the blockThreshold is zero
function lagOverEscapeHatchThreshold(uint256 blockNumber, uint256 blockThreshold)
public
view
virtual
returns (bool);
Parameters
| Name | Type | Description |
|---|---|---|
blockNumber | uint256 | The block number to compare against the latest state updates. |
blockThreshold | uint256 | The number of blocks updates this contract is allowed to lag behind. |
Returns
| Name | Type | Description |
|---|---|---|
<none> | bool | bool returns true if the lag exceeds the blockThreshold; otherwise, false. |
getHotShotCommitment
get the HotShot commitment that represents the Merkle root containing the leaf at the provided hotShotBlockHeight where the block height in the array is greater than
if the provided hotShotBlockHeight is greater than or equal to the latest commitment in the array, the function reverts.
function getHotShotCommitment(uint256 hotShotBlockHeight)
public
view
virtual
returns (BN254.ScalarField hotShotBlockCommRoot, uint64 hotshotBlockHeight);
Parameters
| Name | Type | Description |
|---|---|---|
hotShotBlockHeight | uint256 | the HotShot block height |
Returns
| Name | Type | Description |
|---|---|---|
hotShotBlockCommRoot | BN254.ScalarField | the HotShot commitment root |
hotshotBlockHeight | uint64 | the HotShot block height for the corresponding commitment root |
getStateHistoryCount
get the number of state history commitments
function getStateHistoryCount() public view returns (uint256);
Returns
| Name | Type | Description |
|---|---|---|
<none> | uint256 | uint256 The number of state history commitments |
setstateHistoryRetentionPeriod
sets the maximum retention period for storing block state history.
Reverts with InvalidMaxStateHistory if the provided value is less than 1 hour,
more than 365 days or less than or equal to the current state history retention period.
function setstateHistoryRetentionPeriod(uint32 historySeconds) public onlyOwner;
Parameters
| Name | Type | Description |
|---|---|---|
historySeconds | uint32 | The maximum number of seconds for which state history updates will be stored, based on the block timestamp. It must be greater than or equal to the current state history retention period and must be at least 1 hour and max 365 days. |
isPermissionedProverEnabled
Check if permissioned prover is enabled
function isPermissionedProverEnabled() public view returns (bool);
Events
Upgrade
upgrade event when the proxy updates the implementation it’s pointing to
event Upgrade(address implementation);
PermissionedProverRequired
when a permissioned prover is set, this event is emitted.
event PermissionedProverRequired(address permissionedProver);
PermissionedProverNotRequired
when the permissioned prover is unset, this event is emitted.
event PermissionedProverNotRequired();
NewState
Event that a new finalized state has been successfully verified and updated
event NewState(
uint64 indexed viewNum, uint64 indexed blockHeight, BN254.ScalarField blockCommRoot
);
Errors
OutdatedState
The state is outdated and older than currently known finalizedState
error OutdatedState();
InvalidArgs
Invalid user inputs: wrong format or non-sensible arguments
error InvalidArgs();
InvalidProof
Wrong plonk proof or public inputs.
error InvalidProof();
WrongStakeTableUsed
Wrong stake table used, should match finalizedState
error WrongStakeTableUsed();
InvalidAddress
Invalid address
error InvalidAddress();
ProverNotPermissioned
Only a permissioned prover can perform this action
error ProverNotPermissioned();
NoChangeRequired
If the same mode or prover is sent to the function, then no change is required
error NoChangeRequired();
InsufficientSnapshotHistory
Invalid L1 Block for checking Light Client Updates, premature or in the future
error InsufficientSnapshotHistory();
InvalidHotShotBlockForCommitmentCheck
Invalid HotShot Block for checking HotShot commitments, premature or in the future
error InvalidHotShotBlockForCommitmentCheck();
InvalidMaxStateHistory
Invalid Max Block States
error InvalidMaxStateHistory();
OwnershipCannotBeRenounced
Cannot renounce ownership
error OwnershipCannotBeRenounced();
Structs
LightClientState
The finalized HotShot state (as the digest of the entire HotShot state)
struct LightClientState {
uint64 viewNum;
uint64 blockHeight;
BN254.ScalarField blockCommRoot;
}
Properties
| Name | Type | Description |
|---|---|---|
viewNum | uint64 | The latest view number of the finalized HotShot chain |
blockHeight | uint64 | The block height of the latest finalized block |
blockCommRoot | BN254.ScalarField | The merkle root of historical block commitments (BN254::ScalarField) |
StakeTableState
The finalized HotShot Stake state (as the digest of the entire HotShot state)
struct StakeTableState {
uint256 threshold;
BN254.ScalarField blsKeyComm;
BN254.ScalarField schnorrKeyComm;
BN254.ScalarField amountComm;
}
Properties
| Name | Type | Description |
|---|---|---|
threshold | uint256 | The (stake-weighted) quorum threshold for a QC to be considered as valid |
blsKeyComm | BN254.ScalarField | The commitment to the BlsVerKey column of the stake table |
schnorrKeyComm | BN254.ScalarField | The commitment to the SchnorrVerKey column of the table |
amountComm | BN254.ScalarField | The commitment to the stake amount column of the stake table |
StateHistoryCommitment
Simplified HotShot commitment struct
struct StateHistoryCommitment {
uint64 l1BlockHeight;
uint64 l1BlockTimestamp;
uint64 hotShotBlockHeight;
BN254.ScalarField hotShotBlockCommRoot;
}
Properties
| Name | Type | Description |
|---|---|---|
l1BlockHeight | uint64 | the block height of l1 when this state update was stored |
l1BlockTimestamp | uint64 | the block timestamp of l1 when this state update was stored |
hotShotBlockHeight | uint64 | The block height of the latest finalized HotShot block |
hotShotBlockCommRoot | BN254.ScalarField | The merkle root of historical block commitments (BN254::ScalarField) |