Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

LightClient

Git Source

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

NameTypeDescription
_genesisLightClientStateThe initial state of the light client
_genesisStakeTableStateStakeTableState
_stateHistoryRetentionPerioduint32The maximum retention period (in seconds) for the state history. the min retention period allowed is 1 hour and max 365 days
owneraddressThe 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

NameTypeDescription
majorVersionuint8The major version of the contract
minorVersionuint8The minor version of the contract
patchVersionuint8The 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

NameTypeDescription
_genesisLightClientStateThe initial state of the light client
_genesisStakeTableStateStakeTableStateThe initial stake table state of the light client
_stateHistoryRetentionPerioduint32The 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

NameTypeDescription
newStateLightClientStatenew light client state
proofIPlonkVerifier.PlonkProofPlonkProof

_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

NameTypeDescription
blockNumberuint64The block number of the new finalized state.
blockTimestampuint64The block timestamp used to check the retention period.
stateLightClientStateThe 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

NameTypeDescription
blockNumberuint256The block number to compare against the latest state updates.
blockThresholduint256The number of blocks updates this contract is allowed to lag behind.

Returns

NameTypeDescription
<none>boolbool 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

NameTypeDescription
hotShotBlockHeightuint256the HotShot block height

Returns

NameTypeDescription
hotShotBlockCommRootBN254.ScalarFieldthe HotShot commitment root
hotshotBlockHeightuint64the HotShot block height for the corresponding commitment root

getStateHistoryCount

get the number of state history commitments

function getStateHistoryCount() public view returns (uint256);

Returns

NameTypeDescription
<none>uint256uint256 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

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

NameTypeDescription
viewNumuint64The latest view number of the finalized HotShot chain
blockHeightuint64The block height of the latest finalized block
blockCommRootBN254.ScalarFieldThe 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

NameTypeDescription
thresholduint256The (stake-weighted) quorum threshold for a QC to be considered as valid
blsKeyCommBN254.ScalarFieldThe commitment to the BlsVerKey column of the stake table
schnorrKeyCommBN254.ScalarFieldThe commitment to the SchnorrVerKey column of the table
amountCommBN254.ScalarFieldThe 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

NameTypeDescription
l1BlockHeightuint64the block height of l1 when this state update was stored
l1BlockTimestampuint64the block timestamp of l1 when this state update was stored
hotShotBlockHeightuint64The block height of the latest finalized HotShot block
hotShotBlockCommRootBN254.ScalarFieldThe merkle root of historical block commitments (BN254::ScalarField)