diff --git a/l1-contracts/src/core/Rollup.sol b/l1-contracts/src/core/Rollup.sol index a675f12ee03e..f1cb7838e66e 100644 --- a/l1-contracts/src/core/Rollup.sol +++ b/l1-contracts/src/core/Rollup.sol @@ -2,7 +2,17 @@ // Copyright 2024 Aztec Labs. pragma solidity >=0.8.27; -import {IRollup, ChainTips, PublicInputArgs} from "@aztec/core/interfaces/IRollup.sol"; +import { + IRollup, + ChainTips, + PublicInputArgs, + L1FeeData, + ManaBaseFeeComponents, + FeeAssetPerEthE9, + EpochRewards, + BlockLog, + BlockHeaderValidationFlags +} from "@aztec/core/interfaces/IRollup.sol"; import { IStaking, ValidatorInfo, @@ -11,12 +21,11 @@ import { EnumerableSet } from "@aztec/core/interfaces/IStaking.sol"; import {IValidatorSelection} from "@aztec/core/interfaces/IValidatorSelection.sol"; -import {DataStructures} from "@aztec/core/libraries/DataStructures.sol"; -import {FeeAssetValue} from "@aztec/core/libraries/RollupLibs/FeeMath.sol"; -import {FeeMath} from "@aztec/core/libraries/RollupLibs/FeeMath.sol"; -import {HeaderLib} from "@aztec/core/libraries/RollupLibs/HeaderLib.sol"; -import {EpochProofLib} from "./libraries/RollupLibs/EpochProofLib.sol"; -import {ValidatorSelectionLib} from "./libraries/ValidatorSelectionLib/ValidatorSelectionLib.sol"; +import {FeeMath, FeeAssetValue, PriceLib} from "@aztec/core/libraries/rollup/FeeMath.sol"; +import {HeaderLib} from "@aztec/core/libraries/rollup/HeaderLib.sol"; +import {EpochProofLib} from "./libraries/rollup/EpochProofLib.sol"; +import {ProposeLib, ValidateHeaderArgs} from "./libraries/rollup/ProposeLib.sol"; +import {ValidatorSelectionLib} from "./libraries/validator-selection/ValidatorSelectionLib.sol"; import { RollupCore, RollupConfig, @@ -24,7 +33,6 @@ import { IRewardDistributor, IFeeJuicePortal, IERC20, - BlockLog, StakingLib, TimeLib, Slot, @@ -34,13 +42,10 @@ import { Signature, ExtRollupLib, EthValue, - PriceLib, STFLib, RollupStore, IInbox, - IOutbox, - ProposeLib, - EpochRewards + IOutbox } from "./RollupCore.sol"; /** @@ -185,16 +190,18 @@ contract Rollup is IStaking, IValidatorSelection, IRollup, RollupCore { bytes32 _digest, Timestamp _currentTime, bytes32 _blobsHash, - DataStructures.ExecutionFlags memory _flags + BlockHeaderValidationFlags memory _flags ) external view override(IRollup) { ProposeLib.validateHeader( - HeaderLib.decode(_header), - _signatures, - _digest, - _currentTime, - getManaBaseFeeAt(_currentTime, true), - _blobsHash, - _flags + ValidateHeaderArgs({ + header: HeaderLib.decode(_header), + attestations: _signatures, + digest: _digest, + currentTime: _currentTime, + manaBaseFee: getManaBaseFeeAt(_currentTime, true), + blobsHashesCommitment: _blobsHash, + flags: _flags + }) ); } @@ -509,10 +516,6 @@ contract Rollup is IStaking, IValidatorSelection, IRollup, RollupCore { return STFLib.getStorage().provingCostPerMana.toFeeAsset(getFeeAssetPerEth()); } - function getCuauhxicalli() external view override(IRollup) returns (address) { - return EpochProofLib.CUAUHXICALLI; - } - function getVersion() external view override(IRollup) returns (uint256) { return STFLib.getStorage().config.version; } @@ -556,8 +559,9 @@ contract Rollup is IStaking, IValidatorSelection, IRollup, RollupCore { RollupStore storage rollupStore = STFLib.getStorage(); // Consider if a prune will hit in this slot - uint256 pendingBlockNumber = - canPruneAtTime(_ts) ? rollupStore.tips.provenBlockNumber : rollupStore.tips.pendingBlockNumber; + uint256 pendingBlockNumber = STFLib.canPruneAtTime(_ts) + ? rollupStore.tips.provenBlockNumber + : rollupStore.tips.pendingBlockNumber; { Slot lastSlot = rollupStore.blocks[pendingBlockNumber].slotNumber; @@ -570,17 +574,36 @@ contract Rollup is IStaking, IValidatorSelection, IRollup, RollupCore { } Signature[] memory sigs = new Signature[](0); - DataStructures.ExecutionFlags memory flags = - DataStructures.ExecutionFlags({ignoreDA: true, ignoreSignatures: true}); - Epoch currentEpoch = slot.epochFromSlot(); - ValidatorSelectionLib.validateValidatorSelection( - StakingLib.getStorage(), slot, currentEpoch, sigs, _archive, flags + ValidatorSelectionLib.verify( + StakingLib.getStorage(), + slot, + slot.epochFromSlot(), + sigs, + _archive, + BlockHeaderValidationFlags({ignoreDA: true, ignoreSignatures: true}) ); return (slot, pendingBlockNumber + 1); } + function getL1FeesAt(Timestamp _timestamp) + external + view + override(IRollup) + returns (L1FeeData memory) + { + return ProposeLib.getL1FeesAt(_timestamp); + } + + function canPruneAtTime(Timestamp _ts) external view override(IRollup) returns (bool) { + return STFLib.canPruneAtTime(_ts); + } + + function getBurnAddress() external pure override(IRollup) returns (address) { + return EpochProofLib.BURN_ADDRESS; + } + /** * @notice Gets the mana base fee * @@ -597,6 +620,28 @@ contract Rollup is IStaking, IValidatorSelection, IRollup, RollupCore { return FeeMath.summedBaseFee(getManaBaseFeeComponentsAt(_timestamp, _inFeeAsset)); } + function getManaBaseFeeComponentsAt(Timestamp _timestamp, bool _inFeeAsset) + public + view + override(IRollup) + returns (ManaBaseFeeComponents memory) + { + return ProposeLib.getManaBaseFeeComponentsAt(_timestamp, _inFeeAsset); + } + + /** + * @notice Gets the fee asset price as fee_asset / eth with 1e9 precision + * + * @return The fee asset price + */ + function getFeeAssetPerEth() public view override(IRollup) returns (FeeAssetPerEthE9) { + return ProposeLib.getFeeAssetPerEth(); + } + + function getEpochForBlock(uint256 _blockNumber) public view override(IRollup) returns (Epoch) { + return STFLib.getEpochForBlock(_blockNumber); + } + /** * @notice Get the archive root of a specific block * diff --git a/l1-contracts/src/core/RollupCore.sol b/l1-contracts/src/core/RollupCore.sol index 44d723d97a3f..9ac1f90febb8 100644 --- a/l1-contracts/src/core/RollupCore.sol +++ b/l1-contracts/src/core/RollupCore.sol @@ -7,27 +7,24 @@ import { IRollupCore, ITestRollup, CheatDepositArgs, - FeeHeader, - ManaBaseFeeComponents, - BlockLog, RollupStore, L1GasOracleValues, L1FeeData, - SubmitEpochRootProofArgs, - EpochRewards + SubmitEpochRootProofArgs } from "@aztec/core/interfaces/IRollup.sol"; import {IStakingCore} from "@aztec/core/interfaces/IStaking.sol"; import {IValidatorSelectionCore} from "@aztec/core/interfaces/IValidatorSelection.sol"; -import {IVerifier} from "@aztec/core/interfaces/IVerifier.sol"; import {IInbox} from "@aztec/core/interfaces/messagebridge/IInbox.sol"; import {IOutbox} from "@aztec/core/interfaces/messagebridge/IOutbox.sol"; import {Constants} from "@aztec/core/libraries/ConstantsGen.sol"; import {Signature} from "@aztec/core/libraries/crypto/SignatureLib.sol"; import {Errors} from "@aztec/core/libraries/Errors.sol"; -import {STFLib} from "@aztec/core/libraries/RollupLibs/core/STFLib.sol"; -import {ExtRollupLib} from "@aztec/core/libraries/RollupLibs/ExtRollupLib.sol"; -import {EthValue, FeeAssetPerEthE9, PriceLib} from "@aztec/core/libraries/RollupLibs/FeeMath.sol"; -import {ProposeArgs, ProposeLib} from "@aztec/core/libraries/RollupLibs/ProposeLib.sol"; +import {CheatLib} from "@aztec/core/libraries/rollup/CheatLib.sol"; +import {ExtRollupLib} from "@aztec/core/libraries/rollup/ExtRollupLib.sol"; +import {EthValue} from "@aztec/core/libraries/rollup/FeeMath.sol"; +import {ProposeArgs, ProposeLib} from "@aztec/core/libraries/rollup/ProposeLib.sol"; +import {RewardLib} from "@aztec/core/libraries/rollup/RewardLib.sol"; +import {STFLib, GenesisState} from "@aztec/core/libraries/rollup/STFLib.sol"; import {StakingLib} from "@aztec/core/libraries/staking/StakingLib.sol"; import {Timestamp, Slot, Epoch, TimeLib} from "@aztec/core/libraries/TimeLib.sol"; import {Inbox} from "@aztec/core/messagebridge/Inbox.sol"; @@ -49,13 +46,6 @@ struct RollupConfig { uint256 slashingRoundSize; } -struct GenesisState { - bytes32 vkTreeRoot; - bytes32 protocolContractTreeRoot; - bytes32 genesisArchiveRoot; - bytes32 genesisBlockHash; -} - /** * @title Rollup * @author Aztec Labs @@ -73,8 +63,6 @@ contract RollupCore is { using ProposeLib for ProposeArgs; - using PriceLib for EthValue; - using TimeLib for Timestamp; using TimeLib for Slot; using TimeLib for Epoch; @@ -104,36 +92,20 @@ contract RollupCore is L1_BLOCK_AT_GENESIS = block.number; + STFLib.initialize(_genesisState); RollupStore storage rollupStore = STFLib.getStorage(); rollupStore.config.proofSubmissionWindow = _config.aztecProofSubmissionWindow; rollupStore.config.feeAsset = _fpcJuicePortal.UNDERLYING(); rollupStore.config.feeAssetPortal = _fpcJuicePortal; rollupStore.config.rewardDistributor = _rewardDistributor; - rollupStore.config.epochProofVerifier = new MockVerifier(); - rollupStore.config.vkTreeRoot = _genesisState.vkTreeRoot; - rollupStore.config.protocolContractTreeRoot = _genesisState.protocolContractTreeRoot; rollupStore.config.version = 1; - rollupStore.config.inbox = IInbox(address(new Inbox(address(this), Constants.L1_TO_L2_MSG_SUBTREE_HEIGHT))); rollupStore.config.outbox = IOutbox(address(new Outbox(address(this)))); - rollupStore.provingCostPerMana = EthValue.wrap(100); - // Genesis block - rollupStore.blocks[0] = BlockLog({ - feeHeader: FeeHeader({ - excessMana: 0, - feeAssetPriceNumerator: 0, - manaUsed: 0, - congestionCost: 0, - provingCost: 0 - }), - archive: _genesisState.genesisArchiveRoot, - blockHash: _genesisState.genesisBlockHash, - slotNumber: Slot.wrap(0) - }); + rollupStore.provingCostPerMana = EthValue.wrap(100); rollupStore.l1GasOracleValues = L1GasOracleValues({ pre: L1FeeData({baseFee: 1 gwei, blobFee: 1}), post: L1FeeData({baseFee: block.basefee, blobFee: ExtRollupLib.getBlobBaseFee()}), @@ -141,6 +113,39 @@ contract RollupCore is }); } + /* -------------------------------------------------------------------------- */ + /* CHEAT CODES START HERE */ + /* -------------------------------------------------------------------------- */ + + function cheat__InitialiseValidatorSet(CheatDepositArgs[] memory _args) + external + override(ITestRollup) + onlyOwner + { + CheatLib.cheat__InitialiseValidatorSet(_args); + setupEpoch(); + } + + function setEpochVerifier(address _verifier) external override(ITestRollup) onlyOwner { + CheatLib.setEpochVerifier(_verifier); + } + + function setVkTreeRoot(bytes32 _vkTreeRoot) external override(ITestRollup) onlyOwner { + CheatLib.setVkTreeRoot(_vkTreeRoot); + } + + function setProtocolContractTreeRoot(bytes32 _protocolContractTreeRoot) + external + override(ITestRollup) + onlyOwner + { + CheatLib.setProtocolContractTreeRoot(_protocolContractTreeRoot); + } + + /* -------------------------------------------------------------------------- */ + /* CHEAT CODES END HERE */ + /* -------------------------------------------------------------------------- */ + function setProvingCostPerMana(EthValue _provingCostPerMana) external override(IRollupCore) @@ -154,12 +159,7 @@ contract RollupCore is override(IRollupCore) returns (uint256) { - RollupStore storage rollupStore = STFLib.getStorage(); - uint256 amount = rollupStore.sequencerRewards[msg.sender]; - rollupStore.sequencerRewards[msg.sender] = 0; - rollupStore.config.feeAsset.transfer(_recipient, amount); - - return amount; + return RewardLib.claimSequencerRewards(_recipient); } function claimProverRewards(address _recipient, Epoch[] memory _epochs) @@ -167,31 +167,7 @@ contract RollupCore is override(IRollupCore) returns (uint256) { - Slot currentSlot = Timestamp.wrap(block.timestamp).slotFromTimestamp(); - RollupStore storage rollupStore = STFLib.getStorage(); - uint256 proofSubmissionWindow = rollupStore.config.proofSubmissionWindow; - - uint256 accumulatedRewards = 0; - for (uint256 i = 0; i < _epochs.length; i++) { - Slot deadline = _epochs[i].toSlots() + Slot.wrap(proofSubmissionWindow); - 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); - } - } - - rollupStore.config.feeAsset.transfer(_recipient, accumulatedRewards); - - return accumulatedRewards; + return RewardLib.claimProverRewards(_recipient, _epochs); } function deposit(address _attester, address _proposer, address _withdrawer, uint256 _amount) @@ -207,8 +183,6 @@ contract RollupCore is override(IStakingCore) returns (bool) { - // @note The attester might be chosen for the epoch, so the delay must be long enough - // to allow for that. setupEpoch(); return StakingLib.initiateWithdraw(_attester, _recipient); } @@ -221,86 +195,11 @@ contract RollupCore is StakingLib.slash(_attester, _amount); } - function cheat__InitialiseValidatorSet(CheatDepositArgs[] memory _args) - external - override(ITestRollup) - onlyOwner - { - for (uint256 i = 0; i < _args.length; i++) { - StakingLib.deposit(_args[i].attester, _args[i].proposer, _args[i].withdrawer, _args[i].amount); - } - setupEpoch(); - } - - /** - * @notice Prune the pending chain up to the last proven block - * - * @dev Will revert if there is nothing to prune or if the chain is not ready to be pruned - */ function prune() external override(IRollupCore) { - require(canPrune(), Errors.Rollup__NothingToPrune()); + require(STFLib.canPruneAtTime(Timestamp.wrap(block.timestamp)), Errors.Rollup__NothingToPrune()); STFLib.prune(); } - /** - * @notice Set the verifier contract - * - * @dev This is only needed for testing, and should be removed - * - * @param _verifier - The new verifier contract - */ - function setEpochVerifier(address _verifier) external override(ITestRollup) onlyOwner { - STFLib.getStorage().config.epochProofVerifier = IVerifier(_verifier); - } - - /** - * @notice Set the vkTreeRoot - * - * @dev This is only needed for testing, and should be removed - * - * @param _vkTreeRoot - The new vkTreeRoot to be used by proofs - */ - function setVkTreeRoot(bytes32 _vkTreeRoot) external override(ITestRollup) onlyOwner { - STFLib.getStorage().config.vkTreeRoot = _vkTreeRoot; - } - - /** - * @notice Set the protocolContractTreeRoot - * - * @dev This is only needed for testing, and should be removed - * - * @param _protocolContractTreeRoot - The new protocolContractTreeRoot to be used by proofs - */ - function setProtocolContractTreeRoot(bytes32 _protocolContractTreeRoot) - external - override(ITestRollup) - onlyOwner - { - STFLib.getStorage().config.protocolContractTreeRoot = _protocolContractTreeRoot; - } - - /** - * @notice Submit a proof for an epoch in the pending chain - * - * @dev Will emit `L2ProofVerified` if the proof is valid - * - * @dev Will throw if: - * - The block number is past the pending chain - * - The last archive root of the header does not match the archive root of parent block - * - The archive root of the header does not match the archive root of the proposed block - * - The proof is invalid - * - * @dev We provide the `_archive` and `_blockHash` even if it could be read from storage itself because it allow for - * better error messages. Without passing it, we would just have a proof verification failure. - * - * @param _args - The arguments to submit the epoch root proof: - * _epochSize - The size of the epoch (to be promoted to a constant) - * _args - Array of public inputs to the proof (previousArchive, endArchive, previousBlockHash, endBlockHash, endTimestamp, outHash, proverId) - * _fees - Array of recipient-value pairs with fees to be distributed for the epoch - * _blobPublicInputs - The blob public inputs for the proof - * _aggregationObject - The aggregation object for the proof - * _proof - The proof to verify - */ function submitEpochRootProof(SubmitEpochRootProofArgs calldata _args) external override(IRollupCore) @@ -308,14 +207,6 @@ contract RollupCore is ExtRollupLib.submitEpochRootProof(_args); } - /** - * @notice Publishes the body and propose the block - * @dev `eth_log_handlers` rely on this function - * - * @param _args - The arguments to propose the block - * @param _signatures - Signatures from the validators - * @param _blobInput - The blob evaluation KZG proof, challenge, and opening required for the precompile. - */ function propose( ProposeArgs calldata _args, Signature[] memory _signatures, @@ -335,55 +226,4 @@ contract RollupCore is function updateL1GasFeeOracle() public override(IRollupCore) { ProposeLib.updateL1GasFeeOracle(); } - - /** - * @notice Gets the fee asset price as fee_asset / eth with 1e9 precision - * - * @return The fee asset price - */ - function getFeeAssetPerEth() public view override(IRollupCore) returns (FeeAssetPerEthE9) { - return ProposeLib.getFeeAssetPerEth(); - } - - function getL1FeesAt(Timestamp _timestamp) - public - view - override(IRollupCore) - returns (L1FeeData memory) - { - return ProposeLib.getL1FeesAt(_timestamp); - } - - /** - * @notice Gets the mana base fee components - * For more context, consult: - * https://github.com/AztecProtocol/engineering-designs/blob/main/in-progress/8757-fees/design.md - * - * @dev TODO #10004 - As part of the refactor, will likely get rid of this function or make it private - * keeping it public for now makes it simpler to test. - * - * @param _inFeeAsset - Whether to return the fee in the fee asset or ETH - * - * @return The mana base fee components - */ - function getManaBaseFeeComponentsAt(Timestamp _timestamp, bool _inFeeAsset) - public - view - override(ITestRollup) - returns (ManaBaseFeeComponents memory) - { - return ProposeLib.getManaBaseFeeComponentsAt(_timestamp, _inFeeAsset); - } - - function getEpochForBlock(uint256 _blockNumber) public view override(IRollupCore) returns (Epoch) { - return STFLib.getEpochForBlock(_blockNumber); - } - - function canPrune() public view override(IRollupCore) returns (bool) { - return canPruneAtTime(Timestamp.wrap(block.timestamp)); - } - - function canPruneAtTime(Timestamp _ts) public view override(IRollupCore) returns (bool) { - return STFLib.canPruneAtTime(_ts); - } } diff --git a/l1-contracts/src/core/interfaces/IRollup.sol b/l1-contracts/src/core/interfaces/IRollup.sol index 4f4d65af631b..ee71647130ee 100644 --- a/l1-contracts/src/core/interfaces/IRollup.sol +++ b/l1-contracts/src/core/interfaces/IRollup.sol @@ -7,14 +7,11 @@ import {IVerifier} from "@aztec/core/interfaces/IVerifier.sol"; import {IInbox} from "@aztec/core/interfaces/messagebridge/IInbox.sol"; import {IOutbox} from "@aztec/core/interfaces/messagebridge/IOutbox.sol"; import {Signature} from "@aztec/core/libraries/crypto/SignatureLib.sol"; -import {DataStructures} from "@aztec/core/libraries/DataStructures.sol"; import { FeeHeader, L1FeeData, ManaBaseFeeComponents -} from "@aztec/core/libraries/RollupLibs/FeeMath.sol"; -import { - FeeAssetPerEthE9, EthValue, FeeAssetValue -} from "@aztec/core/libraries/RollupLibs/FeeMath.sol"; -import {ProposeArgs} from "@aztec/core/libraries/RollupLibs/ProposeLib.sol"; +} from "@aztec/core/libraries/rollup/FeeMath.sol"; +import {FeeAssetPerEthE9, EthValue, FeeAssetValue} from "@aztec/core/libraries/rollup/FeeMath.sol"; +import {ProposeArgs} from "@aztec/core/libraries/rollup/ProposeLib.sol"; import {Timestamp, Slot, Epoch} from "@aztec/core/libraries/TimeLib.sol"; import {IRewardDistributor} from "@aztec/governance/interfaces/IRewardDistributor.sol"; import {IERC20} from "@oz/token/ERC20/IERC20.sol"; @@ -68,6 +65,16 @@ struct EpochRewards { mapping(uint256 length => SubEpochRewards) subEpoch; } +/** + * @notice Struct for storing flags for block header validation + * @param ignoreDA - True will ignore DA check, otherwise checks + * @param ignoreSignature - True will ignore the signatures, otherwise checks + */ +struct BlockHeaderValidationFlags { + bool ignoreDA; + bool ignoreSignatures; +} + // @todo Ideally we should pull these from the code for immutable values // to save gas. Consider using constants or more fancy deployments. struct RollupConfig { @@ -111,10 +118,6 @@ interface ITestRollup { function setVkTreeRoot(bytes32 _vkTreeRoot) external; function setProtocolContractTreeRoot(bytes32 _protocolContractTreeRoot) external; function cheat__InitialiseValidatorSet(CheatDepositArgs[] memory _args) external; - function getManaBaseFeeComponentsAt(Timestamp _timestamp, bool _inFeeAsset) - external - view - returns (ManaBaseFeeComponents memory); } interface IRollupCore { @@ -144,14 +147,6 @@ interface IRollupCore { // solhint-disable-next-line func-name-mixedcase function L1_BLOCK_AT_GENESIS() external view returns (uint256); - - function getFeeAssetPerEth() external view returns (FeeAssetPerEthE9); - function getL1FeesAt(Timestamp _timestamp) external view returns (L1FeeData memory); - - function canPrune() external view returns (bool); - function canPruneAtTime(Timestamp _ts) external view returns (bool); - - function getEpochForBlock(uint256 _blockNumber) external view returns (Epoch); } interface IRollup is IRollupCore { @@ -184,7 +179,7 @@ interface IRollup is IRollupCore { bytes32 _digest, Timestamp _currentTime, bytes32 _blobsHash, - DataStructures.ExecutionFlags memory _flags + BlockHeaderValidationFlags memory _flags ) external view; function canProposeAtTime(Timestamp _ts, bytes32 _archive) external view returns (Slot, uint256); @@ -194,7 +189,16 @@ interface IRollup is IRollupCore { view returns (bytes32[] memory, bytes32, bytes32); + function getManaBaseFeeComponentsAt(Timestamp _timestamp, bool _inFeeAsset) + external + view + returns (ManaBaseFeeComponents memory); function getManaBaseFeeAt(Timestamp _timestamp, bool _inFeeAsset) external view returns (uint256); + function getL1FeesAt(Timestamp _timestamp) external view returns (L1FeeData memory); + function getFeeAssetPerEth() external view returns (FeeAssetPerEthE9); + + function getEpochForBlock(uint256 _blockNumber) external view returns (Epoch); + function canPruneAtTime(Timestamp _ts) external view returns (bool); function archive() external view returns (bytes32); function archiveAt(uint256 _blockNumber) external view returns (bytes32); @@ -223,7 +227,7 @@ interface IRollup is IRollupCore { function getFeeAsset() external view returns (IERC20); function getFeeAssetPortal() external view returns (IFeeJuicePortal); function getRewardDistributor() external view returns (IRewardDistributor); - function getCuauhxicalli() external view returns (address); + function getBurnAddress() external view returns (address); function getInbox() external view returns (IInbox); function getOutbox() external view returns (IOutbox); diff --git a/l1-contracts/src/core/libraries/Converter.sol b/l1-contracts/src/core/libraries/Converter.sol deleted file mode 100644 index 598f27f5063c..000000000000 --- a/l1-contracts/src/core/libraries/Converter.sol +++ /dev/null @@ -1,13 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// Copyright 2024 Aztec Labs. -pragma solidity >=0.8.27; - -library Converter { - function addressToField(address _a) internal pure returns (bytes32) { - return bytes32(uint256(uint160(_a))); - } - - function fieldToAddress(bytes32 _f) internal pure returns (address) { - return address(uint160(uint256(_f))); - } -} diff --git a/l1-contracts/src/core/libraries/DataStructures.sol b/l1-contracts/src/core/libraries/DataStructures.sol index 88bac5713722..86f2f0da59f9 100644 --- a/l1-contracts/src/core/libraries/DataStructures.sol +++ b/l1-contracts/src/core/libraries/DataStructures.sol @@ -64,14 +64,4 @@ library DataStructures { bytes32 content; } // docs:end:l2_to_l1_msg - - /** - * @notice Struct for storing flags for block header validation - * @param ignoreDA - True will ignore DA check, otherwise checks - * @param ignoreSignature - True will ignore the signatures, otherwise checks - */ - struct ExecutionFlags { - bool ignoreDA; - bool ignoreSignatures; - } } diff --git a/l1-contracts/src/core/libraries/Errors.sol b/l1-contracts/src/core/libraries/Errors.sol index f873d0ace2bd..8f6e9e6ba56a 100644 --- a/l1-contracts/src/core/libraries/Errors.sol +++ b/l1-contracts/src/core/libraries/Errors.sol @@ -53,7 +53,6 @@ library Errors { error Rollup__InvalidBlockHash(bytes32 expected, bytes32 actual); error Rollup__InvalidBlockNumber(uint256 expected, uint256 actual); // 0xe5edf847 error Rollup__InvalidChainId(uint256 expected, uint256 actual); // 0x37b5bc12 - error Rollup__InvalidEpoch(Epoch expected, Epoch actual); // 0x3c6d65e6 error Rollup__InvalidInHash(bytes32 expected, bytes32 actual); // 0xcd6f4233 error Rollup__InvalidPreviousArchive(bytes32 expected, bytes32 actual); // 0xb682a40e error Rollup__InvalidPreviousBlockHash(bytes32 expected, bytes32 actual); diff --git a/l1-contracts/src/core/libraries/RollupLibs/BlobLib.sol b/l1-contracts/src/core/libraries/rollup/BlobLib.sol similarity index 100% rename from l1-contracts/src/core/libraries/RollupLibs/BlobLib.sol rename to l1-contracts/src/core/libraries/rollup/BlobLib.sol diff --git a/l1-contracts/src/core/libraries/rollup/CheatLib.sol b/l1-contracts/src/core/libraries/rollup/CheatLib.sol new file mode 100644 index 000000000000..8aeeca1a9926 --- /dev/null +++ b/l1-contracts/src/core/libraries/rollup/CheatLib.sol @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2024 Aztec Labs. +pragma solidity >=0.8.27; + +import {CheatDepositArgs} from "@aztec/core/interfaces/IRollup.sol"; +import {IVerifier} from "@aztec/core/interfaces/IVerifier.sol"; +import {STFLib} from "@aztec/core/libraries/rollup/STFLib.sol"; +import {StakingLib} from "@aztec/core/libraries/staking/StakingLib.sol"; + +/** + * @title CheatLib + * @author Aztec Labs + * @notice A library of cheat codes for the RollupCore + * Should be nuked from orbit. + */ +library CheatLib { + function cheat__InitialiseValidatorSet(CheatDepositArgs[] memory _args) internal { + for (uint256 i = 0; i < _args.length; i++) { + StakingLib.deposit(_args[i].attester, _args[i].proposer, _args[i].withdrawer, _args[i].amount); + } + } + + function setEpochVerifier(address _verifier) internal { + STFLib.getStorage().config.epochProofVerifier = IVerifier(_verifier); + } + + function setVkTreeRoot(bytes32 _vkTreeRoot) internal { + STFLib.getStorage().config.vkTreeRoot = _vkTreeRoot; + } + + function setProtocolContractTreeRoot(bytes32 _protocolContractTreeRoot) internal { + STFLib.getStorage().config.protocolContractTreeRoot = _protocolContractTreeRoot; + } +} diff --git a/l1-contracts/src/core/libraries/RollupLibs/EpochProofLib.sol b/l1-contracts/src/core/libraries/rollup/EpochProofLib.sol similarity index 88% rename from l1-contracts/src/core/libraries/RollupLibs/EpochProofLib.sol rename to l1-contracts/src/core/libraries/rollup/EpochProofLib.sol index 218bcee2b97e..80e0c2467cfa 100644 --- a/l1-contracts/src/core/libraries/RollupLibs/EpochProofLib.sol +++ b/l1-contracts/src/core/libraries/rollup/EpochProofLib.sol @@ -11,11 +11,9 @@ import { } from "@aztec/core/interfaces/IRollup.sol"; import {RollupStore, SubmitEpochRootProofArgs} from "@aztec/core/interfaces/IRollup.sol"; import {Constants} from "@aztec/core/libraries/ConstantsGen.sol"; -import {Converter} from "@aztec/core/libraries/Converter.sol"; import {Errors} from "@aztec/core/libraries/Errors.sol"; -import {Errors} from "@aztec/core/libraries/Errors.sol"; -import {STFLib, RollupStore} from "@aztec/core/libraries/RollupLibs/core/STFLib.sol"; -import {FeeHeader} from "@aztec/core/libraries/RollupLibs/FeeMath.sol"; +import {FeeHeader} from "@aztec/core/libraries/rollup/FeeMath.sol"; +import {STFLib, RollupStore} from "@aztec/core/libraries/rollup/STFLib.sol"; import {Timestamp, Slot, Epoch, TimeLib} from "@aztec/core/libraries/TimeLib.sol"; import {Epoch} from "@aztec/core/libraries/TimeLib.sol"; import {IERC20} from "@oz/token/ERC20/IERC20.sol"; @@ -43,8 +41,30 @@ library EpochProofLib { // A Cuauhxicalli [kʷaːʍʃiˈkalːi] ("eagle gourd bowl") is a ceremonial Aztec vessel or altar used to hold offerings, // such as sacrificial hearts, during rituals performed within temples. - address public constant CUAUHXICALLI = address(bytes20("CUAUHXICALLI")); + address public constant BURN_ADDRESS = address(bytes20("CUAUHXICALLI")); + /** + * @notice Submit a proof for an epoch in the pending chain + * + * @dev Will emit `L2ProofVerified` if the proof is valid + * + * @dev Will throw if: + * - The block number is past the pending chain + * - The last archive root of the header does not match the archive root of parent block + * - The archive root of the header does not match the archive root of the proposed block + * - The proof is invalid + * + * @dev We provide the `_archive` and `_blockHash` even if it could be read from storage itself because it allow for + * better error messages. Without passing it, we would just have a proof verification failure. + * + * @param _args - The arguments to submit the epoch root proof: + * _epochSize - The size of the epoch (to be promoted to a constant) + * _args - Array of public inputs to the proof (previousArchive, endArchive, previousBlockHash, endBlockHash, endTimestamp, outHash, proverId) + * _fees - Array of recipient-value pairs with fees to be distributed for the epoch + * _blobPublicInputs - The blob public inputs for the proof + * _aggregationObject - The aggregation object for the proof + * _proof - The proof to verify + */ function submitEpochRootProof(SubmitEpochRootProofArgs calldata _args) internal { if (STFLib.canPruneAtTime(Timestamp.wrap(block.timestamp))) { STFLib.prune(); @@ -200,7 +220,7 @@ library EpochProofLib { offset += 1; // prover_id: id of current epoch's prover - publicInputs[offset] = Converter.addressToField(_args.proverId); + publicInputs[offset] = addressToField(_args.proverId); offset += 1; { @@ -301,7 +321,7 @@ library EpochProofLib { v.sequencerFee = fee - burn - v.proverFee; { - v.sequencer = Converter.fieldToAddress(_args.fees[i * 2]); + v.sequencer = fieldToAddress(_args.fees[i * 2]); rollupStore.sequencerRewards[v.sequencer] += (v.sequencerBlockReward + v.sequencerFee); } } @@ -313,7 +333,7 @@ library EpochProofLib { } if (t.totalBurn > 0) { - rollupStore.config.feeAsset.transfer(CUAUHXICALLI, t.totalBurn); + rollupStore.config.feeAsset.transfer(BURN_ADDRESS, t.totalBurn); } } } @@ -428,4 +448,12 @@ library EpochProofLib { secondLimb = bytes32(uint256(uint120(bytes15(_input << 16)))); thirdLimb = bytes32(uint256(uint16(bytes2(_input)))); } + + function addressToField(address _a) private pure returns (bytes32) { + return bytes32(uint256(uint160(_a))); + } + + function fieldToAddress(bytes32 _f) private pure returns (address) { + return address(uint160(uint256(_f))); + } } diff --git a/l1-contracts/src/core/libraries/RollupLibs/ExtRollupLib.sol b/l1-contracts/src/core/libraries/rollup/ExtRollupLib.sol similarity index 95% rename from l1-contracts/src/core/libraries/RollupLibs/ExtRollupLib.sol rename to l1-contracts/src/core/libraries/rollup/ExtRollupLib.sol index c4fbd9d579ed..bf6655a474dc 100644 --- a/l1-contracts/src/core/libraries/RollupLibs/ExtRollupLib.sol +++ b/l1-contracts/src/core/libraries/rollup/ExtRollupLib.sol @@ -4,13 +4,13 @@ pragma solidity >=0.8.27; import {SubmitEpochRootProofArgs, PublicInputArgs} from "@aztec/core/interfaces/IRollup.sol"; import {StakingLib} from "./../staking/StakingLib.sol"; -import {ValidatorSelectionLib} from "./../ValidatorSelectionLib/ValidatorSelectionLib.sol"; +import {ValidatorSelectionLib} from "./../validator-selection/ValidatorSelectionLib.sol"; import {BlobLib} from "./BlobLib.sol"; import {EpochProofLib} from "./EpochProofLib.sol"; import {ProposeLib, ProposeArgs, Signature} from "./ProposeLib.sol"; + // We are using this library such that we can more easily "link" just a larger external library // instead of a few smaller ones. - library ExtRollupLib { function submitEpochRootProof(SubmitEpochRootProofArgs calldata _args) external { EpochProofLib.submitEpochRootProof(_args); diff --git a/l1-contracts/src/core/libraries/RollupLibs/FeeMath.sol b/l1-contracts/src/core/libraries/rollup/FeeMath.sol similarity index 100% rename from l1-contracts/src/core/libraries/RollupLibs/FeeMath.sol rename to l1-contracts/src/core/libraries/rollup/FeeMath.sol diff --git a/l1-contracts/src/core/libraries/RollupLibs/HeaderLib.sol b/l1-contracts/src/core/libraries/rollup/HeaderLib.sol similarity index 96% rename from l1-contracts/src/core/libraries/RollupLibs/HeaderLib.sol rename to l1-contracts/src/core/libraries/rollup/HeaderLib.sol index 4749622b9923..5db6291fc0a5 100644 --- a/l1-contracts/src/core/libraries/RollupLibs/HeaderLib.sol +++ b/l1-contracts/src/core/libraries/rollup/HeaderLib.sol @@ -5,6 +5,8 @@ pragma solidity >=0.8.27; import {Constants} from "@aztec/core/libraries/ConstantsGen.sol"; import {Errors} from "@aztec/core/libraries/Errors.sol"; +import {Slot, Timestamp} from "@aztec/core/libraries/TimeLib.sol"; + struct AppendOnlyTreeSnapshot { bytes32 root; uint32 nextAvailableLeafIndex; @@ -32,8 +34,8 @@ struct GlobalVariables { uint256 chainId; uint256 version; uint256 blockNumber; - uint256 slotNumber; - uint256 timestamp; + Slot slotNumber; + Timestamp timestamp; address coinbase; bytes32 feeRecipient; GasFees gasFees; @@ -150,8 +152,8 @@ library HeaderLib { header.globalVariables.chainId = uint256(bytes32(_header[0x0134:0x0154])); header.globalVariables.version = uint256(bytes32(_header[0x0154:0x0174])); header.globalVariables.blockNumber = uint256(bytes32(_header[0x0174:0x0194])); - header.globalVariables.slotNumber = uint256(bytes32(_header[0x0194:0x01b4])); - header.globalVariables.timestamp = uint256(bytes32(_header[0x01b4:0x01d4])); + header.globalVariables.slotNumber = Slot.wrap(uint256(bytes32(_header[0x0194:0x01b4]))); + header.globalVariables.timestamp = Timestamp.wrap(uint256(bytes32(_header[0x01b4:0x01d4]))); header.globalVariables.coinbase = address(bytes20(_header[0x01d4:0x01e8])); header.globalVariables.feeRecipient = bytes32(_header[0x01e8:0x0208]); header.globalVariables.gasFees.feePerDaGas = uint256(bytes32(_header[0x0208:0x0228])); diff --git a/l1-contracts/src/core/libraries/RollupLibs/ProposeLib.sol b/l1-contracts/src/core/libraries/rollup/ProposeLib.sol similarity index 54% rename from l1-contracts/src/core/libraries/RollupLibs/ProposeLib.sol rename to l1-contracts/src/core/libraries/rollup/ProposeLib.sol index 1f6c0a00b99d..8ab2ed47b5fa 100644 --- a/l1-contracts/src/core/libraries/RollupLibs/ProposeLib.sol +++ b/l1-contracts/src/core/libraries/rollup/ProposeLib.sol @@ -2,11 +2,15 @@ // Copyright 2024 Aztec Labs. pragma solidity >=0.8.27; -import {RollupStore, IRollupCore, BlockLog} from "@aztec/core/interfaces/IRollup.sol"; +import { + RollupStore, + IRollupCore, + BlockLog, + BlockHeaderValidationFlags +} from "@aztec/core/interfaces/IRollup.sol"; import {MerkleLib} from "@aztec/core/libraries/crypto/MerkleLib.sol"; import {SignatureLib} from "@aztec/core/libraries/crypto/SignatureLib.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 { OracleInput, @@ -15,14 +19,14 @@ import { L1FeeData, FeeAssetPerEthE9, FeeHeader -} from "@aztec/core/libraries/RollupLibs/FeeMath.sol"; +} from "@aztec/core/libraries/rollup/FeeMath.sol"; import {StakingLib} from "@aztec/core/libraries/staking/StakingLib.sol"; import {Timestamp, Slot, Epoch, TimeLib} from "@aztec/core/libraries/TimeLib.sol"; import {ValidatorSelectionLib} from - "@aztec/core/libraries/ValidatorSelectionLib/ValidatorSelectionLib.sol"; + "@aztec/core/libraries/validator-selection/ValidatorSelectionLib.sol"; import {BlobLib} from "./BlobLib.sol"; -import {STFLib} from "./core/STFLib.sol"; import {Header, HeaderLib} from "./HeaderLib.sol"; +import {STFLib} from "./STFLib.sol"; struct ProposeArgs { bytes32 archive; @@ -32,19 +36,30 @@ struct ProposeArgs { bytes32[] txHashes; } +struct InterimProposeValues { + bytes32[] blobHashes; + bytes32 blobsHashesCommitment; + bytes32 blobPublicInputsHash; + bytes32 inHash; + uint256 outboxMinsize; +} + +/** + * @param header - The proposed block header + * @param attestations - The signatures for the attestations + * @param digest - The digest that signatures signed + * @param currentTime - The time of execution + * @param blobsHashesCommitment - The blobs hash for this block, provided for simpler future simulation + * @param flags - Flags specific to the execution, whether certain checks should be skipped + */ struct ValidateHeaderArgs { Header header; + Signature[] attestations; + bytes32 digest; Timestamp currentTime; uint256 manaBaseFee; bytes32 blobsHashesCommitment; - uint256 pendingBlockNumber; - DataStructures.ExecutionFlags flags; -} - -struct InterimProposeValues { - bytes32[] blobHashes; - bytes32 blobsHashesCommitment; - bytes32 blobPublicInputsHash; + BlockHeaderValidationFlags flags; } library ProposeLib { @@ -71,6 +86,14 @@ library ProposeLib { rollupStore.l1GasOracleValues.slotOfChange = slot + LAG; } + /** + * @notice Publishes the body and propose the block + * @dev `eth_log_handlers` rely on this function + * + * @param _args - The arguments to propose the block + * @param _signatures - Signatures from the validators + * @param _blobInput - The blob evaluation KZG proof, challenge, and opening required for the precompile. + */ function propose( ProposeArgs calldata _args, Signature[] memory _signatures, @@ -95,44 +118,39 @@ library ProposeLib { ManaBaseFeeComponents memory components = getManaBaseFeeComponentsAt(Timestamp.wrap(block.timestamp), true); - { - uint256 manaBaseFee = FeeMath.summedBaseFee(components); - validateHeader({ - _header: header, - _signatures: _signatures, - _digest: digest(_args), - _currentTime: Timestamp.wrap(block.timestamp), - _manaBaseFee: manaBaseFee, - _blobsHashesCommitment: v.blobsHashesCommitment, - _flags: DataStructures.ExecutionFlags({ignoreDA: false, ignoreSignatures: false}) - }); - } + validateHeader( + ValidateHeaderArgs({ + header: header, + attestations: _signatures, + digest: digest(_args), + currentTime: Timestamp.wrap(block.timestamp), + manaBaseFee: FeeMath.summedBaseFee(components), + blobsHashesCommitment: v.blobsHashesCommitment, + flags: BlockHeaderValidationFlags({ignoreDA: false, ignoreSignatures: false}) + }) + ); RollupStore storage rollupStore = STFLib.getStorage(); uint256 blockNumber = ++rollupStore.tips.pendingBlockNumber; - { - // @note The components are measured in the fee asset. - rollupStore.blocks[blockNumber] = - toBlockLog(_args, blockNumber, components.congestionCost, components.provingCost); - } + rollupStore.blocks[blockNumber] = + toBlockLog(_args, header, blockNumber, components.congestionCost, components.provingCost); rollupStore.blobPublicInputsHashes[blockNumber] = v.blobPublicInputsHash; // @note The block number here will always be >=1 as the genesis block is at 0 - { - bytes32 inHash = rollupStore.config.inbox.consume(blockNumber); - require( - header.contentCommitment.inHash == inHash, - Errors.Rollup__InvalidInHash(inHash, header.contentCommitment.inHash) - ); - } - { - // TODO(#7218): Revert to fixed height tree for outbox, currently just providing min as interim - // Min size = smallest path of the rollup tree + 1 - (uint256 min,) = MerkleLib.computeMinMaxPathLength(header.contentCommitment.numTxs); - rollupStore.config.outbox.insert(blockNumber, header.contentCommitment.outHash, min + 1); - } + v.inHash = rollupStore.config.inbox.consume(blockNumber); + require( + header.contentCommitment.inHash == v.inHash, + Errors.Rollup__InvalidInHash(v.inHash, header.contentCommitment.inHash) + ); + + // TODO(#7218): Revert to fixed height tree for outbox, currently just providing min as interim + // Min size = smallest path of the rollup tree + 1 + (v.outboxMinsize,) = MerkleLib.computeMinMaxPathLength(header.contentCommitment.numTxs); + rollupStore.config.outbox.insert( + blockNumber, header.contentCommitment.outHash, v.outboxMinsize + 1 + ); emit IRollupCore.L2BlockProposed(blockNumber, _args.archive, v.blobHashes); } @@ -151,6 +169,16 @@ library ProposeLib { ); } + /** + * @notice Gets the mana base fee components + * For more context, consult: + * https://github.com/AztecProtocol/engineering-designs/blob/main/in-progress/8757-fees/design.md + * + * @param _timestamp - The timestamp of the block + * @param _inFeeAsset - Whether to return the fee in the fee asset or ETH + * + * @return The mana base fee components + */ function getManaBaseFeeComponentsAt(Timestamp _timestamp, bool _inFeeAsset) internal view @@ -185,57 +213,14 @@ library ProposeLib { ); } - /** - * @notice Validates the header for submission - * - * @param _header - The proposed block header - * @param _signatures - The signatures for the attestations - * @param _digest - The digest that signatures signed - * @param _currentTime - The time of execution - * @param _blobsHashesCommitment - The blobs hash for this block - * @dev - This value is provided to allow for simple simulation of future - * @param _flags - Flags specific to the execution, whether certain checks should be skipped - */ - function validateHeader( - Header memory _header, - Signature[] memory _signatures, - bytes32 _digest, - Timestamp _currentTime, - uint256 _manaBaseFee, - bytes32 _blobsHashesCommitment, - DataStructures.ExecutionFlags memory _flags - ) internal view { - RollupStore storage rollupStore = STFLib.getStorage(); - uint256 pendingBlockNumber = STFLib.canPruneAtTime(_currentTime) - ? rollupStore.tips.provenBlockNumber - : rollupStore.tips.pendingBlockNumber; - - validateHeaderForSubmissionBase( - ValidateHeaderArgs({ - header: _header, - currentTime: _currentTime, - manaBaseFee: _manaBaseFee, - blobsHashesCommitment: _blobsHashesCommitment, - pendingBlockNumber: pendingBlockNumber, - flags: _flags - }) - ); - validateHeaderForSubmissionSequencerSelection( - Slot.wrap(_header.globalVariables.slotNumber), _signatures, _digest, _currentTime, _flags - ); - } - - function digest(ProposeArgs memory _args) internal pure returns (bytes32) { - return keccak256(abi.encode(SignatureLib.SignatureDomainSeparator.blockAttestation, _args)); - } - - function validateHeaderForSubmissionBase(ValidateHeaderArgs memory _args) private view { - RollupStore storage rollupStore = STFLib.getStorage(); + function validateHeader(ValidateHeaderArgs memory _args) internal view { require( block.chainid == _args.header.globalVariables.chainId, Errors.Rollup__InvalidChainId(block.chainid, _args.header.globalVariables.chainId) ); + RollupStore storage rollupStore = STFLib.getStorage(); + require( _args.header.globalVariables.version == rollupStore.config.version, Errors.Rollup__InvalidVersion( @@ -243,42 +228,40 @@ library ProposeLib { ) ); + uint256 pendingBlockNumber = STFLib.canPruneAtTime(_args.currentTime) + ? rollupStore.tips.provenBlockNumber + : rollupStore.tips.pendingBlockNumber; + require( - _args.header.globalVariables.blockNumber == _args.pendingBlockNumber + 1, + _args.header.globalVariables.blockNumber == pendingBlockNumber + 1, Errors.Rollup__InvalidBlockNumber( - _args.pendingBlockNumber + 1, _args.header.globalVariables.blockNumber + pendingBlockNumber + 1, _args.header.globalVariables.blockNumber ) ); - bytes32 tipArchive = rollupStore.blocks[_args.pendingBlockNumber].archive; + bytes32 tipArchive = rollupStore.blocks[pendingBlockNumber].archive; require( tipArchive == _args.header.lastArchive.root, Errors.Rollup__InvalidArchive(tipArchive, _args.header.lastArchive.root) ); - Slot slot = Slot.wrap(_args.header.globalVariables.slotNumber); - Slot lastSlot = rollupStore.blocks[_args.pendingBlockNumber].slotNumber; + Slot slot = _args.header.globalVariables.slotNumber; + Slot lastSlot = rollupStore.blocks[pendingBlockNumber].slotNumber; require(slot > lastSlot, Errors.Rollup__SlotAlreadyInChain(lastSlot, slot)); + Slot currentSlot = _args.currentTime.slotFromTimestamp(); + require(slot == currentSlot, Errors.HeaderLib__InvalidSlotNumber(currentSlot, slot)); + Timestamp timestamp = TimeLib.toTimestamp(slot); require( - Timestamp.wrap(_args.header.globalVariables.timestamp) == timestamp, - Errors.Rollup__InvalidTimestamp( - timestamp, Timestamp.wrap(_args.header.globalVariables.timestamp) - ) + _args.header.globalVariables.timestamp == timestamp, + Errors.Rollup__InvalidTimestamp(timestamp, _args.header.globalVariables.timestamp) ); - // @note If you are hitting this error, it is likely because the chain you use have a blocktime that differs - // from the value that we have in the constants. - // When you are encountering this, it will likely be as the sequencer expects to be able to include - // an Aztec block in the "next" ethereum block based on a timestamp that is 12 seconds in the future - // from the last block. However, if the actual will only be 1 second in the future, you will end up - // expecting this value to be in the future. require( timestamp <= _args.currentTime, Errors.Rollup__TimestampInFuture(_args.currentTime, timestamp) ); - // Check if the data is available require( _args.flags.ignoreDA || _args.header.contentCommitment.blobsHash == _args.blobsHashesCommitment, @@ -292,68 +275,41 @@ library ProposeLib { _args.manaBaseFee, _args.header.globalVariables.gasFees.feePerL2Gas ) ); - } - /** - * @notice Validate a header for submission to the pending chain (sequencer selection checks) - * - * These validation checks are directly related to sequencer selection. - * Note that while these checks are strict, they can be relaxed with some changes to - * message boxes. - * - * Each of the following validation checks must pass, otherwise an error is thrown and we revert. - * - The slot MUST be the current slot - * This might be relaxed for allow consensus set to better handle short-term bursts of L1 congestion - * - The slot MUST be in the current epoch - * - * @param _slot - The slot of the header to validate - * @param _signatures - The signatures to validate - * @param _digest - The digest that signatures sign over - */ - function validateHeaderForSubmissionSequencerSelection( - Slot _slot, - Signature[] memory _signatures, - bytes32 _digest, - Timestamp _currentTime, - DataStructures.ExecutionFlags memory _flags - ) private view { - // Ensure that the slot proposed is NOT in the future - Slot currentSlot = _currentTime.slotFromTimestamp(); - require(_slot == currentSlot, Errors.HeaderLib__InvalidSlotNumber(currentSlot, _slot)); - - // @note We are currently enforcing that the slot is in the current epoch - // If this is not the case, there could potentially be a weird reorg - // of an entire epoch if no-one from the new epoch committee have seen - // those blocks or behaves as if they did not. - - Epoch epochNumber = _slot.epochFromSlot(); - Epoch currentEpoch = _currentTime.epochFromTimestamp(); - require(epochNumber == currentEpoch, Errors.Rollup__InvalidEpoch(currentEpoch, epochNumber)); - - ValidatorSelectionLib.validateValidatorSelection( - StakingLib.getStorage(), _slot, epochNumber, _signatures, _digest, _flags + ValidatorSelectionLib.verify( + StakingLib.getStorage(), + slot, + slot.epochFromSlot(), + _args.attestations, + _args.digest, + _args.flags ); } + function digest(ProposeArgs memory _args) internal pure returns (bytes32) { + return keccak256(abi.encode(SignatureLib.SignatureDomainSeparator.blockAttestation, _args)); + } + // Helper to avoid stack too deep function toBlockLog( ProposeArgs calldata _args, + Header memory _header, uint256 _blockNumber, uint256 _congestionCost, uint256 _provingCost ) private view returns (BlockLog memory) { RollupStore storage rollupStore = STFLib.getStorage(); - FeeHeader memory parentFeeHeader = rollupStore.blocks[_blockNumber - 1].feeHeader; + FeeHeader storage parentFeeHeader = rollupStore.blocks[_blockNumber - 1].feeHeader; return BlockLog({ archive: _args.archive, blockHash: _args.blockHash, - slotNumber: Slot.wrap(uint256(bytes32(_args.header[0x0194:0x01b4]))), + slotNumber: _header.globalVariables.slotNumber, feeHeader: FeeHeader({ excessMana: FeeMath.computeExcessMana(parentFeeHeader), feeAssetPriceNumerator: FeeMath.clampedAdd( parentFeeHeader.feeAssetPriceNumerator, _args.oracleInput.feeAssetPriceModifier ), - manaUsed: uint256(bytes32(_args.header[0x0268:0x0288])), + manaUsed: _header.totalManaUsed, congestionCost: _congestionCost, provingCost: _provingCost }) diff --git a/l1-contracts/src/core/libraries/rollup/RewardLib.sol b/l1-contracts/src/core/libraries/rollup/RewardLib.sol new file mode 100644 index 000000000000..83d7fdf160d2 --- /dev/null +++ b/l1-contracts/src/core/libraries/rollup/RewardLib.sol @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2024 Aztec Labs. +pragma solidity >=0.8.27; + +import {RollupStore, EpochRewards} from "@aztec/core/interfaces/IRollup.sol"; +import {Errors} from "@aztec/core/libraries/Errors.sol"; +import {STFLib} from "@aztec/core/libraries/rollup/STFLib.sol"; +import {Epoch, Timestamp, Slot, TimeLib} from "@aztec/core/libraries/TimeLib.sol"; + +library RewardLib { + using TimeLib for Timestamp; + using TimeLib for Epoch; + + function claimSequencerRewards(address _recipient) internal returns (uint256) { + RollupStore storage rollupStore = STFLib.getStorage(); + uint256 amount = rollupStore.sequencerRewards[msg.sender]; + rollupStore.sequencerRewards[msg.sender] = 0; + rollupStore.config.feeAsset.transfer(_recipient, amount); + + return amount; + } + + function claimProverRewards(address _recipient, Epoch[] memory _epochs) + internal + returns (uint256) + { + Slot currentSlot = Timestamp.wrap(block.timestamp).slotFromTimestamp(); + RollupStore storage rollupStore = STFLib.getStorage(); + uint256 proofSubmissionWindow = rollupStore.config.proofSubmissionWindow; + + uint256 accumulatedRewards = 0; + for (uint256 i = 0; i < _epochs.length; i++) { + Slot deadline = _epochs[i].toSlots() + Slot.wrap(proofSubmissionWindow); + 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); + } + } + + rollupStore.config.feeAsset.transfer(_recipient, accumulatedRewards); + + return accumulatedRewards; + } +} diff --git a/l1-contracts/src/core/libraries/RollupLibs/core/STFLib.sol b/l1-contracts/src/core/libraries/rollup/STFLib.sol similarity index 71% rename from l1-contracts/src/core/libraries/RollupLibs/core/STFLib.sol rename to l1-contracts/src/core/libraries/rollup/STFLib.sol index cfd9539d8fdd..1ac62aa18b39 100644 --- a/l1-contracts/src/core/libraries/RollupLibs/core/STFLib.sol +++ b/l1-contracts/src/core/libraries/rollup/STFLib.sol @@ -2,10 +2,18 @@ // Copyright 2024 Aztec Labs. pragma solidity >=0.8.27; -import {RollupStore, IRollupCore} from "@aztec/core/interfaces/IRollup.sol"; +import {RollupStore, IRollupCore, BlockLog} from "@aztec/core/interfaces/IRollup.sol"; import {Errors} from "@aztec/core/libraries/Errors.sol"; +import {FeeHeader} from "@aztec/core/libraries/rollup/FeeMath.sol"; import {Timestamp, Slot, Epoch, TimeLib} from "@aztec/core/libraries/TimeLib.sol"; +struct GenesisState { + bytes32 vkTreeRoot; + bytes32 protocolContractTreeRoot; + bytes32 genesisArchiveRoot; + bytes32 genesisBlockHash; +} + library STFLib { using TimeLib for Slot; using TimeLib for Epoch; @@ -14,6 +22,26 @@ library STFLib { // @note This is also used in the cheatcodes, so if updating, please also update the cheatcode. bytes32 private constant STF_STORAGE_POSITION = keccak256("aztec.stf.storage"); + function initialize(GenesisState memory _genesisState) internal { + RollupStore storage rollupStore = STFLib.getStorage(); + + rollupStore.config.vkTreeRoot = _genesisState.vkTreeRoot; + rollupStore.config.protocolContractTreeRoot = _genesisState.protocolContractTreeRoot; + + rollupStore.blocks[0] = BlockLog({ + feeHeader: FeeHeader({ + excessMana: 0, + feeAssetPriceNumerator: 0, + manaUsed: 0, + congestionCost: 0, + provingCost: 0 + }), + archive: _genesisState.genesisArchiveRoot, + blockHash: _genesisState.genesisBlockHash, + slotNumber: Slot.wrap(0) + }); + } + function prune() internal { RollupStore storage rollupStore = STFLib.getStorage(); uint256 pending = rollupStore.tips.pendingBlockNumber; diff --git a/l1-contracts/src/core/libraries/staking/StakingLib.sol b/l1-contracts/src/core/libraries/staking/StakingLib.sol index 1c0c24ff0802..87ecfda67dfe 100644 --- a/l1-contracts/src/core/libraries/staking/StakingLib.sol +++ b/l1-contracts/src/core/libraries/staking/StakingLib.sol @@ -131,6 +131,8 @@ library StakingLib { // Note that the "amount" is not stored here, but reusing the `validators` // We always exit fully. + // @note The attester might be chosen for the epoch, so the delay must be long enough + // to allow for that. store.exits[_attester] = Exit({exitableAt: Timestamp.wrap(block.timestamp) + store.exitDelay, recipient: _recipient}); validator.status = Status.EXITING; diff --git a/l1-contracts/src/core/libraries/ValidatorSelectionLib/ValidatorSelectionLib.sol b/l1-contracts/src/core/libraries/validator-selection/ValidatorSelectionLib.sol similarity index 97% rename from l1-contracts/src/core/libraries/ValidatorSelectionLib/ValidatorSelectionLib.sol rename to l1-contracts/src/core/libraries/validator-selection/ValidatorSelectionLib.sol index 32e87d134369..0a8335a08990 100644 --- a/l1-contracts/src/core/libraries/ValidatorSelectionLib/ValidatorSelectionLib.sol +++ b/l1-contracts/src/core/libraries/validator-selection/ValidatorSelectionLib.sol @@ -2,13 +2,13 @@ // Copyright 2024 Aztec Labs. pragma solidity >=0.8.27; +import {BlockHeaderValidationFlags} from "@aztec/core/interfaces/IRollup.sol"; import {StakingStorage} from "@aztec/core/interfaces/IStaking.sol"; import { EpochData, ValidatorSelectionStorage } from "@aztec/core/interfaces/IValidatorSelection.sol"; import {SampleLib} from "@aztec/core/libraries/crypto/SampleLib.sol"; import {SignatureLib, 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 {Timestamp, Slot, Epoch, TimeLib} from "@aztec/core/libraries/TimeLib.sol"; import {MessageHashUtils} from "@oz/utils/cryptography/MessageHashUtils.sol"; @@ -66,13 +66,13 @@ library ValidatorSelectionLib { * @param _signatures - The signatures of the committee members * @param _digest - The digest of the block */ - function validateValidatorSelection( + function verify( StakingStorage storage _stakingStore, Slot _slot, Epoch _epochNumber, Signature[] memory _signatures, bytes32 _digest, - DataStructures.ExecutionFlags memory _flags + BlockHeaderValidationFlags memory _flags ) internal view { // Same logic as we got in getProposerAt // Done do avoid duplicate computing the committee @@ -90,7 +90,6 @@ library ValidatorSelectionLib { return; } - // @todo We should allow to provide a signature instead of needing the proposer to broadcast. require( proposer == msg.sender, Errors.ValidatorSelection__InvalidProposer(proposer, msg.sender) ); diff --git a/l1-contracts/test/MultiProof.t.sol b/l1-contracts/test/MultiProof.t.sol index 346c3158eec8..e590865064e5 100644 --- a/l1-contracts/test/MultiProof.t.sol +++ b/l1-contracts/test/MultiProof.t.sol @@ -11,7 +11,7 @@ import {FeeJuicePortal} from "@aztec/core/FeeJuicePortal.sol"; import {TestERC20} from "@aztec/mock/TestERC20.sol"; import {TestConstants} from "./harnesses/TestConstants.sol"; import {RewardDistributor} from "@aztec/governance/RewardDistributor.sol"; -import {ProposeArgs, ProposeLib} from "@aztec/core/libraries/RollupLibs/ProposeLib.sol"; +import {ProposeArgs, ProposeLib} from "@aztec/core/libraries/rollup/ProposeLib.sol"; import { Timestamp, Slot, Epoch, SlotLib, EpochLib, TimeLib diff --git a/l1-contracts/test/Rollup.t.sol b/l1-contracts/test/Rollup.t.sol index fa0536949d40..11644596fefb 100644 --- a/l1-contracts/test/Rollup.t.sol +++ b/l1-contracts/test/Rollup.t.sol @@ -30,9 +30,7 @@ import {TestERC20} from "@aztec/mock/TestERC20.sol"; import {TestConstants} from "./harnesses/TestConstants.sol"; import {RewardDistributor} from "@aztec/governance/RewardDistributor.sol"; import {IERC20Errors} from "@oz/interfaces/draft-IERC6093.sol"; -import { - ProposeArgs, OracleInput, ProposeLib -} from "@aztec/core/libraries/RollupLibs/ProposeLib.sol"; +import {ProposeArgs, OracleInput, ProposeLib} from "@aztec/core/libraries/rollup/ProposeLib.sol"; import { Timestamp, Slot, Epoch, SlotLib, EpochLib, TimeLib diff --git a/l1-contracts/test/base/DecoderBase.sol b/l1-contracts/test/base/DecoderBase.sol index dd47a359ea61..f5792cb97712 100644 --- a/l1-contracts/test/base/DecoderBase.sol +++ b/l1-contracts/test/base/DecoderBase.sol @@ -2,7 +2,6 @@ // Copyright 2024 Aztec Labs. pragma solidity >=0.8.27; -import {Test} from "forge-std/Test.sol"; import {TestBase} from "../base/Base.sol"; // Many of the structs in here match what you see in `header` but with very important exceptions! diff --git a/l1-contracts/test/base/RollupBase.sol b/l1-contracts/test/base/RollupBase.sol index 91f9cf230d56..c5166ed4384d 100644 --- a/l1-contracts/test/base/RollupBase.sol +++ b/l1-contracts/test/base/RollupBase.sol @@ -15,9 +15,7 @@ import { Timestamp, Slot, Epoch, SlotLib, EpochLib, TimeLib } from "@aztec/core/libraries/TimeLib.sol"; import {DataStructures} from "@aztec/core/libraries/DataStructures.sol"; -import { - ProposeArgs, OracleInput, ProposeLib -} from "@aztec/core/libraries/RollupLibs/ProposeLib.sol"; +import {ProposeArgs, OracleInput, ProposeLib} from "@aztec/core/libraries/rollup/ProposeLib.sol"; import {Signature} from "@aztec/core/libraries/crypto/SignatureLib.sol"; import {Inbox} from "@aztec/core/messagebridge/Inbox.sol"; import {Outbox} from "@aztec/core/messagebridge/Outbox.sol"; diff --git a/l1-contracts/test/decoders/Decoders.t.sol b/l1-contracts/test/decoders/Decoders.t.sol index c0568d33612b..9fa5761bdf05 100644 --- a/l1-contracts/test/decoders/Decoders.t.sol +++ b/l1-contracts/test/decoders/Decoders.t.sol @@ -5,7 +5,7 @@ pragma solidity >=0.8.27; import {DecoderBase} from "../base/DecoderBase.sol"; import {HeaderLibHelper} from "./helpers/HeaderLibHelper.sol"; -import {Header} from "@aztec/core/libraries/RollupLibs/HeaderLib.sol"; +import {Header} from "@aztec/core/libraries/rollup/HeaderLib.sol"; /** * Blocks are generated using the `integration_l1_publisher.test.ts` tests. diff --git a/l1-contracts/test/decoders/helpers/HeaderLibHelper.sol b/l1-contracts/test/decoders/helpers/HeaderLibHelper.sol index 8b81756fa772..f275db7735ec 100644 --- a/l1-contracts/test/decoders/helpers/HeaderLibHelper.sol +++ b/l1-contracts/test/decoders/helpers/HeaderLibHelper.sol @@ -2,7 +2,7 @@ // Copyright 2024 Aztec Labs. pragma solidity >=0.8.27; -import {HeaderLib, Header} from "@aztec/core/libraries/RollupLibs/HeaderLib.sol"; +import {HeaderLib, Header} from "@aztec/core/libraries/rollup/HeaderLib.sol"; contract HeaderLibHelper { // A wrapper used such that we get "calldata" and not memory diff --git a/l1-contracts/test/fees/FeeModelTestPoints.t.sol b/l1-contracts/test/fees/FeeModelTestPoints.t.sol index cda00c039135..fd9af2e9a5da 100644 --- a/l1-contracts/test/fees/FeeModelTestPoints.t.sol +++ b/l1-contracts/test/fees/FeeModelTestPoints.t.sol @@ -4,11 +4,11 @@ pragma solidity >=0.8.27; import {TestBase} from "../base/Base.sol"; -import {OracleInput} from "@aztec/core/libraries/RollupLibs/FeeMath.sol"; +import {OracleInput} from "@aztec/core/libraries/rollup/FeeMath.sol"; import { MAX_FEE_ASSET_PRICE_MODIFIER, MINIMUM_CONGESTION_MULTIPLIER -} from "@aztec/core/libraries/RollupLibs/FeeMath.sol"; +} from "@aztec/core/libraries/rollup/FeeMath.sol"; import {Math} from "@oz/utils/math/Math.sol"; // Remember that foundry json parsing is alphabetically done, so you MUST diff --git a/l1-contracts/test/fees/FeeRollup.t.sol b/l1-contracts/test/fees/FeeRollup.t.sol index ab6cc17dc88b..d576e3a84df7 100644 --- a/l1-contracts/test/fees/FeeRollup.t.sol +++ b/l1-contracts/test/fees/FeeRollup.t.sol @@ -28,9 +28,7 @@ import {RewardDistributor} from "@aztec/governance/RewardDistributor.sol"; import {IERC20Errors} from "@oz/interfaces/draft-IERC6093.sol"; import {IFeeJuicePortal} from "@aztec/core/interfaces/IFeeJuicePortal.sol"; import {IRewardDistributor, IRegistry} from "@aztec/governance/interfaces/IRewardDistributor.sol"; -import { - ProposeArgs, OracleInput, ProposeLib -} from "@aztec/core/libraries/RollupLibs/ProposeLib.sol"; +import {ProposeArgs, OracleInput, ProposeLib} from "@aztec/core/libraries/rollup/ProposeLib.sol"; import {IERC20} from "@oz/token/ERC20/IERC20.sol"; import { FeeMath, @@ -41,7 +39,7 @@ import { FeeHeader, L1FeeData, ManaBaseFeeComponents -} from "@aztec/core/libraries/RollupLibs/FeeMath.sol"; +} from "@aztec/core/libraries/rollup/FeeMath.sol"; import { FeeModelTestPoints, @@ -156,7 +154,7 @@ contract FeeRollupTest is FeeModelTestPoints, DecoderBase { vm.label(address(rollup), "ROLLUP"); vm.label(address(fakeCanonical), "FAKE CANONICAL"); vm.label(address(asset), "ASSET"); - vm.label(rollup.getCuauhxicalli(), "CUAUHXICALLI"); + vm.label(rollup.getBurnAddress(), "BURN_ADDRESS"); } function _loadL1Metadata(uint256 index) internal { @@ -454,7 +452,7 @@ contract FeeRollupTest is FeeModelTestPoints, DecoderBase { fees[feeIndex * 2 + 1] = bytes32(fee); } - uint256 cuauhxicalliBalanceBefore = asset.balanceOf(rollup.getCuauhxicalli()); + uint256 burnAddressBalanceBefore = asset.balanceOf(rollup.getBurnAddress()); uint256 sequencerRewardsBefore = rollup.getSequencerRewards(coinbase); PublicInputArgs memory args = PublicInputArgs({ @@ -489,7 +487,7 @@ contract FeeRollupTest is FeeModelTestPoints, DecoderBase { ); } - uint256 burned = asset.balanceOf(rollup.getCuauhxicalli()) - cuauhxicalliBalanceBefore; + uint256 burned = asset.balanceOf(rollup.getBurnAddress()) - burnAddressBalanceBefore; assertEq(burnSum, burned, "Sum of burned does not match"); // The reward is not yet distributed, but only accumulated. diff --git a/l1-contracts/test/fees/MinimalFeeModel.sol b/l1-contracts/test/fees/MinimalFeeModel.sol index ad1d6b107fbf..4366b3f5e42f 100644 --- a/l1-contracts/test/fees/MinimalFeeModel.sol +++ b/l1-contracts/test/fees/MinimalFeeModel.sol @@ -16,7 +16,7 @@ import { FeeHeader, L1FeeData, ManaBaseFeeComponents -} from "@aztec/core/libraries/RollupLibs/FeeMath.sol"; +} from "@aztec/core/libraries/rollup/FeeMath.sol"; import {Vm} from "forge-std/Vm.sol"; import { ManaBaseFeeComponentsModel, diff --git a/l1-contracts/test/fees/MinimalFeeModel.t.sol b/l1-contracts/test/fees/MinimalFeeModel.t.sol index 409195321f39..dfa034f09f18 100644 --- a/l1-contracts/test/fees/MinimalFeeModel.t.sol +++ b/l1-contracts/test/fees/MinimalFeeModel.t.sol @@ -18,7 +18,7 @@ import { MINIMUM_CONGESTION_MULTIPLIER, EthValue, FeeAssetPerEthE9 -} from "@aztec/core/libraries/RollupLibs/FeeMath.sol"; +} from "@aztec/core/libraries/rollup/FeeMath.sol"; import {Math} from "@oz/utils/math/Math.sol"; contract MinimalFeeModelTest is FeeModelTestPoints { diff --git a/l1-contracts/test/validator-selection/ValidatorSelection.t.sol b/l1-contracts/test/validator-selection/ValidatorSelection.t.sol index c438c0e4cc68..65143f64978f 100644 --- a/l1-contracts/test/validator-selection/ValidatorSelection.t.sol +++ b/l1-contracts/test/validator-selection/ValidatorSelection.t.sol @@ -18,9 +18,7 @@ import {MerkleTestUtil} from "../merkle/TestUtil.sol"; import {TestERC20} from "@aztec/mock/TestERC20.sol"; import {MessageHashUtils} from "@oz/utils/cryptography/MessageHashUtils.sol"; import {MockFeeJuicePortal} from "@aztec/mock/MockFeeJuicePortal.sol"; -import { - ProposeArgs, OracleInput, ProposeLib -} from "@aztec/core/libraries/RollupLibs/ProposeLib.sol"; +import {ProposeArgs, OracleInput, ProposeLib} from "@aztec/core/libraries/rollup/ProposeLib.sol"; import {TestConstants} from "../harnesses/TestConstants.sol"; import {CheatDepositArgs} from "@aztec/core/interfaces/IRollup.sol";