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

chore: add test that slasher actually stores events #10846

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
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
8 changes: 6 additions & 2 deletions l1-contracts/src/core/Leonidas.sol
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,17 @@ contract Leonidas is Staking, TimeFns, ILeonidas {
LeonidasStorage private leonidasStore;

constructor(
address _ares,
IERC20 _stakingAsset,
uint256 _minimumStake,
uint256 _slashingQuorum,
uint256 _roundSize,
uint256 _slotDuration,
uint256 _epochDuration,
uint256 _targetCommitteeSize
) Staking(_ares, _stakingAsset, _minimumStake) TimeFns(_slotDuration, _epochDuration) {
)
Staking(_stakingAsset, _minimumStake, _slashingQuorum, _roundSize)
TimeFns(_slotDuration, _epochDuration)
{
GENESIS_TIME = Timestamp.wrap(block.timestamp);
SLOT_DURATION = _slotDuration;
EPOCH_DURATION = _epochDuration;
Expand Down
12 changes: 8 additions & 4 deletions l1-contracts/src/core/Rollup.sol
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ struct Config {
uint256 targetCommitteeSize;
uint256 aztecEpochProofClaimWindowInL2Slots;
uint256 minimumStake;
uint256 slashingQuorum;
uint256 slashingRoundSize;
}

/**
Expand Down Expand Up @@ -110,15 +112,15 @@ contract Rollup is EIP712("Aztec Rollup", "1"), Ownable, Leonidas, IRollup, ITes
)
Ownable(_ares)
Leonidas(
_ares,
_stakingAsset,
_config.minimumStake,
_config.slashingQuorum,
_config.slashingRoundSize,
_config.aztecSlotDuration,
_config.aztecEpochDuration,
_config.targetCommitteeSize
)
{
rollupStore.epochProofVerifier = new MockVerifier();
FEE_JUICE_PORTAL = _fpcJuicePortal;
REWARD_DISTRIBUTOR = _rewardDistributor;
ASSET = _fpcJuicePortal.UNDERLYING();
Expand All @@ -127,14 +129,16 @@ contract Rollup is EIP712("Aztec Rollup", "1"), Ownable, Leonidas, IRollup, ITes
);
INBOX = IInbox(address(new Inbox(address(this), Constants.L1_TO_L2_MSG_SUBTREE_HEIGHT)));
OUTBOX = IOutbox(address(new Outbox(address(this))));
rollupStore.vkTreeRoot = _vkTreeRoot;
rollupStore.protocolContractTreeRoot = _protocolContractTreeRoot;
VERSION = 1;
L1_BLOCK_AT_GENESIS = block.number;
CLAIM_DURATION_IN_L2_SLOTS = _config.aztecEpochProofClaimWindowInL2Slots;

IS_FOUNDRY_TEST = VM_ADDRESS.code.length > 0;

rollupStore.epochProofVerifier = new MockVerifier();
rollupStore.vkTreeRoot = _vkTreeRoot;
rollupStore.protocolContractTreeRoot = _protocolContractTreeRoot;

// Genesis block
rollupStore.blocks[0] = BlockLog({
feeHeader: FeeHeader({
Expand Down
9 changes: 9 additions & 0 deletions l1-contracts/src/core/interfaces/ISlasher.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2024 Aztec Labs.
pragma solidity >=0.8.27;

import {IPayload} from "@aztec/governance/interfaces/IPayload.sol";

interface ISlasher {
function slash(IPayload _payload) external returns (bool);
}
37 changes: 37 additions & 0 deletions l1-contracts/src/core/staking/Slasher.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2024 Aztec Labs.
pragma solidity >=0.8.27;

import {ISlasher} from "@aztec/core/interfaces/ISlasher.sol";
import {SlashingProposer} from "@aztec/core/staking/SlashingProposer.sol";
import {IPayload} from "@aztec/governance/interfaces/IPayload.sol";

contract Slasher is ISlasher {
SlashingProposer public immutable PROPOSER;

event SlashFailed(address target, bytes data, bytes returnData);

error Slasher__CallerNotProposer(address caller, address proposer); // 0x44c1f74f

constructor(uint256 _n, uint256 _m) {
PROPOSER = new SlashingProposer(msg.sender, this, _n, _m);
}

function slash(IPayload _payload) external override(ISlasher) returns (bool) {
require(
msg.sender == address(PROPOSER), Slasher__CallerNotProposer(msg.sender, address(PROPOSER))
);

IPayload.Action[] memory actions = _payload.getActions();

for (uint256 i = 0; i < actions.length; i++) {
// Allow failure of individual calls but emit the failure!
(bool success, bytes memory returnData) = actions[i].target.call(actions[i].data);
if (!success) {
emit SlashFailed(actions[i].target, actions[i].data, returnData);
}
}

return true;
}
}
35 changes: 35 additions & 0 deletions l1-contracts/src/core/staking/SlashingProposer.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2024 Aztec Labs.
pragma solidity >=0.8.27;

import {ISlasher} from "@aztec/core/interfaces/ISlasher.sol";
import {IGovernanceProposer} from "@aztec/governance/interfaces/IGovernanceProposer.sol";
import {IPayload} from "@aztec/governance/interfaces/IPayload.sol";
import {EmpireBase} from "@aztec/governance/proposer/EmpireBase.sol";

/**
* @notice A SlashingProposer implementation following the empire model
*/
contract SlashingProposer is IGovernanceProposer, EmpireBase {
address public immutable INSTANCE;
ISlasher public immutable SLASHER;

constructor(address _instance, ISlasher _slasher, uint256 _slashingQuorum, uint256 _roundSize)
EmpireBase(_slashingQuorum, _roundSize)
{
INSTANCE = _instance;
SLASHER = _slasher;
}

function getExecutor() public view override(EmpireBase, IGovernanceProposer) returns (address) {
return address(SLASHER);
}

function getInstance() public view override(EmpireBase, IGovernanceProposer) returns (address) {
return INSTANCE;
}

function _execute(IPayload _proposal) internal override(EmpireBase) returns (bool) {
return SLASHER.slash(_proposal);
}
}
16 changes: 12 additions & 4 deletions l1-contracts/src/core/staking/Staking.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
} from "@aztec/core/interfaces/IStaking.sol";
import {Errors} from "@aztec/core/libraries/Errors.sol";
import {Timestamp} from "@aztec/core/libraries/TimeMath.sol";
import {Slasher} from "@aztec/core/staking/Slasher.sol";
import {IERC20} from "@oz/token/ERC20/IERC20.sol";
import {SafeERC20} from "@oz/token/ERC20/utils/SafeERC20.sol";
import {EnumerableSet} from "@oz/utils/structs/EnumerableSet.sol";
Expand All @@ -23,14 +24,19 @@ contract Staking is IStaking {
// Constant pulled out of the ass
Timestamp public constant EXIT_DELAY = Timestamp.wrap(60 * 60 * 24);

address public immutable SLASHER;
Slasher public immutable SLASHER;
IERC20 public immutable STAKING_ASSET;
uint256 public immutable MINIMUM_STAKE;

StakingStorage internal stakingStore;

constructor(address _slasher, IERC20 _stakingAsset, uint256 _minimumStake) {
SLASHER = _slasher;
constructor(
IERC20 _stakingAsset,
uint256 _minimumStake,
uint256 _slashingQuorum,
uint256 _roundSize
) {
SLASHER = new Slasher(_slashingQuorum, _roundSize);
STAKING_ASSET = _stakingAsset;
MINIMUM_STAKE = _minimumStake;
}
Expand All @@ -57,7 +63,9 @@ contract Staking is IStaking {
}

function slash(address _attester, uint256 _amount) external override(IStaking) {
require(msg.sender == SLASHER, Errors.Staking__NotSlasher(SLASHER, msg.sender));
require(
msg.sender == address(SLASHER), Errors.Staking__NotSlasher(address(SLASHER), msg.sender)
);

ValidatorInfo storage validator = stakingStore.info[_attester];
require(validator.status != Status.NONE, Errors.Staking__NoOneToSlash(_attester));
Expand Down
2 changes: 1 addition & 1 deletion l1-contracts/src/governance/CoinIssuer.sol
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ contract CoinIssuer is ICoinIssuer, Ownable {
*/
function mint(address _to, uint256 _amount) external override(ICoinIssuer) onlyOwner {
uint256 maxMint = mintAvailable();
require(_amount <= maxMint, Errors.CoinIssuer__InssuficientMintAvailable(maxMint, _amount));
require(_amount <= maxMint, Errors.CoinIssuer__InsufficientMintAvailable(maxMint, _amount));
timeOfLastMint = block.timestamp;
ASSET.mint(_to, _amount);
}
Expand Down
10 changes: 5 additions & 5 deletions l1-contracts/src/governance/interfaces/IGovernanceProposer.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,19 @@
pragma solidity >=0.8.27;

import {Slot} from "@aztec/core/libraries/TimeMath.sol";
import {IGovernance} from "@aztec/governance/interfaces/IGovernance.sol";
import {IPayload} from "@aztec/governance/interfaces/IPayload.sol";

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

function vote(IPayload _proposa) external returns (bool);
function pushProposal(uint256 _roundNumber) external returns (bool);
function vote(IPayload _proposal) external returns (bool);
function executeProposal(uint256 _roundNumber) external returns (bool);
function yeaCount(address _instance, uint256 _round, IPayload _proposal)
external
view
returns (uint256);
function computeRound(Slot _slot) external view returns (uint256);
function getGovernance() external view returns (IGovernance);
function getInstance() external view returns (address);
function getExecutor() external view returns (address);
}
18 changes: 9 additions & 9 deletions l1-contracts/src/governance/libraries/Errors.sol
Original file line number Diff line number Diff line change
Expand Up @@ -45,20 +45,20 @@ library Errors {
error Governance__ProposalLib__ZeroYeaVotesNeeded();
error Governance__ProposalLib__MoreYeaVoteThanExistNeeded();

error GovernanceProposer__CanOnlyPushProposalInPast(); // 0x49fdf611"
error GovernanceProposer__FailedToPropose(IPayload proposal); // 0x6ca2a2ed
error GovernanceProposer__InstanceHaveNoCode(address instance); // 0x20a3b441
error GovernanceProposer__InsufficientVotes(); // 0xba1e05ef
error GovernanceProposer__CanOnlyExecuteProposalInPast(); // 0x8bf1d3b8
error GovernanceProposer__FailedToPropose(IPayload proposal); // 0x8d94fbfc
error GovernanceProposer__InstanceHaveNoCode(address instance); // 0x5fa92625
error GovernanceProposer__InsufficientVotes(uint256 votesCast, uint256 votesNeeded); // 0xd4ad89c2
error GovernanceProposer__InvalidNAndMValues(uint256 n, uint256 m); // 0x520d9704
error GovernanceProposer__NCannotBeLargerTHanM(uint256 n, uint256 m); // 0x2fdfc063
error GovernanceProposer__OnlyProposerCanVote(address caller, address proposer); // 0xba27df38
error GovernanceProposer__ProposalAlreadyExecuted(uint256 roundNumber); // 0x7aeacb17
error GovernanceProposer__ProposalCannotBeAddressZero(); // 0xdb3e4b6e
error GovernanceProposer__ProposalHaveNoCode(IPayload proposal); // 0xdce0615b
error GovernanceProposer__ProposalTooOld(uint256 roundNumber, uint256 currentRoundNumber); //0x02283b1a
error GovernanceProposer__VoteAlreadyCastForSlot(Slot slot); //0xc2201452
error GovernanceProposer__ProposalCannotBeAddressZero(); // 0x16ac1942
error GovernanceProposer__ProposalHaveNoCode(IPayload proposal); // 0xb69440a1
error GovernanceProposer__ProposalTooOld(uint256 roundNumber, uint256 currentRoundNumber); // 0xc3d7aa4f
error GovernanceProposer__VoteAlreadyCastForSlot(Slot slot); // 0x3a6150ca

error CoinIssuer__InssuficientMintAvailable(uint256 available, uint256 needed); // 0xf268b931
error CoinIssuer__InsufficientMintAvailable(uint256 available, uint256 needed); // 0xa1cc8799

error Registry__RollupAlreadyRegistered(address rollup); // 0x3c34eabf
error Registry__RollupNotRegistered(address rollup); // 0xa1fee4cf
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,8 @@ pragma solidity >=0.8.27;

import {ILeonidas} from "@aztec/core/interfaces/ILeonidas.sol";
import {Slot, SlotLib} from "@aztec/core/libraries/TimeMath.sol";
import {IGovernance} from "@aztec/governance/interfaces/IGovernance.sol";
import {IGovernanceProposer} from "@aztec/governance/interfaces/IGovernanceProposer.sol";
import {IPayload} from "@aztec/governance/interfaces/IPayload.sol";
import {IRegistry} from "@aztec/governance/interfaces/IRegistry.sol";
import {Errors} from "@aztec/governance/libraries/Errors.sol";

/**
Expand All @@ -17,7 +15,7 @@ import {Errors} from "@aztec/governance/libraries/Errors.sol";
* This also means that the implementation here will need to be "updated" if
* the interfaces of the sequencer selection changes, for example going optimistic.
*/
contract GovernanceProposer is IGovernanceProposer {
abstract contract EmpireBase is IGovernanceProposer {
using SlotLib for Slot;

struct RoundAccounting {
Expand All @@ -29,14 +27,12 @@ contract GovernanceProposer is IGovernanceProposer {

uint256 public constant LIFETIME_IN_ROUNDS = 5;

IRegistry public immutable REGISTRY;
uint256 public immutable N;
uint256 public immutable M;

mapping(address instance => mapping(uint256 roundNumber => RoundAccounting)) public rounds;

constructor(IRegistry _registry, uint256 _n, uint256 _m) {
REGISTRY = _registry;
constructor(uint256 _n, uint256 _m) {
N = _n;
M = _m;

Expand All @@ -57,11 +53,12 @@ contract GovernanceProposer is IGovernanceProposer {
* @return True if executed successfully, false otherwise
*/
function vote(IPayload _proposal) external override(IGovernanceProposer) returns (bool) {
require(
// For now, skipping this as the check is not really needed but there were not full agreement
/*require(
address(_proposal).code.length > 0, Errors.GovernanceProposer__ProposalHaveNoCode(_proposal)
);
);*/

address instance = REGISTRY.getRollup();
address instance = getInstance();
require(instance.code.length > 0, Errors.GovernanceProposer__InstanceHaveNoCode(instance));

ILeonidas selection = ILeonidas(instance);
Expand Down Expand Up @@ -94,22 +91,26 @@ contract GovernanceProposer is IGovernanceProposer {
}

/**
* @notice Push the proposal to the appela
* @notice Executes the proposal using the `_execute` function
*
* @param _roundNumber - The round number to execute
*
* @return True if executed successfully, false otherwise
*/
function pushProposal(uint256 _roundNumber) external override(IGovernanceProposer) returns (bool) {
function executeProposal(uint256 _roundNumber)
external
override(IGovernanceProposer)
returns (bool)
{
// Need to ensure that the round is not active.
address instance = REGISTRY.getRollup();
address instance = getInstance();
require(instance.code.length > 0, Errors.GovernanceProposer__InstanceHaveNoCode(instance));

ILeonidas selection = ILeonidas(instance);
Slot currentSlot = selection.getCurrentSlot();

uint256 currentRound = computeRound(currentSlot);
require(_roundNumber < currentRound, Errors.GovernanceProposer__CanOnlyPushProposalInPast());
require(_roundNumber < currentRound, Errors.GovernanceProposer__CanOnlyExecuteProposalInPast());
require(
_roundNumber + LIFETIME_IN_ROUNDS >= currentRound,
Errors.GovernanceProposer__ProposalTooOld(_roundNumber, currentRound)
Expand All @@ -120,16 +121,14 @@ contract GovernanceProposer is IGovernanceProposer {
require(
round.leader != IPayload(address(0)), Errors.GovernanceProposer__ProposalCannotBeAddressZero()
);
require(round.yeaCount[round.leader] >= N, Errors.GovernanceProposer__InsufficientVotes());
uint256 votesCast = round.yeaCount[round.leader];
require(votesCast >= N, Errors.GovernanceProposer__InsufficientVotes(votesCast, N));

round.executed = true;

emit ProposalPushed(round.leader, _roundNumber);
emit ProposalExecuted(round.leader, _roundNumber);

require(
getGovernance().propose(round.leader),
Errors.GovernanceProposer__FailedToPropose(round.leader)
);
require(_execute(round.leader), Errors.GovernanceProposer__FailedToPropose(round.leader));
return true;
}

Expand Down Expand Up @@ -162,7 +161,8 @@ contract GovernanceProposer is IGovernanceProposer {
return _slot.unwrap() / M;
}

function getGovernance() public view override(IGovernanceProposer) returns (IGovernance) {
return IGovernance(REGISTRY.getGovernance());
}
// Virtual functions
function getInstance() public view virtual override(IGovernanceProposer) returns (address);
function getExecutor() public view virtual override(IGovernanceProposer) returns (address);
function _execute(IPayload _proposal) internal virtual returns (bool);
}
Loading
Loading