Requirements: Stake Table Contract Requirements
Contents |
The Espresso stake table smart contract is an EVM smart contract deployed to Ethereum layer 1 (L1) and it’s purpose is to securely facilitate permissionless staking for the GCL. To that extent it will expose public functions for validator registration and exits, delegation deposit and withdrawals (with an escrow period) of the staking asset (ESP Token), as well as emit EVM events. The GCL can use these EVM events to build a stake table at any finalized Ethereum block height. The GCL only defines a stake table per epoch and handles the mapping of epochs to finalized L1 block number. The contract functions will be called by staking user interfaces.
The participants relevant to the smart contract are the GCL and
A GCL node that participates in consensus, operated by a GCL node operator.
An ESP token holder who would like to stake ESP tokens to participate in securing the GCL. The entitity behind a delegator may also be a validator.
A graphical or command line interface staking user interface (UI) that allows validators and delegators to participate in the staking protocol.
An L1 account (an externally owned account (EOA) or multisig smart contract).
The stake table contract supports the following operations
Register a validator.
De-register a validator.
Update consensus verifying keys of a validator.
Delegate to a validator.
Undelegate from a validator.
Withdraw funds.
In addition to that the following methods exist that can only be invoked by the contract admin:
Set the escrow period.
In summary the stake table needs implement the following functionality
Safely hold staked assets until it is safe to release them.
Emit EVM for all changes to the stake table that the GCL needs to know about.
Associate delegations with delegators and validators in order for the stake table contract to
return withdrawn delegations to original delegator
slash delegations if a validator commits a slashable offence
In other words the stake table serves as a timestamped (by ethereum block number) ledger for stake table events with some restrictions, notably an escrow period for exiting. The stake table contract does not itself compute a stake table but merely ensures that the flow of funds and respective information satisfy the requirements of validators, delegators and the GCL.
The GCL will receive the following events, and associated data
Validator registration: validator Ethereum address, BLS verifying key, Schnorr verifying key, commission.
Validator de-registration (or exit): Ethereum address
Delegation: validator Ethereum address, delegator Ethereum address, delegation amount
Undelegation: validator Ethereum address, delegator Ethereum address, undelegation amount
Update of consensus keys: validator Ethereum address, new BLS verifying key, new Schnorr verifying key
The BLS key is used as the consensus signing key and the Schnorr key is used to sign GCL state update when building a light client proof. The commission is the percentage of fees (with 2 decimal points) that the validator will earn from delegated funds.
The GCL’s interface with the stake table smart contract is via EVM events. EVM events are emitted when an EVM change (here the L1) executes EVM transactions. By “emitted” we mean that event associated with the transaction in the EVM block and part of the block. Every EVM transaction can have zero or more EVM events associated with it. EVM events can have arbitry data attached to them. EVM events are persistent, in the sense that if the execution of a transaction emitted a certain event we expect that event and its associated data to be forever part of the history of the L1 and obtainable with the eth_getLogs RPC call to an Ethereum JsonRPC node. In practice we do not call the Ethereum JsonRPC directly but use the rust code generated by foundry for this purpose (the “contract bindings”).
If an EVM transaction fails to execute it is said “to revert”. The revert causes the transaction to not make any changes to the state (other than consume gas for the execution until the revert) and to not emit any EVM events. Importantly, if a transaction fails to execute there will be no EVM events emitted in this transaction. When we say the contract prevents something from happening we mean that it causes a transaction that violates this constraint to revert, which means that no events will be emitted.
For the GCL the following information about the behaviour of the stake table contract is of interest
Only one validator can register with the same BLS key or Ethereum address, but the contract does not verify ownership of the Schnorr key.
The contract verifies ownership of the BLS key during validator registration and update of consensus keys. There will be no ValidatorRegistered event if the ownership verification fails.
The contract verifies that during the consensus keys update either the BLS key or the Schnorr key were in fact changed.
The contract prevents a de-registration from a validator that does not exist.
The contract prevents a Validator from de-registering twice.
Due to restrictions in the EVM, the contract emits a ‘ValidatorDeregistered‘ with only the validators address when a validator deregisters. The GCL needs to ensure that this event initiates the removal of the validator and its delegated funds from the stake table.
The contract always transfers staked funds to itself during delegation. There will be no Delegation event if the transfer is not successful.
The contract prevents an Undelegation if the delegator does not have sufficient funds delegated. There will be no ‘Undelegation‘ event otherwise.
The contract prevents delegations to non-registered or de-registered validators.
The GCL implementation of the stake table must not assume that stake table smart contract implements all requirements correctly. However, the GCL should not attempt to handle bugs in the smart contract. If the smart contract does not satisfy its requirements the behaviour of the GCL is not always well defined. If for example we receive a registration event for a validator that is already registered this is a serious bug in the smart contract and the GCL can panic.
For certain operations we will trust the smart contract to handle it correctly. For example the GCL will not verify at runtime that tokens were actually transferred to the smart contract. However as part of the requirements for the stake table smart contracts we will implement tests that ensure the smart contract correctly handles the requirements.
The GCL needs to handle the contract events as follows.
Validator registration: store address, keys and commission.
Validator de-registration (or exit): remove all funds delegated to the validator. The validators weight is now zero.
Delegation: increase weight of validator and delegation by the delegation amount.
Undelegation: decrease weight of validator and delegation by the undelegation amount.
Update of consensus keys: modify the validators consensus keys.
The GCL has some flexibility on how go about the implementation but there are some constraints
We must ensure the GCL only ever processes L1 events from finalized blocks. Otherwise, the events processed my be changed during a L1 reorg. Unfortunately the Ethereum JsonRPC does not provide a convenient way to subscribe to only finalized events and we have some custom code to work around this limitation.
We must ensure we always include exactly the stake table L1 events with block number up to and including the finalized block number referenced in the header received by the add_epoch_root function.
The events must be processed in the correct order in which the appear in the L1 chain.
Since the contract does not enforce a limit of registered validators the GCL is responsible for choosing N validators with the largest delegated amount as the active validator set. N will initially be set to 100, but may be increased later.
We must not be careless regarding the amount of JsonRPC calls made. We use Infura and Alchemy as JsonRPC providers and calls to the RPCs are metered. We may incur high costs if we make too many unnecessary calls.
Assume that the internal stake table represention in memory is as follows in the GCL.
Define the stake_table as an ordered mapping of validators: validator address => Validator
A Validator has fields
validator address
BLS verifying key
Schnorr verifying key
total delegated stake amount
delegators: ordered mapping delegator address => delegation amount
assume that we have a stake table for epoch e. If we are upgrading from the static stake table to PoS the mapping may be empty.
case Registration: insert a validator, with total stake amount zero
case Deregistration: remove the validator
case Delegation: increase total delegated stake amount of the validator and delegation amount of the delegator
case Undelegation: decrease total delegated stake amount of the delegator and delegation amount of the delegator
The consensus algorithm and the Membership implementation do not need delegation information. The GCL can implement a way to convert the GCL internal representation of the stake table (which includes delegations and is used for rewards computation in the GCL) to the Membership implementation. Alternatively the GCL could opt for a bigger implementation of the Membership trait that serves also as internal GCL type.
While the algorithm outline above should be correct, we do not want to fetch all stake table events each time add_epoch_root is called because this would be slow and expensive. Therefore as an optimization we should at minimum fetch finalized stake table events in a separate thread. We should use those pre-fetched events when add_epoch_root is called. As a second optimization we should compute the stake table of the new epoch by applying the new events to the stake table of the previous epoch.
Prior to performing the proof of stake upgrade the following will happen on L1.
The ESP token will deployed and the Token Generation Event will happen.
The stake table contract will be deployed. This will make the L1 contract address and the L1 block number when the stake table contract was deployed known.
Validators will register in the stake table contract.
Delegators will delegate to validators in the stake table contract.
The process of initial validator registration and delegation may take some time and we will not carry out the Proof of Stake (POS) upgrade until we are happy with the state of the stake table contract on L1.
When the POS upgrade is activated in the GCL consensus will start calling add_epoch_root to build the stake table from L1 events in the stake table contract. Two epochs later consensus will start using the new stake table for voting but will keep using the existing data availability (DA) stake table.