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
14 changes: 14 additions & 0 deletions l1-contracts/src/core/Rollup.sol
Original file line number Diff line number Diff line change
Expand Up @@ -461,12 +461,26 @@ contract Rollup is IStaking, IValidatorSelection, IRollup, RollupCore {
return rollupStore.epochRewards[_epoch].rewards;
}

/**
* @notice Get the rewards for a specific prover for a given epoch
* BEWARE! If the epoch is not past its deadline, this value is the "current" value
* and could change if a provers proves a longer series of blocks.
*
* @param _epoch - The epoch to get the rewards for
* @param _prover - The prover to get the rewards for
*
* @return The rewards for the specific prover for the given epoch
*/
function getSpecificProverRewardsForEpoch(Epoch _epoch, address _prover)
external
view
override(IRollup)
returns (uint256)
{
if (rollupStore.proverClaimed[_prover][_epoch]) {
return 0;
}

EpochRewards storage er = rollupStore.epochRewards[_epoch];
uint256 length = er.longestProvenLength;

Expand Down
63 changes: 45 additions & 18 deletions l1-contracts/src/core/RollupCore.sol
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,47 @@ contract RollupCore is
rollupStore.provingCostPerMana = _provingCostPerMana;
}

function claimSequencerRewards(address _recipient)
external
override(IRollupCore)
returns (uint256)
{
uint256 amount = rollupStore.sequencerRewards[msg.sender];
rollupStore.sequencerRewards[msg.sender] = 0;
ASSET.transfer(_recipient, amount);

return amount;
}

function claimProverRewards(address _recipient, Epoch[] memory _epochs)
external
override(IRollupCore)
returns (uint256)
{
Slot currentSlot = Timestamp.wrap(block.timestamp).slotFromTimestamp();
uint256 accumulatedRewards = 0;
for (uint256 i = 0; i < _epochs.length; i++) {
Slot deadline = _epochs[i].toSlots() + Slot.wrap(PROOF_SUBMISSION_WINDOW);
require(deadline < currentSlot, Errors.Rollup__NotPastDeadline(deadline, currentSlot));

// We can use fancier bitmaps for performance
require(
!rollupStore.proverClaimed[msg.sender][_epochs[i]],
Errors.Rollup__AlreadyClaimed(msg.sender, _epochs[i])
);
rollupStore.proverClaimed[msg.sender][_epochs[i]] = true;

EpochRewards storage e = rollupStore.epochRewards[_epochs[i]];
if (e.subEpoch[e.longestProvenLength].hasSubmitted[msg.sender]) {
accumulatedRewards += (e.rewards / e.subEpoch[e.longestProvenLength].summedCount);
}
}

ASSET.transfer(_recipient, accumulatedRewards);

return accumulatedRewards;
}

function deposit(address _attester, address _proposer, address _withdrawer, uint256 _amount)
external
override(IStakingCore)
Expand Down Expand Up @@ -338,7 +379,10 @@ contract RollupCore is

interim.deadline = startEpoch.toSlots() + Slot.wrap(PROOF_SUBMISSION_WINDOW);
require(
interim.deadline >= Timestamp.wrap(block.timestamp).slotFromTimestamp(), "past deadline"
interim.deadline >= Timestamp.wrap(block.timestamp).slotFromTimestamp(),
Errors.Rollup__PastDeadline(
interim.deadline, Timestamp.wrap(block.timestamp).slotFromTimestamp()
)
);

// By making sure that the previous block is in another epoch, we know that we were
Expand Down Expand Up @@ -611,23 +655,6 @@ contract RollupCore is
return rollupStore.blocks[_blockNumber].slotNumber.epochFromSlot();
}

/**
* @notice Get the epoch that should be proven
*
* @dev This is the epoch that should be proven. It does so by getting the epoch of the block
* following the last proven block. If there is no such block (i.e. the pending chain is
* the same as the proven chain), then revert.
*
* @return uint256 - The epoch to prove
*/
function getEpochToProve() public view override(IRollupCore) returns (Epoch) {
require(
rollupStore.tips.provenBlockNumber != rollupStore.tips.pendingBlockNumber,
Errors.Rollup__NoEpochToProve()
);
return getEpochForBlock(rollupStore.tips.provenBlockNumber + 1);
}

function canPrune() public view override(IRollupCore) returns (bool) {
return canPruneAtTime(Timestamp.wrap(block.timestamp));
}
Expand Down
9 changes: 8 additions & 1 deletion l1-contracts/src/core/interfaces/IRollup.sol
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,9 @@ struct RollupStore {
IVerifier epochProofVerifier;
mapping(address => uint256) sequencerRewards;
mapping(Epoch => EpochRewards) epochRewards;
// @todo Below can be optimised with a bitmap as we can benefit from provers likely proving for epochs close
// to one another.
mapping(address prover => mapping(Epoch epoch => bool claimed)) proverClaimed;
EthValue provingCostPerMana;
}

Expand Down Expand Up @@ -96,6 +99,11 @@ interface IRollupCore {
event L2ProofVerified(uint256 indexed blockNumber, bytes32 indexed proverId);
event PrunedPending(uint256 provenBlockNumber, uint256 pendingBlockNumber);

function claimSequencerRewards(address _recipient) external returns (uint256);
function claimProverRewards(address _recipient, Epoch[] memory _epochs)
external
returns (uint256);

function prune() external;
function updateL1GasFeeOracle() external;

Expand Down Expand Up @@ -124,7 +132,6 @@ interface IRollupCore {

function canPrune() external view returns (bool);
function canPruneAtTime(Timestamp _ts) external view returns (bool);
function getEpochToProve() external view returns (Epoch);

function getEpochForBlock(uint256 _blockNumber) external view returns (Epoch);
}
Expand Down
3 changes: 3 additions & 0 deletions l1-contracts/src/core/libraries/Errors.sol
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,9 @@ library Errors {
error Rollup__StartAndEndNotSameEpoch(Epoch start, Epoch end);
error Rollup__StartIsNotFirstBlockOfEpoch();
error Rollup__StartIsNotBuildingOnProven();
error Rollup__AlreadyClaimed(address prover, Epoch epoch);
error Rollup__NotPastDeadline(Slot deadline, Slot currentSlot);
error Rollup__PastDeadline(Slot deadline, Slot currentSlot);

// HeaderLib
error HeaderLib__InvalidHeaderSize(uint256 expected, uint256 actual); // 0xf3ccb247
Expand Down
74 changes: 66 additions & 8 deletions l1-contracts/test/MultiProof.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ contract MultiProofTest is RollupBase {
uint256 internal SLOT_DURATION;
uint256 internal EPOCH_DURATION;

address internal sequencer = address(bytes20("sequencer"));

constructor() {
TimeLib.initialize(
block.timestamp, TestConstants.AZTEC_SLOT_DURATION, TestConstants.AZTEC_EPOCH_DURATION
Expand Down Expand Up @@ -115,8 +117,7 @@ contract MultiProofTest is RollupBase {
emit log_named_uint("proven block number", provenBlockNumber);
emit log_named_uint("pending block number", pendingBlockNumber);

address[2] memory provers = [address(bytes20("lasse")), address(bytes20("mitch"))];
address sequencer = address(bytes20("sequencer"));
address[2] memory provers = [address(bytes20("alice")), address(bytes20("bob"))];

emit log_named_decimal_uint("sequencer rewards", rollup.getSequencerRewards(sequencer), 18);
emit log_named_decimal_uint(
Expand All @@ -141,20 +142,77 @@ contract MultiProofTest is RollupBase {
}
}

function testMultiProof() public setUpFor("mixed_block_1") {
function testMultipleProvers() public setUpFor("mixed_block_1") {
address alice = address(bytes20("alice"));
address bob = address(bytes20("bob"));

_proposeBlock("mixed_block_1", 1, 15e6);
_proposeBlock("mixed_block_2", 2, 15e6);

assertEq(rollup.getProvenBlockNumber(), 0, "Block already proven");

string memory name = "mixed_block_";
_proveBlocks(name, 1, 1, address(bytes20("lasse")));
_proveBlocks(name, 1, 1, address(bytes20("mitch")));
_proveBlocks(name, 1, 2, address(bytes20("mitch")));
_proveBlocks(name, 1, 1, alice);
_proveBlocks(name, 1, 1, bob);
_proveBlocks(name, 1, 2, bob);

logStatus();

assertTrue(rollup.getHasSubmitted(Epoch.wrap(0), 1, alice));
assertFalse(rollup.getHasSubmitted(Epoch.wrap(0), 2, alice));
assertTrue(rollup.getHasSubmitted(Epoch.wrap(0), 1, bob));
assertTrue(rollup.getHasSubmitted(Epoch.wrap(0), 2, bob));

assertEq(rollup.getProvenBlockNumber(), 2, "Block not proven");

{
uint256 sequencerRewards = rollup.getSequencerRewards(sequencer);
assertGt(sequencerRewards, 0, "Sequencer rewards is zero");
vm.prank(sequencer);
uint256 sequencerRewardsClaimed = rollup.claimSequencerRewards(sequencer);
assertEq(sequencerRewardsClaimed, sequencerRewards, "Sequencer rewards not claimed");
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can we also assert the value? Or at least assert greater than 0?

assertEq(rollup.getSequencerRewards(sequencer), 0, "Sequencer rewards not zeroed");
}

Epoch[] memory epochs = new Epoch[](1);
epochs[0] = Epoch.wrap(0);

{
uint256 aliceRewards = rollup.getSpecificProverRewardsForEpoch(Epoch.wrap(0), alice);
assertEq(aliceRewards, 0, "Alice rewards not zero");
}

{
uint256 bobRewards = rollup.getSpecificProverRewardsForEpoch(Epoch.wrap(0), bob);
assertGt(bobRewards, 0, "Bob rewards is zero");

vm.expectRevert(
abi.encodeWithSelector(
Errors.Rollup__NotPastDeadline.selector, TestConstants.AZTEC_PROOF_SUBMISSION_WINDOW, 2
)
);
vm.prank(bob);
rollup.claimProverRewards(bob, epochs);

vm.warp(
Timestamp.unwrap(
rollup.getTimestampForSlot(Slot.wrap(TestConstants.AZTEC_PROOF_SUBMISSION_WINDOW + 1))
)
);
vm.prank(bob);
uint256 bobRewardsClaimed = rollup.claimProverRewards(bob, epochs);

assertEq(bobRewardsClaimed, bobRewards, "Bob rewards not claimed");
assertEq(
rollup.getSpecificProverRewardsForEpoch(Epoch.wrap(0), bob), 0, "Bob rewards not zeroed"
);

vm.expectRevert(
abi.encodeWithSelector(Errors.Rollup__AlreadyClaimed.selector, bob, Epoch.wrap(0))
);
vm.prank(bob);
rollup.claimProverRewards(bob, epochs);
}
}

function testNoHolesInProvenBlocks() public setUpFor("mixed_block_1") {
Expand All @@ -166,7 +224,7 @@ contract MultiProofTest is RollupBase {
name,
2,
2,
address(bytes20("lasse")),
address(bytes20("alice")),
abi.encodeWithSelector(Errors.Rollup__StartIsNotBuildingOnProven.selector)
);
}
Expand All @@ -180,7 +238,7 @@ contract MultiProofTest is RollupBase {
name,
1,
2,
address(bytes20("lasse")),
address(bytes20("alice")),
abi.encodeWithSelector(Errors.Rollup__StartAndEndNotSameEpoch.selector, 0, 1)
);
}
Expand Down
1 change: 0 additions & 1 deletion l1-contracts/test/Rollup.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,6 @@ contract RollupTest is RollupBase {

function testPruneDuringPropose() public setUpFor("mixed_block_1") {
_proposeBlock("mixed_block_1", 1);
assertEq(rollup.getEpochToProve(), 0, "Invalid epoch to prove");

// the same block is proposed, with the diff in slot number.
_proposeBlock("mixed_block_1", rollup.getProofSubmissionWindow() + 1);
Expand Down
2 changes: 1 addition & 1 deletion l1-contracts/test/fees/FeeRollup.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ contract FeeRollupTest is FeeModelTestPoints, DecoderBase {
aztecSlotDuration: SLOT_DURATION,
aztecEpochDuration: EPOCH_DURATION,
targetCommitteeSize: 48,
aztecProofSubmissionWindow: EPOCH_DURATION * 2,
aztecProofSubmissionWindow: EPOCH_DURATION * 2 - 1,
minimumStake: TestConstants.AZTEC_MINIMUM_STAKE,
slashingQuorum: TestConstants.AZTEC_SLASHING_QUORUM,
slashingRoundSize: TestConstants.AZTEC_SLASHING_ROUND_SIZE
Expand Down
2 changes: 1 addition & 1 deletion l1-contracts/test/harnesses/TestConstants.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ library TestConstants {
uint256 internal constant AZTEC_SLOT_DURATION = 24;
uint256 internal constant AZTEC_EPOCH_DURATION = 16;
uint256 internal constant AZTEC_TARGET_COMMITTEE_SIZE = 48;
uint256 internal constant AZTEC_PROOF_SUBMISSION_WINDOW = 32;
uint256 internal constant AZTEC_PROOF_SUBMISSION_WINDOW = AZTEC_EPOCH_DURATION * 2 - 1;
uint256 internal constant AZTEC_MINIMUM_STAKE = 100e18;
uint256 internal constant AZTEC_SLASHING_QUORUM = 6;
uint256 internal constant AZTEC_SLASHING_ROUND_SIZE = 10;
Expand Down
2 changes: 1 addition & 1 deletion yarn-project/end-to-end/src/e2e_p2p/slashing.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ describe('e2e_p2p_slashing', () => {
metricsPort: shouldCollectMetrics(),
initialConfig: {
aztecEpochDuration: 1,
aztecProofSubmissionWindow: 2,
aztecProofSubmissionWindow: 1,
slashingQuorum,
slashingRoundSize,
},
Expand Down
2 changes: 1 addition & 1 deletion yarn-project/end-to-end/src/e2e_simple.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ describe('e2e_simple', () => {
blockCheckIntervalMS: 200,
minTxsPerBlock: 1,
aztecEpochDuration: 8,
aztecProofSubmissionWindow: 16,
aztecProofSubmissionWindow: 15,
aztecSlotDuration: 12,
ethereumSlotDuration: 12,
startProverNode: true,
Expand Down
2 changes: 1 addition & 1 deletion yarn-project/ethereum/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export const DefaultL1ContractsConfig = {
aztecSlotDuration: 24,
aztecEpochDuration: 16,
aztecTargetCommitteeSize: 48,
aztecProofSubmissionWindow: 32, // you have a full epoch to submit a proof after the epoch to prove ends
aztecProofSubmissionWindow: 31, // you have a full epoch to submit a proof after the epoch to prove ends
minimumStake: BigInt(100e18),
slashingQuorum: 6,
slashingRoundSize: 10,
Expand Down
8 changes: 0 additions & 8 deletions yarn-project/ethereum/src/contracts/rollup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -231,14 +231,6 @@ export class RollupContract {
return this.rollup.read.getEpochProofPublicInputs(args);
}

public async getEpochToProve(): Promise<bigint | undefined> {
try {
return await this.rollup.read.getEpochToProve();
} catch (err: unknown) {
throw formatViemError(err);
}
}

public async validateHeader(
args: readonly [
`0x${string}`,
Expand Down