Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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