Skip to content

Commit

Permalink
feat!: staking library (#11559)
Browse files Browse the repository at this point in the history
Fixes #11519. Minor oddities can be encountered, if the events are in
the library, they won't be part of the rollup contract since the library
is external. Therefore events and structs are kept in the interface for
simple access.
  • Loading branch information
LHerskind authored Feb 4, 2025
1 parent 5e9f12e commit c050320
Show file tree
Hide file tree
Showing 20 changed files with 392 additions and 266 deletions.
142 changes: 108 additions & 34 deletions l1-contracts/src/core/ValidatorSelection.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Copyright 2024 Aztec Labs.
pragma solidity >=0.8.27;

import {IStaking, ValidatorInfo, Exit, OperatorInfo} from "@aztec/core/interfaces/IStaking.sol";
import {
IValidatorSelection,
EpochData,
Expand All @@ -10,13 +11,13 @@ import {
import {Signature} from "@aztec/core/libraries/crypto/SignatureLib.sol";
import {DataStructures} from "@aztec/core/libraries/DataStructures.sol";
import {Errors} from "@aztec/core/libraries/Errors.sol";
import {StakingLib} from "@aztec/core/libraries/staking/StakingLib.sol";
import {
Timestamp, Slot, Epoch, SlotLib, EpochLib, TimeLib
} from "@aztec/core/libraries/TimeLib.sol";
import {ValidatorSelectionLib} from
"@aztec/core/libraries/ValidatorSelectionLib/ValidatorSelectionLib.sol";
import {Staking} from "@aztec/core/staking/Staking.sol";

import {Slasher} from "@aztec/core/staking/Slasher.sol";
import {IERC20} from "@oz/token/ERC20/IERC20.sol";
import {EnumerableSet} from "@oz/utils/structs/EnumerableSet.sol";

Expand All @@ -27,7 +28,7 @@ import {EnumerableSet} from "@oz/utils/structs/EnumerableSet.sol";
* It is a reference implementation, it is not optimized for gas.
*
*/
contract ValidatorSelection is Staking, IValidatorSelection {
contract ValidatorSelection is IValidatorSelection, IStaking {
using EnumerableSet for EnumerableSet.AddressSet;

using SlotLib for Slot;
Expand All @@ -50,10 +51,45 @@ contract ValidatorSelection is Staking, IValidatorSelection {
uint256 _slotDuration,
uint256 _epochDuration,
uint256 _targetCommitteeSize
) Staking(_stakingAsset, _minimumStake, _slashingQuorum, _roundSize) {
) {
TARGET_COMMITTEE_SIZE = _targetCommitteeSize;

TimeLib.initialize(block.timestamp, _slotDuration, _epochDuration);

Timestamp exitDelay = Timestamp.wrap(60 * 60 * 24);
Slasher slasher = new Slasher(_slashingQuorum, _roundSize);
StakingLib.initialize(_stakingAsset, _minimumStake, exitDelay, address(slasher));
}

function deposit(address _attester, address _proposer, address _withdrawer, uint256 _amount)
external
override(IStaking)
{
setupEpoch();
require(
_attester != address(0) && _proposer != address(0),
Errors.ValidatorSelection__InvalidDeposit(_attester, _proposer)
);
StakingLib.deposit(_attester, _proposer, _withdrawer, _amount);
}

function initiateWithdraw(address _attester, address _recipient)
external
override(IStaking)
returns (bool)
{
// @note The attester might be chosen for the epoch, so the delay must be long enough
// to allow for that.
setupEpoch();
return StakingLib.initiateWithdraw(_attester, _recipient);
}

function finaliseWithdraw(address _attester) external override(IStaking) {
StakingLib.finaliseWithdraw(_attester);
}

function slash(address _attester, uint256 _amount) external override(IStaking) {
StakingLib.slash(_attester, _amount);
}

function getGenesisTime() external view override(IValidatorSelection) returns (Timestamp) {
Expand All @@ -68,6 +104,67 @@ contract ValidatorSelection is Staking, IValidatorSelection {
return TimeLib.getStorage().epochDuration;
}

function getSlasher() external view override(IStaking) returns (address) {
return StakingLib.getStorage().slasher;
}

function getStakingAsset() external view override(IStaking) returns (IERC20) {
return StakingLib.getStorage().stakingAsset;
}

function getMinimumStake() external view override(IStaking) returns (uint256) {
return StakingLib.getStorage().minimumStake;
}

function getExitDelay() external view override(IStaking) returns (Timestamp) {
return StakingLib.getStorage().exitDelay;
}

function getActiveAttesterCount() external view override(IStaking) returns (uint256) {
return StakingLib.getStorage().attesters.length();
}

function getProposerForAttester(address _attester)
external
view
override(IStaking)
returns (address)
{
return StakingLib.getStorage().info[_attester].proposer;
}

function getAttesterAtIndex(uint256 _index) external view override(IStaking) returns (address) {
return StakingLib.getStorage().attesters.at(_index);
}

function getProposerAtIndex(uint256 _index) external view override(IStaking) returns (address) {
return StakingLib.getStorage().info[StakingLib.getStorage().attesters.at(_index)].proposer;
}

function getInfo(address _attester)
external
view
override(IStaking)
returns (ValidatorInfo memory)
{
return StakingLib.getStorage().info[_attester];
}

function getExit(address _attester) external view override(IStaking) returns (Exit memory) {
return StakingLib.getStorage().exits[_attester];
}

function getOperatorAtIndex(uint256 _index)
external
view
override(IStaking)
returns (OperatorInfo memory)
{
address attester = StakingLib.getStorage().attesters.at(_index);
return
OperatorInfo({proposer: StakingLib.getStorage().info[attester].proposer, attester: attester});
}

/**
* @notice Get the validator set for a given epoch
*
Expand Down Expand Up @@ -97,7 +194,7 @@ contract ValidatorSelection is Staking, IValidatorSelection {
returns (address[] memory)
{
return ValidatorSelectionLib.getCommitteeAt(
validatorSelectionStore, stakingStore, getCurrentEpoch(), TARGET_COMMITTEE_SIZE
validatorSelectionStore, StakingLib.getStorage(), getCurrentEpoch(), TARGET_COMMITTEE_SIZE
);
}

Expand All @@ -115,7 +212,7 @@ contract ValidatorSelection is Staking, IValidatorSelection {
returns (address[] memory)
{
return ValidatorSelectionLib.getCommitteeAt(
validatorSelectionStore, stakingStore, getEpochAt(_ts), TARGET_COMMITTEE_SIZE
validatorSelectionStore, StakingLib.getStorage(), getEpochAt(_ts), TARGET_COMMITTEE_SIZE
);
}

Expand Down Expand Up @@ -144,29 +241,6 @@ contract ValidatorSelection is Staking, IValidatorSelection {
return ValidatorSelectionLib.getSampleSeed(validatorSelectionStore, getCurrentEpoch());
}

function initiateWithdraw(address _attester, address _recipient)
public
override(Staking)
returns (bool)
{
// @note The attester might be chosen for the epoch, so the delay must be long enough
// to allow for that.
setupEpoch();
return super.initiateWithdraw(_attester, _recipient);
}

function deposit(address _attester, address _proposer, address _withdrawer, uint256 _amount)
public
override(Staking)
{
setupEpoch();
require(
_attester != address(0) && _proposer != address(0),
Errors.ValidatorSelection__InvalidDeposit(_attester, _proposer)
);
super.deposit(_attester, _proposer, _withdrawer, _amount);
}

/**
* @notice Performs a setup of an epoch if needed. The setup will
* - Sample the validator set for the epoch
Expand All @@ -185,7 +259,7 @@ contract ValidatorSelection is Staking, IValidatorSelection {
epoch.sampleSeed = ValidatorSelectionLib.getSampleSeed(validatorSelectionStore, epochNumber);
epoch.nextSeed = validatorSelectionStore.lastSeed = _computeNextSeed(epochNumber);
epoch.committee = ValidatorSelectionLib.sampleValidators(
stakingStore, epoch.sampleSeed, TARGET_COMMITTEE_SIZE
StakingLib.getStorage(), epoch.sampleSeed, TARGET_COMMITTEE_SIZE
);
}
}
Expand All @@ -198,7 +272,7 @@ contract ValidatorSelection is Staking, IValidatorSelection {
* @return The validator set
*/
function getAttesters() public view override(IValidatorSelection) returns (address[] memory) {
return stakingStore.attesters.values();
return StakingLib.getStorage().attesters.values();
}

/**
Expand Down Expand Up @@ -271,7 +345,7 @@ contract ValidatorSelection is Staking, IValidatorSelection {
Slot slot = getSlotAt(_ts);
Epoch epochNumber = getEpochAtSlot(slot);
return ValidatorSelectionLib.getProposerAt(
validatorSelectionStore, stakingStore, slot, epochNumber, TARGET_COMMITTEE_SIZE
validatorSelectionStore, StakingLib.getStorage(), slot, epochNumber, TARGET_COMMITTEE_SIZE
);
}

Expand Down Expand Up @@ -325,7 +399,7 @@ contract ValidatorSelection is Staking, IValidatorSelection {
Errors.ValidatorSelection__InvalidDeposit(_attester, _proposer)
);

super.deposit(_attester, _proposer, _withdrawer, _amount);
StakingLib.deposit(_attester, _proposer, _withdrawer, _amount);
}

/**
Expand Down Expand Up @@ -353,7 +427,7 @@ contract ValidatorSelection is Staking, IValidatorSelection {
Epoch epochNumber = getEpochAtSlot(_slot);
ValidatorSelectionLib.validateValidatorSelection(
validatorSelectionStore,
stakingStore,
StakingLib.getStorage(),
_slot,
epochNumber,
_signatures,
Expand Down
11 changes: 10 additions & 1 deletion l1-contracts/src/core/interfaces/IStaking.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
// Copyright 2024 Aztec Labs.
pragma solidity >=0.8.27;

import {Timestamp} from "@aztec/core/libraries/TimeLib.sol";
import {Timestamp} from "@aztec/core/libraries/TimeMath.sol";
import {IERC20} from "@oz/token/ERC20/IERC20.sol";
import {EnumerableSet} from "@oz/utils/structs/EnumerableSet.sol";

// None -> Does not exist in our setup
Expand Down Expand Up @@ -35,6 +36,10 @@ struct Exit {
}

struct StakingStorage {
IERC20 stakingAsset;
address slasher;
uint256 minimumStake;
Timestamp exitDelay;
EnumerableSet.AddressSet attesters;
mapping(address attester => ValidatorInfo) info;
mapping(address attester => Exit) exits;
Expand All @@ -61,4 +66,8 @@ interface IStaking {
function getProposerAtIndex(uint256 _index) external view returns (address);
function getProposerForAttester(address _attester) external view returns (address);
function getOperatorAtIndex(uint256 _index) external view returns (OperatorInfo memory);
function getSlasher() external view returns (address);
function getStakingAsset() external view returns (IERC20);
function getMinimumStake() external view returns (uint256);
function getExitDelay() external view returns (Timestamp);
}
Loading

0 comments on commit c050320

Please sign in to comment.