Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: staking integration #10403

Merged
merged 2 commits into from
Dec 5, 2024
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
2 changes: 2 additions & 0 deletions .github/workflows/devnet-deploys.yml
Original file line number Diff line number Diff line change
Expand Up @@ -547,6 +547,7 @@ jobs:
echo "TF_VAR_INBOX_CONTRACT_ADDRESS=$(extract inboxAddress)" >>$GITHUB_ENV
echo "TF_VAR_OUTBOX_CONTRACT_ADDRESS=$(extract outboxAddress)" >>$GITHUB_ENV
echo "TF_VAR_FEE_JUICE_CONTRACT_ADDRESS=$(extract feeJuiceAddress)" >>$GITHUB_ENV
echo "TF_VAR_STAKING_ASSET_CONTRACT_ADDRESS=$(extract stakingAssetAddress)" >>$GITHUB_ENV
echo "TF_VAR_FEE_JUICE_PORTAL_CONTRACT_ADDRESS=$(extract feeJuicePortalAddress)" >>$GITHUB_ENV

- name: Apply l1-contracts Terraform
Expand Down Expand Up @@ -678,6 +679,7 @@ jobs:
aws s3 cp ${{ env.CONTRACT_S3_BUCKET }}/${{ env.DEPLOY_TAG }}/basic_contracts.json ./basic_contracts.json

echo "TF_VAR_FEE_JUICE_CONTRACT_ADDRESS=$(jq -r '.feeJuiceAddress' ./l1_contracts.json)" >>$GITHUB_ENV
echo "TF_VAR_STAKING_ASSET_CONTRACT_ADDRESS=$(jq -r '.stakingAssetAddress' ./l1_contracts.json)" >>$GITHUB_ENV
echo "TF_VAR_DEV_COIN_CONTRACT_ADDRESS=$(jq -r '.devCoinL1' ./basic_contracts.json)" >>$GITHUB_ENV

- name: Deploy Faucet
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/sepolia-deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ jobs:
echo "TF_VAR_OUTBOX_CONTRACT_ADDRESS=$(extract outboxAddress)" >>$GITHUB_ENV
echo "TF_VAR_AVAILABILITY_ORACLE_CONTRACT_ADDRESS=$(extract availabilityOracleAddress)" >>$GITHUB_ENV
echo "TF_VAR_FEE_JUICE_CONTRACT_ADDRESS=$(extract feeJuiceAddress)" >>$GITHUB_ENV
echo "TF_VAR_STAKING_ASSET_CONTRACT_ADDRESS=$(extract stakingAssetAddress)" >>$GITHUB_ENV
echo "TF_VAR_FEE_JUICE_PORTAL_CONTRACT_ADDRESS=$(extract feeJuicePortalAddress)" >>$GITHUB_ENV

- name: Apply l1-contracts Terraform
Expand Down
155 changes: 70 additions & 85 deletions l1-contracts/src/core/Leonidas.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@ pragma solidity >=0.8.27;
import {ILeonidas, EpochData, LeonidasStorage} from "@aztec/core/interfaces/ILeonidas.sol";
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 {LeonidasLib} from "@aztec/core/libraries/LeonidasLib/LeonidasLib.sol";
import {
Timestamp, Slot, Epoch, SlotLib, EpochLib, TimeFns
} from "@aztec/core/libraries/TimeMath.sol";
import {Ownable} from "@oz/access/Ownable.sol";
import {Staking} from "@aztec/core/staking/Staking.sol";
import {IERC20} from "@oz/token/ERC20/IERC20.sol";
import {EnumerableSet} from "@oz/utils/structs/EnumerableSet.sol";

/**
Expand All @@ -19,16 +21,13 @@ import {EnumerableSet} from "@oz/utils/structs/EnumerableSet.sol";
* He define the structure needed for committee and leader selection and provides logic for validating that
* the block and its "evidence" follows his rules.
*
* @dev Leonidas is depending on Ares to add/remove warriors to/from his army competently.
*
* @dev Leonidas have one thing in mind, he provide a reference of the LOGIC going on for the spartan selection.
* He is not concerned about gas costs, he is a king, he just throw gas in the air like no-one cares.
* It will be the duty of his successor (Pleistarchus) to optimize the costs with same functionality.
*
*/
contract Leonidas is Ownable, TimeFns, ILeonidas {
contract Leonidas is Staking, TimeFns, ILeonidas {
using EnumerableSet for EnumerableSet.AddressSet;
using LeonidasLib for LeonidasStorage;

using SlotLib for Slot;
using EpochLib for Epoch;
Expand All @@ -40,50 +39,22 @@ contract Leonidas is Ownable, TimeFns, ILeonidas {
// The time that the contract was deployed
Timestamp public immutable GENESIS_TIME;

LeonidasStorage private store;
LeonidasStorage private leonidasStore;

constructor(
address _ares,
IERC20 _stakingAsset,
uint256 _minimumStake,
uint256 _slotDuration,
uint256 _epochDuration,
uint256 _targetCommitteeSize
) Ownable(_ares) TimeFns(_slotDuration, _epochDuration) {
) Staking(_ares, _stakingAsset, _minimumStake) TimeFns(_slotDuration, _epochDuration) {
GENESIS_TIME = Timestamp.wrap(block.timestamp);
SLOT_DURATION = _slotDuration;
EPOCH_DURATION = _epochDuration;
TARGET_COMMITTEE_SIZE = _targetCommitteeSize;
}

/**
* @notice Adds a validator to the validator set
*
* @dev Only ARES can add validators
*
* @dev Will setup the epoch if needed BEFORE adding the validator.
* This means that the validator will effectively be added to the NEXT epoch.
*
* @param _validator - The validator to add
*/
function addValidator(address _validator) external override(ILeonidas) onlyOwner {
setupEpoch();
_addValidator(_validator);
}

/**
* @notice Removes a validator from the validator set
*
* @dev Only ARES can add validators
*
* @dev Will setup the epoch if needed BEFORE removing the validator.
* This means that the validator will effectively be removed from the NEXT epoch.
*
* @param _validator - The validator to remove
*/
function removeValidator(address _validator) external override(ILeonidas) onlyOwner {
setupEpoch();
store.validatorSet.remove(_validator);
}

/**
* @notice Get the validator set for a given epoch
*
Expand All @@ -99,26 +70,40 @@ contract Leonidas is Ownable, TimeFns, ILeonidas {
override(ILeonidas)
returns (address[] memory)
{
return store.epochs[_epoch].committee;
return leonidasStore.epochs[_epoch].committee;
}

/**
* @notice Get the validator set for the current epoch
* @return The validator set for the current epoch
*/
function getCurrentEpochCommittee() external view override(ILeonidas) returns (address[] memory) {
return store.getCommitteeAt(getCurrentEpoch(), TARGET_COMMITTEE_SIZE);
return LeonidasLib.getCommitteeAt(
leonidasStore, stakingStore, getCurrentEpoch(), TARGET_COMMITTEE_SIZE
);
}

/**
* @notice Get the validator set
*
* @dev Consider removing this to replace with a `size` and individual getter.
*
* @return The validator set
*/
function getValidators() external view override(ILeonidas) returns (address[] memory) {
return store.validatorSet.values();
function deposit(address _attester, address _proposer, address _withdrawer, uint256 _amount)
public
override(Staking)
{
setupEpoch();
require(
_attester != address(0) && _proposer != address(0),
Errors.Leonidas__InvalidDeposit(_attester, _proposer)
);
super.deposit(_attester, _proposer, _withdrawer, _amount);
}

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);
}

/**
Expand All @@ -127,49 +112,31 @@ contract Leonidas is Ownable, TimeFns, ILeonidas {
* - Set the seed for the epoch
* - Update the last seed
*
* @dev Since this is a reference optimising for simplicity, we store the actual validator set in the epoch structure.
* @dev Since this is a reference optimising for simplicity, we leonidasStore the actual validator set in the epoch structure.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whoops, a search a replace gone wrong. I am inclined to not fix this unless there are other more important stuff, since rerunning the ci tests for a comment will make me yearn for the urn.

* This is very heavy on gas, so start crying because the gas here will melt the poles
* https://i.giphy.com/U1aN4HTfJ2SmgB2BBK.webp
*/
function setupEpoch() public override(ILeonidas) {
Epoch epochNumber = getCurrentEpoch();
EpochData storage epoch = store.epochs[epochNumber];
EpochData storage epoch = leonidasStore.epochs[epochNumber];

if (epoch.sampleSeed == 0) {
epoch.sampleSeed = store.getSampleSeed(epochNumber);
epoch.nextSeed = store.lastSeed = _computeNextSeed(epochNumber);

epoch.committee = store.sampleValidators(epoch.sampleSeed, TARGET_COMMITTEE_SIZE);
epoch.sampleSeed = LeonidasLib.getSampleSeed(leonidasStore, epochNumber);
epoch.nextSeed = leonidasStore.lastSeed = _computeNextSeed(epochNumber);
epoch.committee =
LeonidasLib.sampleValidators(stakingStore, epoch.sampleSeed, TARGET_COMMITTEE_SIZE);
}
}

/**
* @notice Get the number of validators in the validator set
* @notice Get the attester set
*
* @return The number of validators in the validator set
*/
function getValidatorCount() public view override(ILeonidas) returns (uint256) {
return store.validatorSet.length();
}

/**
* @notice Get the number of validators in the validator set
*
* @return The number of validators in the validator set
*/
function getValidatorAt(uint256 _index) public view override(ILeonidas) returns (address) {
return store.validatorSet.at(_index);
}

/**
* @notice Checks if an address is in the validator set
*
* @param _validator - The address to check
* @dev Consider removing this to replace with a `size` and individual getter.
*
* @return True if the address is in the validator set, false otherwise
* @return The validator set
*/
function isValidator(address _validator) public view override(ILeonidas) returns (bool) {
return store.validatorSet.contains(_validator);
function getAttesters() public view override(ILeonidas) returns (address[] memory) {
return stakingStore.attesters.values();
}

/**
Expand Down Expand Up @@ -241,7 +208,9 @@ contract Leonidas is Ownable, TimeFns, ILeonidas {
function getProposerAt(Timestamp _ts) public view override(ILeonidas) returns (address) {
Slot slot = getSlotAt(_ts);
Epoch epochNumber = getEpochAtSlot(slot);
return store.getProposerAt(slot, epochNumber, TARGET_COMMITTEE_SIZE);
return LeonidasLib.getProposerAt(
leonidasStore, stakingStore, slot, epochNumber, TARGET_COMMITTEE_SIZE
);
}

/**
Expand Down Expand Up @@ -277,12 +246,19 @@ contract Leonidas is Ownable, TimeFns, ILeonidas {
return Epoch.wrap(_slotNumber.unwrap() / EPOCH_DURATION);
}

/**
* @notice Adds a validator to the set WITHOUT setting up the epoch
* @param _validator - The validator to add
*/
function _addValidator(address _validator) internal {
store.validatorSet.add(_validator);
// Can be used to add validators without setting up the epoch, useful for the initial set.
function _cheat__Deposit(
address _attester,
address _proposer,
address _withdrawer,
uint256 _amount
) internal {
require(
_attester != address(0) && _proposer != address(0),
Errors.Leonidas__InvalidDeposit(_attester, _proposer)
);

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

/**
Expand All @@ -308,7 +284,16 @@ contract Leonidas is Ownable, TimeFns, ILeonidas {
DataStructures.ExecutionFlags memory _flags
) internal view {
Epoch epochNumber = getEpochAtSlot(_slot);
store.validateLeonidas(_slot, epochNumber, _signatures, _digest, _flags, TARGET_COMMITTEE_SIZE);
LeonidasLib.validateLeonidas(
leonidasStore,
stakingStore,
_slot,
epochNumber,
_signatures,
_digest,
_flags,
TARGET_COMMITTEE_SIZE
);
}

/**
Expand Down
21 changes: 17 additions & 4 deletions l1-contracts/src/core/Rollup.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {IProofCommitmentEscrow} from "@aztec/core/interfaces/IProofCommitmentEsc
import {
IRollup,
ITestRollup,
CheatDepositArgs,
FeeHeader,
ManaBaseFeeComponents,
BlockLog,
Expand Down Expand Up @@ -40,6 +41,7 @@ import {Outbox} from "@aztec/core/messagebridge/Outbox.sol";
import {ProofCommitmentEscrow} from "@aztec/core/ProofCommitmentEscrow.sol";
import {IRewardDistributor} from "@aztec/governance/interfaces/IRewardDistributor.sol";
import {MockVerifier} from "@aztec/mock/MockVerifier.sol";
import {Ownable} from "@oz/access/Ownable.sol";
import {IERC20} from "@oz/token/ERC20/IERC20.sol";
import {EIP712} from "@oz/utils/cryptography/EIP712.sol";
import {Vm} from "forge-std/Vm.sol";
Expand All @@ -49,6 +51,7 @@ struct Config {
uint256 aztecEpochDuration;
uint256 targetCommitteeSize;
uint256 aztecEpochProofClaimWindowInL2Slots;
uint256 minimumStake;
}

/**
Expand All @@ -57,7 +60,7 @@ struct Config {
* @notice Rollup contract that is concerned about readability and velocity of development
* not giving a damn about gas costs.
*/
contract Rollup is EIP712("Aztec Rollup", "1"), Leonidas, IRollup, ITestRollup {
contract Rollup is EIP712("Aztec Rollup", "1"), Ownable, Leonidas, IRollup, ITestRollup {
using SlotLib for Slot;
using EpochLib for Epoch;
using ProposeLib for ProposeArgs;
Expand Down Expand Up @@ -97,14 +100,17 @@ contract Rollup is EIP712("Aztec Rollup", "1"), Leonidas, IRollup, ITestRollup {
constructor(
IFeeJuicePortal _fpcJuicePortal,
IRewardDistributor _rewardDistributor,
IERC20 _stakingAsset,
bytes32 _vkTreeRoot,
bytes32 _protocolContractTreeRoot,
address _ares,
address[] memory _validators,
Config memory _config
)
Ownable(_ares)
Leonidas(
_ares,
_stakingAsset,
_config.minimumStake,
_config.aztecSlotDuration,
_config.aztecEpochDuration,
_config.targetCommitteeSize
Expand Down Expand Up @@ -145,8 +151,15 @@ contract Rollup is EIP712("Aztec Rollup", "1"), Leonidas, IRollup, ITestRollup {
post: L1FeeData({baseFee: block.basefee, blobFee: _getBlobBaseFee()}),
slotOfChange: LIFETIME
});
for (uint256 i = 0; i < _validators.length; i++) {
_addValidator(_validators[i]);
}

function cheat__InitialiseValidatorSet(CheatDepositArgs[] memory _args)
external
override(ITestRollup)
onlyOwner
{
for (uint256 i = 0; i < _args.length; i++) {
_cheat__Deposit(_args[i].attester, _args[i].proposer, _args[i].withdrawer, _args[i].amount);
}
setupEpoch();
}
Expand Down
Loading
Loading