Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
5a0829d
chore: cleanup stakinglib usage slightly
LHerskind May 8, 2025
99d8f5a
feat: initial
LHerskind May 12, 2025
b05ed85
chore: updating tests
LHerskind May 14, 2025
3589f6b
chore: add minimal test for "stake move"
LHerskind May 14, 2025
ff0e98b
chore: renaming things for clarity
LHerskind May 15, 2025
2a02776
chore: update typescript side
LHerskind May 15, 2025
1744e6e
chore: address comments
LHerskind May 20, 2025
d7cbcde
chore: address deployment script issue
LHerskind May 20, 2025
c02bc64
chore: fix lint issues
LHerskind May 20, 2025
ee41021
chore: fix doc preview build
LHerskind May 20, 2025
889dfa3
chore: address maddia comments
LHerskind May 21, 2025
86ea7b4
feat: remove proposer as separate address
LHerskind May 15, 2025
8fc7b11
temp
LHerskind May 20, 2025
24b4b0f
Merge branch 'next' into lh/14287
just-mitch May 28, 2025
21335d2
l1 tests passing
just-mitch May 28, 2025
e7030b6
fix up TS side.
just-mitch May 28, 2025
24f8dde
fix linter
just-mitch May 28, 2025
327a388
fix: EIP712 digest construction in empire base
just-mitch May 29, 2025
2434c89
fix: forwarder test
just-mitch May 29, 2025
b2d70cf
remove owner from forwarder in TS
just-mitch May 29, 2025
7746d10
fix: sequencer publisher test
just-mitch May 29, 2025
9e8d166
fix: solhint ordering
just-mitch May 29, 2025
9ad1f64
fix: slasher client test
just-mitch May 29, 2025
cbee290
fix: linter
just-mitch May 29, 2025
36f183a
fix: linter
just-mitch May 29, 2025
063c356
Merge remote-tracking branch 'origin/next' into lh/14287
just-mitch May 29, 2025
8f82d3a
Merge branch 'next' into lh/14287
just-mitch May 29, 2025
9570af4
fix multi validator test
just-mitch May 29, 2025
db9f7ed
disable flaky tests
just-mitch May 30, 2025
50f0dbc
skip more flaky tests
just-mitch May 30, 2025
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
16 changes: 12 additions & 4 deletions .test_patterns.yml
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,18 @@ tests:
skip: true
owners:
- *lasse
- regex: "src/slasher_client"
skip: true
owners:
- *mitch
- regex: "src/e2e_p2p/validators_sentinel"
skip: true
owners:
- *mitch
- regex: "testbench/port_change.test.ts"
skip: true
owners:
- *sean
# e2e tests flakes
- regex: "src/e2e_p2p"
error_regex: "could not listen on any available address"
Expand Down Expand Up @@ -195,10 +207,6 @@ tests:
error_regex: "✕ should stop after max retry attempts"
owners:
- *sean
- regex: "p2p/src/testbench/port_change.test.ts"
error_regex: "Timeout waiting for worker"
owners:
- *sean
- regex: "yarn-project/kv-store"
error_regex: "Could not import your test module"
owners:
Expand Down
4 changes: 2 additions & 2 deletions l1-contracts/bootstrap.sh
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,7 @@ function validator_costs {
report="gas_benchmark.new.md" # will be overwritten each run

# keep ONLY these functions, in this order
wanted_funcs="forward setupEpoch submitEpochRootProof"
wanted_funcs="propose setupEpoch submitEpochRootProof"

# one label per numeric column (use | to separate)
labels='Min|Avg|Median|Max|# Calls'
Expand Down Expand Up @@ -310,7 +310,7 @@ function validator_costs {
END{
for (k = 1; k <= nf; k++) {
fn = order[k]
div = (fn == "forward" ? 360 : 11520) # change 11520→720 if desired
div = (fn == "propose" ? 360 : 11520) # change 11520→720 if desired

for (j = 1; j <= cols[fn]; j++) {
idx = j + 2
Expand Down
23 changes: 1 addition & 22 deletions l1-contracts/src/core/Rollup.sol
Original file line number Diff line number Diff line change
Expand Up @@ -202,8 +202,7 @@ contract Rollup is IStaking, IValidatorSelection, IRollup, RollupCore {
bytes32 tipArchive = rollupStore.blocks[pendingBlockNumber].archive;
require(tipArchive == _archive, Errors.Rollup__InvalidArchive(tipArchive, _archive));

Epoch epochNumber = slot.epochFromSlot();
address proposer = ValidatorSelectionLib.getProposerAt(slot, epochNumber);
address proposer = ValidatorSelectionLib.getProposerAt(slot, slot.epochFromSlot());
require(
proposer == msg.sender, Errors.ValidatorSelection__InvalidProposer(proposer, msg.sender)
);
Expand Down Expand Up @@ -367,10 +366,6 @@ contract Rollup is IStaking, IValidatorSelection, IRollup, RollupCore {
return STFLib.getStorage().blobPublicInputsHashes[_blockNumber];
}

function getProposerAtIndex(uint256 _index) external view override(IStaking) returns (address) {
return getProposerForAttester(getAttesterAtIndex(_index));
}

function getConfig(address _attester)
external
view
Expand Down Expand Up @@ -645,22 +640,6 @@ contract Rollup is IStaking, IValidatorSelection, IRollup, RollupCore {
return ValidatorSelectionLib.getProposerAt(slot, epochNumber);
}

/**
* @notice Get the proposer for an attester
*
* @param _attester - The attester to get the proposer for
*
* @return The proposer for the attester
*/
function getProposerForAttester(address _attester)
public
view
override(IStaking)
returns (address)
{
return StakingLib.getProposerForAttester(_attester);
}

/**
* @notice Get the attester at an index
*
Expand Down
4 changes: 2 additions & 2 deletions l1-contracts/src/core/RollupCore.sol
Original file line number Diff line number Diff line change
Expand Up @@ -178,11 +178,11 @@ contract RollupCore is
return RewardLib.claimProverRewards(_recipient, _epochs);
}

function deposit(address _attester, address _proposer, address _withdrawer, bool _onCanonical)
function deposit(address _attester, address _withdrawer, bool _onCanonical)
external
override(IStakingCore)
{
StakingLib.deposit(_attester, _proposer, _withdrawer, _onCanonical);
StakingLib.deposit(_attester, _withdrawer, _onCanonical);
}

function initiateWithdraw(address _attester, address _recipient)
Expand Down
9 changes: 2 additions & 7 deletions l1-contracts/src/core/interfaces/IStaking.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,13 @@ import {IERC20} from "@oz/token/ERC20/IERC20.sol";

interface IStakingCore {
event SlasherUpdated(address indexed oldSlasher, address indexed newSlasher);
event Deposit(
address indexed attester, address indexed proposer, address indexed withdrawer, uint256 amount
);
event Deposit(address indexed attester, address indexed withdrawer, uint256 amount);
event WithdrawInitiated(address indexed attester, address indexed recipient, uint256 amount);
event WithdrawFinalised(address indexed attester, address indexed recipient, uint256 amount);
event Slashed(address indexed attester, uint256 amount);

function setSlasher(address _slasher) external;
function deposit(address _attester, address _proposer, address _withdrawer, bool _onCanonical)
external;
function deposit(address _attester, address _withdrawer, bool _onCanonical) external;
function initiateWithdraw(address _attester, address _recipient) external returns (bool);
function finaliseWithdraw(address _attester) external;
function slash(address _attester, uint256 _amount) external;
Expand All @@ -29,8 +26,6 @@ interface IStaking is IStakingCore {
function getExit(address _attester) external view returns (Exit memory);
function getActiveAttesterCount() external view returns (uint256);
function getAttesterAtIndex(uint256 _index) external view returns (address);
function getProposerAtIndex(uint256 _index) external view returns (address);
function getProposerForAttester(address _attester) external view returns (address);
function getSlasher() external view returns (address);
function getStakingAsset() external view returns (IERC20);
function getMinimumStake() external view returns (uint256);
Expand Down
5 changes: 2 additions & 3 deletions l1-contracts/src/core/libraries/Errors.sol
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,8 @@ library Errors {
error ValidatorSelection__InvalidProposer(address expected, address actual); // 0xa8843a68
error ValidatorSelection__InvalidDeposit(address attester, address proposer); // 0x533169bd
error ValidatorSelection__InsufficientAttestations(uint256 minimumNeeded, uint256 provided); // 0xaf47297f
error ValidatorSelection__InvalidCommitteeCommitment(bytes32 reconstructed, bytes32 expected); // 0x10816cae
error ValidatorSelection__InvalidAttestationsLength(uint256 expected, uint256 actual);
error ValidatorSelection__InvalidCommitteeCommitment(bytes32 reconstructed, bytes32 expected); // 0xca8d5954
error ValidatorSelection__InvalidAttestationsLength(uint256 expected, uint256 actual); // 0xe923198c

// Staking
error Staking__AlreadyActive(address attester); // 0x5e206fa4
Expand All @@ -124,7 +124,6 @@ library Errors {
error Staking__RollupAlreadyRegistered(address); // 0x108a39c8
error Staking__InvalidRollupAddress(address); // 0xd876720e
error Staking__NotCanonical(address); // 0x6244212e
error Staking__InvalidProposer();

// Fee Juice Portal
error FeeJuicePortal__AlreadyInitialized(); // 0xc7a172fe
Expand Down
11 changes: 10 additions & 1 deletion l1-contracts/src/core/libraries/crypto/SignatureLib.sol
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,21 @@ library SignatureLib {
* @param _signer - The expected signer of the signature
* @param _digest - The digest that was signed
*/
function verify(Signature memory _signature, address _signer, bytes32 _digest) internal pure {
function verify(Signature memory _signature, address _signer, bytes32 _digest)
internal
pure
returns (bool)
{
address recovered = ecrecover(_digest, _signature.v, _signature.r, _signature.s);
require(_signer == recovered, Errors.SignatureLib__InvalidSignature(_signer, recovered));
return true;
}

function toBytes(Signature memory _signature) internal pure returns (bytes memory) {
return abi.encodePacked(_signature.r, _signature.s, _signature.v);
}

function isEmpty(Signature memory _signature) internal pure returns (bool) {
return _signature.v == 0;
}
}
15 changes: 4 additions & 11 deletions l1-contracts/src/core/libraries/staking/StakingLib.sol
Original file line number Diff line number Diff line change
Expand Up @@ -131,12 +131,10 @@ library StakingLib {
emit IStakingCore.Slashed(_attester, _amount);
}

function deposit(address _attester, address _proposer, address _withdrawer, bool _onCanonical)
internal
{
function deposit(address _attester, address _withdrawer, bool _onCanonical) internal {
require(
_attester != address(0) && _proposer != address(0),
Errors.Staking__InvalidDeposit(_attester, _proposer)
_attester != address(0) && _withdrawer != address(0),
Errors.Staking__InvalidDeposit(_attester, _withdrawer)
);
StakingStorage storage store = getStorage();
require(!store.exits[_attester].exists, Errors.Staking__AlreadyRegistered(_attester));
Expand All @@ -147,7 +145,7 @@ library StakingLib {

store.stakingAsset.transferFrom(msg.sender, address(this), amount);
store.stakingAsset.approve(address(store.gse), amount);
store.gse.deposit(_attester, _proposer, _withdrawer, _onCanonical);
store.gse.deposit(_attester, _withdrawer, _onCanonical);
}

function initiateWithdraw(address _attester, address _recipient) internal returns (bool) {
Expand Down Expand Up @@ -204,11 +202,6 @@ library StakingLib {
);
}

function getProposerForAttester(address _attester) internal view returns (address) {
(address proposer,,) = getStorage().gse.getProposer(address(this), _attester);
return proposer;
}

function getAttestersFromIndicesAtTime(Timestamp _timestamp, uint256[] memory _indices)
internal
view
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,34 +101,29 @@ library ValidatorSelectionLib {
return;
}

if (_flags.ignoreSignatures) {
return;
}

require(
_attestations.length == committeeSize,
Errors.ValidatorSelection__InvalidAttestationsLength(committeeSize, _attestations.length)
);

// We determine who the proposer from indexing into the provided attestations array, we then recover their proposer
// address from storage
uint256 proposerIndex =
computeProposerIndex(_epochNumber, _slot, getSampleSeed(_epochNumber), committeeSize);

// We determine who the proposer from indexing into the provided attestations array, we then recover their proposer
// address from storage
// The user controls this value, however, if a false value is provided, the recalculated committee commitment will
// be incorrect, and we will revert.
address attester = _attestations[proposerIndex].addr;
address proposer = StakingLib.getProposerForAttester(attester);

require(
proposer == msg.sender, Errors.ValidatorSelection__InvalidProposer(proposer, msg.sender)
);

if (_flags.ignoreSignatures) {
return;
}

// Validate the attestations
uint256 needed = committeeSize * 2 / 3 + 1;
uint256 validAttestations = 0;

address[] memory reconstructedCommittee = new address[](committeeSize);
bool proposerVerified = false;

bytes32 digest = _digest.toEthSignedMessageHash();
for (uint256 i = 0; i < _attestations.length; i++) {
Expand All @@ -141,11 +136,21 @@ library ValidatorSelectionLib {
);
reconstructedCommittee[i] = recovered;
validAttestations++;
if (i == proposerIndex) {
proposerVerified = true;
}
} else {
reconstructedCommittee[i] = attestation.addr;
}
}

address proposer = reconstructedCommittee[proposerIndex];

require(
proposerVerified || proposer == msg.sender,
Errors.ValidatorSelection__InvalidProposer(proposer, msg.sender)
);

require(
validAttestations >= needed,
Errors.ValidatorSelection__InsufficientAttestations(needed, validAttestations)
Expand All @@ -171,10 +176,7 @@ library ValidatorSelectionLib {
return address(0);
}

address attester =
committee[computeProposerIndex(_epochNumber, _slot, sampleSeed, committee.length)];

return StakingLib.getProposerForAttester(attester);
return committee[computeProposerIndex(_epochNumber, _slot, sampleSeed, committee.length)];
}

/**
Expand Down
22 changes: 7 additions & 15 deletions l1-contracts/src/core/staking/GSE.sol
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import {Checkpoints} from "@oz/utils/structs/Checkpoints.sol";

struct AttesterConfig {
address withdrawer;
address proposer;
}

struct InstanceStaking {
Expand All @@ -27,13 +26,10 @@ struct InstanceStaking {
}

interface IGSE {
event Deposit(
address indexed instance, address indexed attester, address proposer, address withdrawer
);
event Deposit(address indexed instance, address indexed attester, address withdrawer);

function addRollup(address _rollup) external;
function deposit(address _attester, address _proposer, address _withdrawer, bool _onCanonical)
external;
function deposit(address _attester, address _withdrawer, bool _onCanonical) external;
function withdraw(address _attester, uint256 _amount) external returns (uint256, bool);

function isRegistered(address _instance, address _attester) external view returns (bool);
Expand Down Expand Up @@ -125,12 +121,11 @@ contract GSE is IGSE, Ownable {
* @dev Deposits only allowed to and by listed rollups.
*
* @param _attester - The attester address of the validator
* @param _proposer - The proposer address of the validator
* @param _withdrawer - The withdrawer address of the validator
* @param _onCanonical - Whether to deposit into the specific instance, or canonical
* @dev Must be the current canonical for `_onCanonical = true` to be valid.
*/
function deposit(address _attester, address _proposer, address _withdrawer, bool _onCanonical)
function deposit(address _attester, address _withdrawer, bool _onCanonical)
external
override(IGSE)
onlyRollup
Expand All @@ -143,17 +138,14 @@ contract GSE is IGSE, Ownable {
InstanceStaking storage instanceStaking =
instances[_onCanonical ? CANONICAL_MAGIC_ADDRESS : msg.sender];
require(instanceStaking.attesters.add(_attester), Errors.Staking__AlreadyRegistered(_attester));
instanceStaking.configOf[_attester] =
AttesterConfig({withdrawer: _withdrawer, proposer: _proposer});
instanceStaking.configOf[_attester] = AttesterConfig({withdrawer: _withdrawer});
instanceStaking.balanceOf[_attester] += MINIMUM_DEPOSIT;
instanceStaking.supply += MINIMUM_DEPOSIT;
totalSupply += MINIMUM_DEPOSIT;

STAKING_ASSET.transferFrom(msg.sender, address(this), MINIMUM_DEPOSIT);

emit Deposit(
_onCanonical ? CANONICAL_MAGIC_ADDRESS : msg.sender, _attester, _proposer, _withdrawer
);
emit Deposit(_onCanonical ? CANONICAL_MAGIC_ADDRESS : msg.sender, _attester, _withdrawer);
}

/**
Expand Down Expand Up @@ -232,7 +224,7 @@ contract GSE is IGSE, Ownable {
_getInstanceStoreWithAttester(_instance, _attester);

if (!attesterExists) {
return AttesterConfig({withdrawer: address(0), proposer: address(0)});
return AttesterConfig({withdrawer: address(0)});
}

return instanceStaking.configOf[_attester];
Expand Down Expand Up @@ -269,7 +261,7 @@ contract GSE is IGSE, Ownable {
return (address(0), false, false);
}

return (instanceStaking.configOf[_attester].proposer, true, isCanonical);
return (_attester, true, isCanonical);
}

function balanceOf(address _instance, address _attester)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2024 Aztec Labs.
// solhint-disable imports-order
pragma solidity >=0.8.27;

import {Slot} from "@aztec/core/libraries/TimeLib.sol";
import {IPayload} from "@aztec/governance/interfaces/IPayload.sol";
import {Signature} from "@aztec/core/libraries/crypto/SignatureLib.sol";

interface IGovernanceProposer {
event VoteCast(IPayload indexed proposal, uint256 indexed round, address indexed voter);
event ProposalExecutable(IPayload indexed proposal, uint256 indexed round);
event ProposalExecuted(IPayload indexed proposal, uint256 indexed round);

function vote(IPayload _proposal) external returns (bool);
function voteWithSig(IPayload _proposal, Signature memory _sig) external returns (bool);

function executeProposal(uint256 _roundNumber) external returns (bool);
function yeaCount(address _instance, uint256 _round, IPayload _proposal)
external
Expand Down
Loading