Skip to content

Commit 880075b

Browse files
authored
feat: Consensus-based slashing proposer contract (#16357)
Implementation for the consensus-based slashing on L1 as discussed [here](https://linear.app/aztec-labs/document/slashing-discussion-w33-8810b51b11b0#l1-consensus-db4c0919): > Build a new SlashingProposer version that does not extend from EmpireBase, but follows same logic on slashing rounds, execution delays, and allowing votes from the current L2 proposer only. > Each vote is now an array of proposed slashes. We define a slashing-unit value, and every slash is a multiple of this unit. Slashes are represented as a bit vector, where the i-th 4-bits (or 2, or 8, depending on how much granularity we want) indicate how many slashing-units proposer i is slashed. An expensive tallying function computes the effective payload by iterating over all votes. This still keeps the EmpireSlashProposer around. Next step will be to let the `Slasher` choose which implementation to use, so we can keep tests for both, and then rebase this on top of #16284 and tweak the updated slasher client to use this new SlashProposer. Other changes: - Updates the `SlashProposer` to be `Ownable` by governance, who can execute slash payloads. - Modifies the `Slasher` deployment due to contract size limits (the slasher no longer deploys the proposer, instead it needs to be initialized after deployment with the proposer address). - Renames `ExtRollupLibN` to have more descriptive names. - Adds env var config for the new proposer flavor. - Adds type-checking for L1 contract deployer on ts.
2 parents b630fd6 + 474754f commit 880075b

40 files changed

+2537
-211
lines changed

l1-contracts/foundry.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,9 @@ gas_reports = [
2626
"Rollup",
2727
"RollupCore",
2828
"Slasher",
29-
"SlashingProposer",
3029
"RollupWithPreheating",
30+
"EmpireSlashingProposer",
31+
"TallySlashingProposer",
3132
]
3233

3334
remappings = [

l1-contracts/src/core/Rollup.sol

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,8 @@ import {
3838
Timestamp,
3939
Errors,
4040
CommitteeAttestations,
41-
ExtRollupLib,
42-
ExtRollupLib2,
41+
RollupOperationsExtLib,
42+
ValidatorOperationsExtLib,
4343
EthValue,
4444
STFLib,
4545
RollupStore,
@@ -93,7 +93,7 @@ contract Rollup is IStaking, IValidatorSelection, IRollup, RollupCore {
9393
BlockHeaderValidationFlags memory _flags
9494
) external override(IRollup) {
9595
Timestamp currentTime = Timestamp.wrap(block.timestamp);
96-
ExtRollupLib.validateHeaderWithAttestations(
96+
RollupOperationsExtLib.validateHeaderWithAttestations(
9797
ValidateHeaderArgs({
9898
header: _header,
9999
digest: _digest,
@@ -134,7 +134,19 @@ contract Rollup is IStaking, IValidatorSelection, IRollup, RollupCore {
134134
* @return The committee size for the given timestamp
135135
*/
136136
function getCommitteeCommitmentAt(Timestamp _ts) external override(IValidatorSelection) returns (bytes32, uint256) {
137-
return ExtRollupLib2.getCommitteeCommitmentAt(getEpochAt(_ts));
137+
return ValidatorOperationsExtLib.getCommitteeCommitmentAt(getEpochAt(_ts));
138+
}
139+
140+
/**
141+
* @notice Get the committee commitment a the given epoch
142+
*
143+
* @param _epoch - The epoch to get the committee for
144+
*
145+
* @return The committee commitment for the given epoch
146+
* @return The committee size for the given epoch
147+
*/
148+
function getEpochCommitteeCommitment(Epoch _epoch) external override(IValidatorSelection) returns (bytes32, uint256) {
149+
return ValidatorOperationsExtLib.getCommitteeCommitmentAt(_epoch);
138150
}
139151

140152
/**
@@ -163,11 +175,11 @@ contract Rollup is IStaking, IValidatorSelection, IRollup, RollupCore {
163175
override(IRollup)
164176
returns (Slot, uint256)
165177
{
166-
return ExtRollupLib2.canProposeAtTime(_ts, _archive, _who);
178+
return ValidatorOperationsExtLib.canProposeAtTime(_ts, _archive, _who);
167179
}
168180

169181
function getTargetCommitteeSize() external view override(IValidatorSelection) returns (uint256) {
170-
return ExtRollupLib2.getTargetCommitteeSize();
182+
return ValidatorOperationsExtLib.getTargetCommitteeSize();
171183
}
172184

173185
function getGenesisTime() external view override(IValidatorSelection) returns (Timestamp) {
@@ -267,7 +279,7 @@ contract Rollup is IStaking, IValidatorSelection, IRollup, RollupCore {
267279
bytes32[] calldata _fees,
268280
bytes calldata _blobPublicInputs
269281
) external view override(IRollup) returns (bytes32[] memory) {
270-
return ExtRollupLib.getEpochProofPublicInputs(_start, _end, _args, _fees, _blobPublicInputs);
282+
return RollupOperationsExtLib.getEpochProofPublicInputs(_start, _end, _args, _fees, _blobPublicInputs);
271283
}
272284

273285
/**
@@ -280,7 +292,7 @@ contract Rollup is IStaking, IValidatorSelection, IRollup, RollupCore {
280292
override(IRollup)
281293
returns (bytes32[] memory, bytes32, bytes[] memory)
282294
{
283-
return ExtRollupLib.validateBlobs(_blobsInput, checkBlob);
295+
return RollupOperationsExtLib.validateBlobs(_blobsInput, checkBlob);
284296
}
285297

286298
/**
@@ -365,7 +377,7 @@ contract Rollup is IStaking, IValidatorSelection, IRollup, RollupCore {
365377
* @return The sample seed for the given timestamp
366378
*/
367379
function getSampleSeedAt(Timestamp _ts) external view override(IValidatorSelection) returns (uint256) {
368-
return ExtRollupLib2.getSampleSeedAt(getEpochAt(_ts));
380+
return ValidatorOperationsExtLib.getSampleSeedAt(getEpochAt(_ts));
369381
}
370382

371383
/**
@@ -374,7 +386,7 @@ contract Rollup is IStaking, IValidatorSelection, IRollup, RollupCore {
374386
* @return The sample seed for the current epoch
375387
*/
376388
function getCurrentSampleSeed() external view override(IValidatorSelection) returns (uint256) {
377-
return ExtRollupLib2.getSampleSeedAt(getCurrentEpoch());
389+
return ValidatorOperationsExtLib.getSampleSeedAt(getCurrentEpoch());
378390
}
379391

380392
/**
@@ -532,7 +544,7 @@ contract Rollup is IStaking, IValidatorSelection, IRollup, RollupCore {
532544
* @return The validator set for the given epoch
533545
*/
534546
function getEpochCommittee(Epoch _epoch) public override(IValidatorSelection) returns (address[] memory) {
535-
return ExtRollupLib2.getCommitteeAt(_epoch);
547+
return ValidatorOperationsExtLib.getCommitteeAt(_epoch);
536548
}
537549

538550
/**
@@ -557,7 +569,7 @@ contract Rollup is IStaking, IValidatorSelection, IRollup, RollupCore {
557569
* @return The address of the proposer
558570
*/
559571
function getProposerAt(Timestamp _ts) public override(IValidatorSelection) returns (address) {
560-
return ExtRollupLib2.getProposerAt(_ts.slotFromTimestamp());
572+
return ValidatorOperationsExtLib.getProposerAt(_ts.slotFromTimestamp());
561573
}
562574

563575
/**

l1-contracts/src/core/RollupCore.sol

Lines changed: 57 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,12 @@ import {IOutbox} from "@aztec/core/interfaces/messagebridge/IOutbox.sol";
1515
import {Constants} from "@aztec/core/libraries/ConstantsGen.sol";
1616
import {CommitteeAttestations} from "@aztec/core/libraries/rollup/AttestationLib.sol";
1717
import {Errors} from "@aztec/core/libraries/Errors.sol";
18-
import {ExtRollupLib} from "@aztec/core/libraries/rollup/ExtRollupLib.sol";
19-
import {ExtRollupLib2} from "@aztec/core/libraries/rollup/ExtRollupLib2.sol";
20-
import {ExtRollupLib3} from "@aztec/core/libraries/rollup/ExtRollupLib3.sol";
18+
import {RollupOperationsExtLib} from "@aztec/core/libraries/rollup/RollupOperationsExtLib.sol";
19+
import {ValidatorOperationsExtLib} from "@aztec/core/libraries/rollup/ValidatorOperationsExtLib.sol";
20+
import {RewardDeploymentExtLib} from "@aztec/core/libraries/rollup/RewardDeploymentExtLib.sol";
21+
import {TallySlasherDeploymentExtLib} from "@aztec/core/libraries/rollup/TallySlasherDeploymentExtLib.sol";
22+
import {EmpireSlasherDeploymentExtLib} from "@aztec/core/libraries/rollup/EmpireSlasherDeploymentExtLib.sol";
23+
import {SlasherFlavor} from "@aztec/core/interfaces/ISlasher.sol";
2124
import {EthValue, FeeLib} from "@aztec/core/libraries/rollup/FeeLib.sol";
2225
import {ProposeArgs} from "@aztec/core/libraries/rollup/ProposeLib.sol";
2326
import {STFLib, GenesisState} from "@aztec/core/libraries/rollup/STFLib.sol";
@@ -228,21 +231,44 @@ contract RollupCore is EIP712("Aztec Rollup", "1"), Ownable, IStakingCore, IVali
228231
);
229232

230233
Timestamp exitDelay = Timestamp.wrap(_config.exitDelaySeconds);
231-
ISlasher slasher = ExtRollupLib3.deploySlasher(
232-
address(this),
233-
_config.slashingQuorum,
234-
_config.slashingRoundSize,
235-
_config.slashingLifetimeInRounds,
236-
_config.slashingExecutionDelayInRounds,
237-
_config.slashingVetoer
238-
);
234+
235+
// Deploy slasher based on flavor
236+
ISlasher slasher;
237+
238+
// We call one external library or another based on the slasher flavor
239+
// This allows us to keep the slash flavors in separate external libraries so we do not exceed max contract size
240+
if (_config.slasherFlavor == SlasherFlavor.TALLY) {
241+
slasher = TallySlasherDeploymentExtLib.deployTallySlasher(
242+
address(this),
243+
_config.slashingVetoer,
244+
_governance,
245+
_config.slashingQuorum,
246+
_config.slashingRoundSize,
247+
_config.slashingLifetimeInRounds,
248+
_config.slashingExecutionDelayInRounds,
249+
_config.slashingUnit,
250+
_config.targetCommitteeSize,
251+
_config.aztecEpochDuration,
252+
_config.slashingOffsetInRounds
253+
);
254+
} else {
255+
slasher = EmpireSlasherDeploymentExtLib.deployEmpireSlasher(
256+
address(this),
257+
_config.slashingVetoer,
258+
_governance,
259+
_config.slashingQuorum,
260+
_config.slashingRoundSize,
261+
_config.slashingLifetimeInRounds,
262+
_config.slashingExecutionDelayInRounds
263+
);
264+
}
239265

240266
StakingLib.initialize(_stakingAsset, _gse, exitDelay, address(slasher), _config.stakingQueueConfig);
241-
ExtRollupLib2.initializeValidatorSelection(_config.targetCommitteeSize);
267+
ValidatorOperationsExtLib.initializeValidatorSelection(_config.targetCommitteeSize);
242268

243269
// If no booster is specifically provided, deploy one.
244270
if (address(_config.rewardConfig.booster) == address(0)) {
245-
_config.rewardConfig.booster = ExtRollupLib3.deployRewardBooster(_config.rewardBoostConfig);
271+
_config.rewardConfig.booster = RewardDeploymentExtLib.deployRewardBooster(_config.rewardBoostConfig);
246272
}
247273

248274
RewardLib.setConfig(_config.rewardConfig);
@@ -309,7 +335,7 @@ contract RollupCore is EIP712("Aztec Rollup", "1"), Ownable, IStakingCore, IVali
309335
* @param _slasher The address of the new slasher contract
310336
*/
311337
function setSlasher(address _slasher) external override(IStakingCore) onlyOwner {
312-
ExtRollupLib2.setSlasher(_slasher);
338+
ValidatorOperationsExtLib.setSlasher(_slasher);
313339
}
314340

315341
/**
@@ -327,7 +353,7 @@ contract RollupCore is EIP712("Aztec Rollup", "1"), Ownable, IStakingCore, IVali
327353
* @param _config New configuration including queue size limits and timing parameters
328354
*/
329355
function updateStakingQueueConfig(StakingQueueConfig memory _config) external override(IStakingCore) onlyOwner {
330-
ExtRollupLib2.updateStakingQueueConfig(_config);
356+
ValidatorOperationsExtLib.updateStakingQueueConfig(_config);
331357
}
332358

333359
/**
@@ -365,7 +391,7 @@ contract RollupCore is EIP712("Aztec Rollup", "1"), Ownable, IStakingCore, IVali
365391
* @param _proposalId The ID of the proposal to vote on
366392
*/
367393
function vote(uint256 _proposalId) external override(IStakingCore) {
368-
ExtRollupLib2.vote(_proposalId);
394+
ValidatorOperationsExtLib.vote(_proposalId);
369395
}
370396

371397
/**
@@ -386,7 +412,7 @@ contract RollupCore is EIP712("Aztec Rollup", "1"), Ownable, IStakingCore, IVali
386412
G1Point memory _proofOfPossession,
387413
bool _moveWithLatestRollup
388414
) external override(IStakingCore) {
389-
ExtRollupLib2.deposit(
415+
ValidatorOperationsExtLib.deposit(
390416
_attester, _withdrawer, _publicKeyInG1, _publicKeyInG2, _proofOfPossession, _moveWithLatestRollup
391417
);
392418
}
@@ -397,7 +423,7 @@ contract RollupCore is EIP712("Aztec Rollup", "1"), Ownable, IStakingCore, IVali
397423
* This helps maintain a controlled growth rate of the validator set.
398424
*/
399425
function flushEntryQueue() external override(IStakingCore) {
400-
ExtRollupLib2.flushEntryQueue();
426+
ValidatorOperationsExtLib.flushEntryQueue();
401427
}
402428

403429
/**
@@ -409,7 +435,7 @@ contract RollupCore is EIP712("Aztec Rollup", "1"), Ownable, IStakingCore, IVali
409435
* @return True if withdrawal was initiated, false if already initiated
410436
*/
411437
function initiateWithdraw(address _attester, address _recipient) external override(IStakingCore) returns (bool) {
412-
return ExtRollupLib2.initiateWithdraw(_attester, _recipient);
438+
return ValidatorOperationsExtLib.initiateWithdraw(_attester, _recipient);
413439
}
414440

415441
/**
@@ -418,7 +444,7 @@ contract RollupCore is EIP712("Aztec Rollup", "1"), Ownable, IStakingCore, IVali
418444
* @param _attester The validator address whose withdrawal to finalize
419445
*/
420446
function finaliseWithdraw(address _attester) external override(IStakingCore) {
421-
ExtRollupLib2.finaliseWithdraw(_attester);
447+
ValidatorOperationsExtLib.finaliseWithdraw(_attester);
422448
}
423449

424450
/**
@@ -429,7 +455,7 @@ contract RollupCore is EIP712("Aztec Rollup", "1"), Ownable, IStakingCore, IVali
429455
* @return True if slashing was successful
430456
*/
431457
function slash(address _attester, uint256 _amount) external override(IStakingCore) returns (bool) {
432-
return ExtRollupLib2.slash(_attester, _amount);
458+
return ValidatorOperationsExtLib.slash(_attester, _amount);
433459
}
434460

435461
/**
@@ -439,7 +465,7 @@ contract RollupCore is EIP712("Aztec Rollup", "1"), Ownable, IStakingCore, IVali
439465
* Pruning occurs at epoch boundaries and removes all blocks in unproven epochs.
440466
*/
441467
function prune() external override(IRollupCore) {
442-
ExtRollupLib.prune();
468+
RollupOperationsExtLib.prune();
443469
}
444470

445471
/**
@@ -450,7 +476,7 @@ contract RollupCore is EIP712("Aztec Rollup", "1"), Ownable, IStakingCore, IVali
450476
* @param _args Contains the epoch range, public inputs, fees, attestations, and the ZK proof
451477
*/
452478
function submitEpochRootProof(SubmitEpochRootProofArgs calldata _args) external override(IRollupCore) {
453-
ExtRollupLib.submitEpochRootProof(_args);
479+
RollupOperationsExtLib.submitEpochRootProof(_args);
454480
}
455481

456482
/**
@@ -471,7 +497,7 @@ contract RollupCore is EIP712("Aztec Rollup", "1"), Ownable, IStakingCore, IVali
471497
address[] calldata _signers,
472498
bytes calldata _blobInput
473499
) external override(IRollupCore) {
474-
ExtRollupLib.propose(_args, _attestations, _signers, _blobInput, checkBlob);
500+
RollupOperationsExtLib.propose(_args, _attestations, _signers, _blobInput, checkBlob);
475501
}
476502

477503
/**
@@ -489,7 +515,7 @@ contract RollupCore is EIP712("Aztec Rollup", "1"), Ownable, IStakingCore, IVali
489515
address[] memory _committee,
490516
uint256 _invalidIndex
491517
) external override(IRollupCore) {
492-
ExtRollupLib2.invalidateBadAttestation(_blockNumber, _attestations, _committee, _invalidIndex);
518+
ValidatorOperationsExtLib.invalidateBadAttestation(_blockNumber, _attestations, _committee, _invalidIndex);
493519
}
494520

495521
/**
@@ -505,15 +531,16 @@ contract RollupCore is EIP712("Aztec Rollup", "1"), Ownable, IStakingCore, IVali
505531
CommitteeAttestations memory _attestations,
506532
address[] memory _committee
507533
) external override(IRollupCore) {
508-
ExtRollupLib2.invalidateInsufficientAttestations(_blockNumber, _attestations, _committee);
534+
ValidatorOperationsExtLib.invalidateInsufficientAttestations(_blockNumber, _attestations, _committee);
509535
}
510536

511537
/**
512538
* @notice Sets up validator selection for the current epoch
513539
* @dev Can be called by anyone at the start of an epoch. Samples the committee and determines proposers for all
514540
* slots in the epoch. Also stores a seed that is used for future sampling. The corresponding library
515541
* functionality is automatically called when `RollupCore.propose(...)` is called (via the
516-
* `ExtRollupLib.propose(...)` -> `ProposeLib.propose(...)` -> `ValidatorSelectionLib.setupEpoch(...)`).
542+
* `RollupOperationsExtLib.propose(...)` -> `ProposeLib.propose(...)` ->
543+
* `ValidatorSelectionLib.setupEpoch(...)`).
517544
*
518545
* If there are missed proposals then setupEpoch does not get called automatically. Since the next committee
519546
* selection is computed based on the stored randao and the epoch number, failing to update the randao stored
@@ -526,7 +553,7 @@ contract RollupCore is EIP712("Aztec Rollup", "1"), Ownable, IStakingCore, IVali
526553
* generation impractical.
527554
*/
528555
function setupEpoch() external override(IValidatorSelectionCore) {
529-
ExtRollupLib2.setupEpoch();
556+
ValidatorOperationsExtLib.setupEpoch();
530557
}
531558

532559
/**
@@ -536,7 +563,7 @@ contract RollupCore is EIP712("Aztec Rollup", "1"), Ownable, IStakingCore, IVali
536563
* `setupEpoch` to update the randao checkpoints.
537564
*/
538565
function checkpointRandao() public override(IValidatorSelectionCore) {
539-
ExtRollupLib2.checkpointRandao();
566+
ValidatorOperationsExtLib.checkpointRandao();
540567
}
541568

542569
/**
@@ -555,7 +582,7 @@ contract RollupCore is EIP712("Aztec Rollup", "1"), Ownable, IStakingCore, IVali
555582
* @return The number of validators that can be added in the next flush
556583
*/
557584
function getEntryQueueFlushSize() public view override(IStakingCore) returns (uint256) {
558-
return ExtRollupLib2.getEntryQueueFlushSize();
585+
return ValidatorOperationsExtLib.getEntryQueueFlushSize();
559586
}
560587

561588
/**

l1-contracts/src/core/interfaces/IRollup.sol

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
pragma solidity >=0.8.27;
44

55
import {IFeeJuicePortal} from "@aztec/core/interfaces/IFeeJuicePortal.sol";
6+
import {SlasherFlavor} from "@aztec/core/interfaces/ISlasher.sol";
67
import {IVerifier} from "@aztec/core/interfaces/IVerifier.sol";
78
import {IInbox} from "@aztec/core/interfaces/messagebridge/IInbox.sol";
89
import {IOutbox} from "@aztec/core/interfaces/messagebridge/IOutbox.sol";
@@ -60,6 +61,9 @@ struct RollupConfigInput {
6061
uint256 slashingRoundSize;
6162
uint256 slashingLifetimeInRounds;
6263
uint256 slashingExecutionDelayInRounds;
64+
uint256 slashingUnit;
65+
uint256 slashingOffsetInRounds;
66+
SlasherFlavor slasherFlavor;
6367
address slashingVetoer;
6468
uint256 manaTarget;
6569
uint256 exitDelaySeconds;

l1-contracts/src/core/interfaces/ISlasher.sol

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,14 @@ pragma solidity >=0.8.27;
44

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

7+
enum SlasherFlavor {
8+
EMPIRE,
9+
TALLY
10+
}
11+
712
interface ISlasher {
13+
event VetoedPayload(address indexed payload);
14+
815
function slash(IPayload _payload) external returns (bool);
916
function vetoPayload(IPayload _payload) external returns (bool);
1017
}

l1-contracts/src/core/interfaces/IValidatorSelection.sol

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ interface IValidatorSelection is IValidatorSelectionCore, IEmperor {
2727
function getCommitteeAt(Timestamp _ts) external returns (address[] memory);
2828
function getCommitteeCommitmentAt(Timestamp _ts) external returns (bytes32, uint256);
2929
function getEpochCommittee(Epoch _epoch) external returns (address[] memory);
30+
function getEpochCommitteeCommitment(Epoch _epoch) external returns (bytes32, uint256);
3031

3132
// Stable
3233
function getCurrentEpoch() external view returns (Epoch);

0 commit comments

Comments
 (0)