From 098611afb2fde7337897b7b7b5ced833e17e40cf Mon Sep 17 00:00:00 2001 From: Santiago Palladino Date: Fri, 8 Nov 2024 17:58:14 -0300 Subject: [PATCH] chore: Move epoch and slot durations to config De-enshrines the following constants and turns them into config: ``` ETHEREUM_SLOT_DURATION = 12; AZTEC_SLOT_DURATION = 24; AZTEC_EPOCH_DURATION = 16; AZTEC_TARGET_COMMITTEE_SIZE = 48; AZTEC_EPOCH_PROOF_CLAIM_WINDOW_IN_L2_SLOTS = 13; ``` These can now be set via env vars. On L1, they are set as immutable variables across all contracts that require them. As for circuits, none of them was needed, except for the epoch duration to be able to dimension the fees array. This was handled by introducing a new MAX_EPOCH_DURATION constant (32) which sets the max length of the array. This is a prerequisite to #9809 --- l1-contracts/src/core/Leonidas.sol | 42 +++++----- .../src/core/ProofCommitmentEscrow.sol | 11 ++- l1-contracts/src/core/Rollup.sol | 40 ++++++--- .../src/core/libraries/ConstantsGen.sol | 10 +-- l1-contracts/src/core/libraries/TimeMath.sol | 59 +++++++++----- l1-contracts/test/Rollup.t.sol | 81 +++++++++++-------- .../fee_portal/depositToAztecPublic.t.sol | 2 +- .../test/fee_portal/distributeFees.t.sol | 2 +- .../governance-proposer/pushProposal.t.sol | 2 +- .../governance/governance-proposer/vote.t.sol | 2 +- .../UpgradeGovernanceProposerTest.t.sol | 2 +- l1-contracts/test/harnesses/Leonidas.sol | 17 ++++ l1-contracts/test/harnesses/Rollup.sol | 34 ++++++++ l1-contracts/test/harnesses/TestConstants.sol | 12 +++ l1-contracts/test/portals/TokenPortal.t.sol | 2 +- l1-contracts/test/portals/UniswapPortal.t.sol | 2 +- .../ProofCommitmentEscrow.t.sol | 6 +- l1-contracts/test/sparta/Sparta.t.sol | 4 +- ...block_root_or_block_merge_public_inputs.nr | 11 +-- .../block_root/block_root_rollup_inputs.nr | 4 +- .../empty_block_root_rollup_inputs.nr | 4 +- .../crates/rollup-lib/src/components.nr | 6 +- .../src/root/root_rollup_public_inputs.nr | 4 +- .../crates/types/src/constants.nr | 12 +-- .../archiver/src/archiver/archiver.test.ts | 10 ++- .../archiver/src/archiver/archiver.ts | 17 ++-- yarn-project/archiver/src/archiver/config.ts | 12 ++- .../archiver/src/archiver/epoch_helpers.ts | 28 ++++--- .../archiver/src/test/mock_l2_block_source.ts | 4 +- .../aztec.js/src/utils/cheat_codes.ts | 24 ++++-- yarn-project/aztec/src/sandbox.ts | 9 ++- yarn-project/circuits.js/src/constants.gen.ts | 8 +- ...block_root_or_block_merge_public_inputs.ts | 6 +- .../src/structs/rollup/root_rollup.ts | 6 +- .../circuits.js/src/tests/factories.ts | 6 +- .../cli/src/cmds/l1/deploy_l1_contracts.ts | 4 + .../cli/src/cmds/l1/update_l1_validators.ts | 7 +- yarn-project/cli/src/utils/aztec.ts | 4 +- .../composed/integration_l1_publisher.test.ts | 8 +- .../end-to-end/src/e2e_block_building.test.ts | 6 +- .../src/e2e_l1_with_wall_time.test.ts | 10 ++- .../src/e2e_lending_contract.test.ts | 4 +- .../end-to-end/src/e2e_p2p/p2p_network.ts | 8 +- .../end-to-end/src/e2e_synching.test.ts | 6 +- .../src/fixtures/setup_l1_contracts.ts | 4 +- .../src/fixtures/snapshot_manager.ts | 3 +- yarn-project/end-to-end/src/fixtures/utils.ts | 3 +- .../e2e_prover_coordination.test.ts | 13 +-- .../src/simulators/lending_simulator.ts | 6 +- .../end-to-end/src/spartan/4epochs.test.ts | 6 +- .../end-to-end/src/spartan/reorg.test.ts | 16 ++-- yarn-project/ethereum/src/config.ts | 54 +++++++++++++ .../ethereum/src/deploy_l1_contracts.ts | 33 ++++---- yarn-project/ethereum/src/index.ts | 1 + yarn-project/ethereum/src/l1_reader.ts | 21 ++--- yarn-project/foundation/src/config/env_var.ts | 7 +- yarn-project/ivc-integration/package.json | 2 +- .../src/type_conversion.ts | 6 +- yarn-project/sequencer-client/src/config.ts | 16 +++- .../global_variable_builder/global_builder.ts | 17 ++-- .../src/publisher/l1-publisher.test.ts | 8 +- .../src/publisher/l1-publisher.ts | 22 ++--- .../src/sequencer/sequencer.test.ts | 19 ++--- .../src/sequencer/sequencer.ts | 29 ++++--- .../sequencer-client/src/sequencer/utils.ts | 5 +- yarn-project/validator-client/src/config.ts | 9 ++- 66 files changed, 545 insertions(+), 313 deletions(-) create mode 100644 l1-contracts/test/harnesses/Leonidas.sol create mode 100644 l1-contracts/test/harnesses/Rollup.sol create mode 100644 l1-contracts/test/harnesses/TestConstants.sol create mode 100644 yarn-project/ethereum/src/config.ts diff --git a/l1-contracts/src/core/Leonidas.sol b/l1-contracts/src/core/Leonidas.sol index 14a85d3bce1d..a750520364c0 100644 --- a/l1-contracts/src/core/Leonidas.sol +++ b/l1-contracts/src/core/Leonidas.sol @@ -8,7 +8,9 @@ import {SampleLib} from "@aztec/core/libraries/crypto/SampleLib.sol"; import {SignatureLib} 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, SlotLib, EpochLib} from "@aztec/core/libraries/TimeMath.sol"; +import { + Timestamp, Slot, Epoch, SlotLib, EpochLib, TimeFns +} from "@aztec/core/libraries/TimeMath.sol"; import {Ownable} from "@oz/access/Ownable.sol"; import {MessageHashUtils} from "@oz/utils/cryptography/MessageHashUtils.sol"; import {EnumerableSet} from "@oz/utils/structs/EnumerableSet.sol"; @@ -27,7 +29,7 @@ import {EnumerableSet} from "@oz/utils/structs/EnumerableSet.sol"; * It will be the duty of his successor (Pleistarchus) to optimize the costs with same functionality. * */ -contract Leonidas is Ownable, ILeonidas { +contract Leonidas is Ownable, TimeFns, ILeonidas { using EnumerableSet for EnumerableSet.AddressSet; using SignatureLib for SignatureLib.Signature; using MessageHashUtils for bytes32; @@ -47,25 +49,9 @@ contract Leonidas is Ownable, ILeonidas { uint256 nextSeed; } - // @note @LHerskind The multiple cause pain and suffering in the E2E tests as we introduce - // a timeliness requirement into the publication that did not exists before, - // and at the same time have a setup that will impact the time at every tx - // because of auto-mine. By using just 1, we can make our test work - // but anything using an actual working chain would eat dung as simulating - // transactions is slower than an actual ethereum slot. - // - // The value should be a higher multiple for any actual chain - // @todo #8019 - uint256 public constant SLOT_DURATION = Constants.AZTEC_SLOT_DURATION; - - // The duration of an epoch in slots - // @todo @LHerskind - This value should be updated when we are not blind. - // @todo #8020 - uint256 public constant EPOCH_DURATION = Constants.AZTEC_EPOCH_DURATION; - // The target number of validators in a committee // @todo #8021 - uint256 public constant TARGET_COMMITTEE_SIZE = Constants.AZTEC_TARGET_COMMITTEE_SIZE; + uint256 public immutable TARGET_COMMITTEE_SIZE; // The time that the contract was deployed Timestamp public immutable GENESIS_TIME; @@ -79,8 +65,16 @@ contract Leonidas is Ownable, ILeonidas { // The last stored randao value, same value as `seed` in the last inserted epoch uint256 private lastSeed; - constructor(address _ares) Ownable(_ares) { + constructor( + address _ares, + uint256 _slotDuration, + uint256 _epochDuration, + uint256 _targetCommitteeSize + ) Ownable(_ares) TimeFns(_slotDuration, _epochDuration) { GENESIS_TIME = Timestamp.wrap(block.timestamp); + SLOT_DURATION = _slotDuration; + EPOCH_DURATION = _epochDuration; + TARGET_COMMITTEE_SIZE = _targetCommitteeSize; } /** @@ -232,7 +226,7 @@ contract Leonidas is Ownable, ILeonidas { override(ILeonidas) returns (Timestamp) { - return GENESIS_TIME + _slotNumber.toTimestamp(); + return GENESIS_TIME + toTimestamp(_slotNumber); } /** @@ -303,7 +297,7 @@ contract Leonidas is Ownable, ILeonidas { * @return The computed epoch */ function getEpochAt(Timestamp _ts) public view override(ILeonidas) returns (Epoch) { - return _ts < GENESIS_TIME ? Epoch.wrap(0) : EpochLib.fromTimestamp(_ts - GENESIS_TIME); + return _ts < GENESIS_TIME ? Epoch.wrap(0) : epochFromTimestamp(_ts - GENESIS_TIME); } /** @@ -314,7 +308,7 @@ contract Leonidas is Ownable, ILeonidas { * @return The computed slot */ function getSlotAt(Timestamp _ts) public view override(ILeonidas) returns (Slot) { - return _ts < GENESIS_TIME ? Slot.wrap(0) : SlotLib.fromTimestamp(_ts - GENESIS_TIME); + return _ts < GENESIS_TIME ? Slot.wrap(0) : slotFromTimestamp(_ts - GENESIS_TIME); } /** @@ -324,7 +318,7 @@ contract Leonidas is Ownable, ILeonidas { * * @return The computed epoch */ - function getEpochAtSlot(Slot _slotNumber) public pure override(ILeonidas) returns (Epoch) { + function getEpochAtSlot(Slot _slotNumber) public view override(ILeonidas) returns (Epoch) { return Epoch.wrap(_slotNumber.unwrap() / EPOCH_DURATION); } diff --git a/l1-contracts/src/core/ProofCommitmentEscrow.sol b/l1-contracts/src/core/ProofCommitmentEscrow.sol index fb2997d17cb8..cfa578652449 100644 --- a/l1-contracts/src/core/ProofCommitmentEscrow.sol +++ b/l1-contracts/src/core/ProofCommitmentEscrow.sol @@ -17,8 +17,7 @@ contract ProofCommitmentEscrow is IProofCommitmentEscrow { Timestamp executableAt; } - uint256 public constant WITHDRAW_DELAY = - Constants.ETHEREUM_SLOT_DURATION * Constants.AZTEC_EPOCH_DURATION * 3; + uint256 public immutable WITHDRAW_DELAY; address public immutable ROLLUP; IERC20 public immutable TOKEN; @@ -31,9 +30,15 @@ contract ProofCommitmentEscrow is IProofCommitmentEscrow { _; } - constructor(IERC20 _token, address _rollup) { + constructor( + IERC20 _token, + address _rollup, + uint256 _aztecSlotDuration, + uint256 _aztecEpochDuration + ) { ROLLUP = _rollup; TOKEN = _token; + WITHDRAW_DELAY = _aztecSlotDuration * _aztecEpochDuration; } /** diff --git a/l1-contracts/src/core/Rollup.sol b/l1-contracts/src/core/Rollup.sol index ac9a5cd3eaa5..9e30c805b11f 100644 --- a/l1-contracts/src/core/Rollup.sol +++ b/l1-contracts/src/core/Rollup.sol @@ -52,12 +52,18 @@ contract Rollup is EIP712("Aztec Rollup", "1"), Leonidas, IRollup, ITestRollup { Slot slotNumber; } + struct Config { + uint256 aztecSlotDuration; + uint256 aztecEpochDuration; + uint256 targetCommitteeSize; + uint256 aztecEpochProofClaimWindowInL2Slots; + } + // See https://github.com/AztecProtocol/engineering-designs/blob/main/in-progress/8401-proof-timeliness/proof-timeliness.ipynb // for justification of CLAIM_DURATION_IN_L2_SLOTS. - uint256 public constant CLAIM_DURATION_IN_L2_SLOTS = - Constants.AZTEC_EPOCH_PROOF_CLAIM_WINDOW_IN_L2_SLOTS; uint256 public constant PROOF_COMMITMENT_MIN_BOND_AMOUNT_IN_TST = 1000; + uint256 public immutable CLAIM_DURATION_IN_L2_SLOTS; uint256 public immutable L1_BLOCK_AT_GENESIS; IInbox public immutable INBOX; IOutbox public immutable OUTBOX; @@ -66,6 +72,7 @@ contract Rollup is EIP712("Aztec Rollup", "1"), Leonidas, IRollup, ITestRollup { IFeeJuicePortal public immutable FEE_JUICE_PORTAL; IRewardDistributor public immutable REWARD_DISTRIBUTOR; IERC20 public immutable ASSET; + IVerifier public epochProofVerifier; ChainTips public tips; @@ -91,19 +98,30 @@ contract Rollup is EIP712("Aztec Rollup", "1"), Leonidas, IRollup, ITestRollup { bytes32 _vkTreeRoot, bytes32 _protocolContractTreeRoot, address _ares, - address[] memory _validators - ) Leonidas(_ares) { + address[] memory _validators, + Config memory _config + ) + Leonidas( + _ares, + _config.aztecSlotDuration, + _config.aztecEpochDuration, + _config.targetCommitteeSize + ) + { epochProofVerifier = new MockVerifier(); FEE_JUICE_PORTAL = _fpcJuicePortal; REWARD_DISTRIBUTOR = _rewardDistributor; ASSET = _fpcJuicePortal.UNDERLYING(); - PROOF_COMMITMENT_ESCROW = new ProofCommitmentEscrow(ASSET, address(this)); + PROOF_COMMITMENT_ESCROW = new ProofCommitmentEscrow( + ASSET, address(this), _config.aztecSlotDuration, _config.aztecEpochDuration + ); INBOX = IInbox(address(new Inbox(address(this), Constants.L1_TO_L2_MSG_SUBTREE_HEIGHT))); OUTBOX = IOutbox(address(new Outbox(address(this)))); vkTreeRoot = _vkTreeRoot; protocolContractTreeRoot = _protocolContractTreeRoot; VERSION = 1; L1_BLOCK_AT_GENESIS = block.number; + CLAIM_DURATION_IN_L2_SLOTS = _config.aztecEpochProofClaimWindowInL2Slots; // Genesis block blocks[0] = BlockLog({ @@ -641,7 +659,7 @@ contract Rollup is EIP712("Aztec Rollup", "1"), Leonidas, IRollup, ITestRollup { // out_hash: root of this epoch's l2 to l1 message tree publicInputs[8] = _args[5]; - uint256 feesLength = Constants.AZTEC_EPOCH_DURATION * 2; + uint256 feesLength = Constants.AZTEC_MAX_EPOCH_DURATION * 2; // fees[9 to (9+feesLength-1)]: array of recipient-value pairs for (uint256 i = 0; i < feesLength; i++) { publicInputs[9 + i] = _fees[i]; @@ -703,8 +721,8 @@ contract Rollup is EIP712("Aztec Rollup", "1"), Leonidas, IRollup, ITestRollup { ); require( - currentSlot.positionInEpoch() < CLAIM_DURATION_IN_L2_SLOTS, - Errors.Rollup__NotInClaimPhase(currentSlot.positionInEpoch(), CLAIM_DURATION_IN_L2_SLOTS) + positionInEpoch(currentSlot) < CLAIM_DURATION_IN_L2_SLOTS, + Errors.Rollup__NotInClaimPhase(positionInEpoch(currentSlot), CLAIM_DURATION_IN_L2_SLOTS) ); // if the epoch to prove is not the one that has been claimed, @@ -795,16 +813,16 @@ contract Rollup is EIP712("Aztec Rollup", "1"), Leonidas, IRollup, ITestRollup { Slot currentSlot = getSlotAt(_ts); Epoch oldestPendingEpoch = getEpochForBlock(tips.provenBlockNumber + 1); - Slot startSlotOfPendingEpoch = oldestPendingEpoch.toSlots(); + Slot startSlotOfPendingEpoch = toSlots(oldestPendingEpoch); // suppose epoch 1 is proven, epoch 2 is pending, epoch 3 is the current epoch. // we prune the pending chain back to the end of epoch 1 if: // - the proof claim phase of epoch 3 has ended without a claim to prove epoch 2 (or proof of epoch 2) // - we reach epoch 4 without a proof of epoch 2 (regardless of whether a proof claim was submitted) bool inClaimPhase = currentSlot - < startSlotOfPendingEpoch + Epoch.wrap(1).toSlots() + Slot.wrap(CLAIM_DURATION_IN_L2_SLOTS); + < startSlotOfPendingEpoch + toSlots(Epoch.wrap(1)) + Slot.wrap(CLAIM_DURATION_IN_L2_SLOTS); - bool claimExists = currentSlot < startSlotOfPendingEpoch + Epoch.wrap(2).toSlots() + bool claimExists = currentSlot < startSlotOfPendingEpoch + toSlots(Epoch.wrap(2)) && proofClaim.epochToProve == oldestPendingEpoch && proofClaim.proposerClaimant != address(0); if (inClaimPhase || claimExists) { diff --git a/l1-contracts/src/core/libraries/ConstantsGen.sol b/l1-contracts/src/core/libraries/ConstantsGen.sol index a717be49bca6..9db974b469d1 100644 --- a/l1-contracts/src/core/libraries/ConstantsGen.sol +++ b/l1-contracts/src/core/libraries/ConstantsGen.sol @@ -1,6 +1,6 @@ // GENERATED FILE - DO NOT EDIT, RUN yarn remake-constants in circuits.js // SPDX-License-Identifier: Apache-2.0 -// Copyright 2024 Aztec Labs. +// Copyright 2023 Aztec Labs. pragma solidity >=0.8.27; /** @@ -94,11 +94,7 @@ library Constants { uint256 internal constant INITIAL_L2_BLOCK_NUM = 1; uint256 internal constant PRIVATE_LOG_SIZE_IN_BYTES = 576; uint256 internal constant BLOB_SIZE_IN_BYTES = 126976; - uint256 internal constant ETHEREUM_SLOT_DURATION = 12; - 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_EPOCH_PROOF_CLAIM_WINDOW_IN_L2_SLOTS = 13; + uint256 internal constant AZTEC_MAX_EPOCH_DURATION = 32; uint256 internal constant GENESIS_ARCHIVE_ROOT = 8142738430000951296386584486068033372964809139261822027365426310856631083550; uint256 internal constant FEE_JUICE_INITIAL_MINT = 20000000000; @@ -235,7 +231,7 @@ library Constants { uint256 internal constant KERNEL_CIRCUIT_PUBLIC_INPUTS_LENGTH = 601; uint256 internal constant CONSTANT_ROLLUP_DATA_LENGTH = 13; uint256 internal constant BASE_OR_MERGE_PUBLIC_INPUTS_LENGTH = 30; - uint256 internal constant BLOCK_ROOT_OR_BLOCK_MERGE_PUBLIC_INPUTS_LENGTH = 60; + uint256 internal constant BLOCK_ROOT_OR_BLOCK_MERGE_PUBLIC_INPUTS_LENGTH = 58; uint256 internal constant ROOT_ROLLUP_PUBLIC_INPUTS_LENGTH = 44; uint256 internal constant GET_NOTES_ORACLE_RETURN_LENGTH = 674; uint256 internal constant NOTE_HASHES_NUM_BYTES_PER_BASE_ROLLUP = 2048; diff --git a/l1-contracts/src/core/libraries/TimeMath.sol b/l1-contracts/src/core/libraries/TimeMath.sol index 62f7da682dc2..d7a95db52962 100644 --- a/l1-contracts/src/core/libraries/TimeMath.sol +++ b/l1-contracts/src/core/libraries/TimeMath.sol @@ -10,39 +10,60 @@ type Slot is uint256; type Epoch is uint256; -library SlotLib { - function toTimestamp(Slot _a) internal pure returns (Timestamp) { - return Timestamp.wrap(Slot.unwrap(_a) * Constants.AZTEC_SLOT_DURATION); +abstract contract TimeFns { + // @note @LHerskind The multiple cause pain and suffering in the E2E tests as we introduce + // a timeliness requirement into the publication that did not exists before, + // and at the same time have a setup that will impact the time at every tx + // because of auto-mine. By using just 1, we can make our test work + // but anything using an actual working chain would eat dung as simulating + // transactions is slower than an actual ethereum slot. + // + // The value should be a higher multiple for any actual chain + // @todo #8019 + uint256 public immutable SLOT_DURATION; + + // The duration of an epoch in slots + // @todo @LHerskind - This value should be updated when we are not blind. + // @todo #8020 + uint256 public immutable EPOCH_DURATION; + + constructor(uint256 _slotDuration, uint256 _epochDuration) { + SLOT_DURATION = _slotDuration; + EPOCH_DURATION = _epochDuration; } - function fromTimestamp(Timestamp _a) internal pure returns (Slot) { - return Slot.wrap(Timestamp.unwrap(_a) / Constants.AZTEC_SLOT_DURATION); + function toTimestamp(Slot _a) internal view returns (Timestamp) { + return Timestamp.wrap(Slot.unwrap(_a) * SLOT_DURATION); } - function positionInEpoch(Slot _a) internal pure returns (uint256) { - return Slot.unwrap(_a) % Constants.AZTEC_EPOCH_DURATION; + function slotFromTimestamp(Timestamp _a) internal view returns (Slot) { + return Slot.wrap(Timestamp.unwrap(_a) / SLOT_DURATION); } - function unwrap(Slot _a) internal pure returns (uint256) { - return Slot.unwrap(_a); + function positionInEpoch(Slot _a) internal view returns (uint256) { + return Slot.unwrap(_a) % EPOCH_DURATION; } -} -library EpochLib { - function toSlots(Epoch _a) internal pure returns (Slot) { - return Slot.wrap(Epoch.unwrap(_a) * Constants.AZTEC_EPOCH_DURATION); + function toSlots(Epoch _a) internal view returns (Slot) { + return Slot.wrap(Epoch.unwrap(_a) * EPOCH_DURATION); } - function toTimestamp(Epoch _a) internal pure returns (Timestamp) { - return SlotLib.toTimestamp(toSlots(_a)); + function toTimestamp(Epoch _a) internal view returns (Timestamp) { + return toTimestamp(toSlots(_a)); } - function fromTimestamp(Timestamp _a) internal pure returns (Epoch) { - return Epoch.wrap( - Timestamp.unwrap(_a) / (Constants.AZTEC_EPOCH_DURATION * Constants.AZTEC_SLOT_DURATION) - ); + function epochFromTimestamp(Timestamp _a) internal view returns (Epoch) { + return Epoch.wrap(Timestamp.unwrap(_a) / (EPOCH_DURATION * SLOT_DURATION)); } +} +library SlotLib { + function unwrap(Slot _a) internal pure returns (uint256) { + return Slot.unwrap(_a); + } +} + +library EpochLib { function unwrap(Epoch _a) internal pure returns (uint256) { return Epoch.unwrap(_a); } diff --git a/l1-contracts/test/Rollup.t.sol b/l1-contracts/test/Rollup.t.sol index 5ed5e5b2d702..4b66857bfdba 100644 --- a/l1-contracts/test/Rollup.t.sol +++ b/l1-contracts/test/Rollup.t.sol @@ -14,7 +14,7 @@ import {Registry} from "@aztec/governance/Registry.sol"; import {Inbox} from "@aztec/core/messagebridge/Inbox.sol"; import {Outbox} from "@aztec/core/messagebridge/Outbox.sol"; import {Errors} from "@aztec/core/libraries/Errors.sol"; -import {Rollup} from "@aztec/core/Rollup.sol"; +import {Rollup} from "./harnesses/Rollup.sol"; import {IRollup} from "@aztec/core/interfaces/IRollup.sol"; import {IProofCommitmentEscrow} from "@aztec/core/interfaces/IProofCommitmentEscrow.sol"; import {FeeJuicePortal} from "@aztec/core/FeeJuicePortal.sol"; @@ -22,11 +22,14 @@ import {Leonidas} from "@aztec/core/Leonidas.sol"; import {NaiveMerkle} from "./merkle/Naive.sol"; import {MerkleTestUtil} from "./merkle/TestUtil.sol"; import {TestERC20} from "@aztec/mock/TestERC20.sol"; +import {TestConstants} from "./harnesses/TestConstants.sol"; import {RewardDistributor} from "@aztec/governance/RewardDistributor.sol"; import {TxsDecoderHelper} from "./decoders/helpers/TxsDecoderHelper.sol"; import {IERC20Errors} from "@oz/interfaces/draft-IERC6093.sol"; -import {Timestamp, Slot, Epoch, SlotLib, EpochLib} from "@aztec/core/libraries/TimeMath.sol"; +import { + Timestamp, Slot, Epoch, SlotLib, EpochLib, TimeFns +} from "@aztec/core/libraries/TimeMath.sol"; // solhint-disable comprehensive-interface @@ -34,7 +37,7 @@ import {Timestamp, Slot, Epoch, SlotLib, EpochLib} from "@aztec/core/libraries/T * Blocks are generated using the `integration_l1_publisher.test.ts` tests. * Main use of these test is shorter cycles when updating the decoder contract. */ -contract RollupTest is DecoderBase { +contract RollupTest is DecoderBase, TimeFns { using SlotLib for Slot; using EpochLib for Epoch; @@ -42,6 +45,7 @@ contract RollupTest is DecoderBase { Inbox internal inbox; Outbox internal outbox; Rollup internal rollup; + Leonidas internal leo; MerkleTestUtil internal merkleTestUtil; TxsDecoderHelper internal txsHelper; TestERC20 internal testERC20; @@ -56,16 +60,23 @@ contract RollupTest is DecoderBase { uint256 internal privateKey; address internal signer; + constructor() TimeFns(TestConstants.AZTEC_SLOT_DURATION, TestConstants.AZTEC_EPOCH_DURATION) {} + /** * @notice Set up the contracts needed for the tests with time aligned to the provided block name */ modifier setUpFor(string memory _name) { { - Leonidas leo = new Leonidas(address(1)); + leo = new Leonidas( + address(1), + TestConstants.AZTEC_SLOT_DURATION, + TestConstants.AZTEC_EPOCH_DURATION, + TestConstants.AZTEC_TARGET_COMMITTEE_SIZE + ); DecoderBase.Full memory full = load(_name); uint256 slotNumber = full.block.decodedHeader.globalVariables.slotNumber; uint256 initialTime = - full.block.decodedHeader.globalVariables.timestamp - slotNumber * leo.SLOT_DURATION(); + full.block.decodedHeader.globalVariables.timestamp - slotNumber * SLOT_DURATION; vm.warp(initialTime); } @@ -139,10 +150,10 @@ contract RollupTest is DecoderBase { assertEq(rollup.getClaimableEpoch(), 0, "Invalid claimable epoch"); quote.epochToProve = Epoch.wrap(epochForMixedBlock); - quote.validUntilSlot = Slot.wrap(epochForMixedBlock * Constants.AZTEC_EPOCH_DURATION + 1); + quote.validUntilSlot = Slot.wrap(epochForMixedBlock * EPOCH_DURATION + 1); signedQuote = _quoteToSignedQuote(quote); - _testBlock("mixed_block_1", false, epochForMixedBlock * Constants.AZTEC_EPOCH_DURATION); + _testBlock("mixed_block_1", false, epochForMixedBlock * EPOCH_DURATION); assertEq(rollup.getClaimableEpoch(), Epoch.wrap(epochForMixedBlock), "Invalid claimable epoch"); rollup.claimEpochProofRight(signedQuote); @@ -250,7 +261,7 @@ contract RollupTest is DecoderBase { rollup.propose(header, archive, blockHash, txHashes, signatures, body); quote.epochToProve = Epoch.wrap(1); - quote.validUntilSlot = Epoch.wrap(2).toSlots(); + quote.validUntilSlot = toSlots(Epoch.wrap(2)); signedQuote = _quoteToSignedQuote(quote); rollup.claimEpochProofRight(signedQuote); (bytes32 preArchive, bytes32 preBlockHash,) = rollup.blocks(0); @@ -267,7 +278,7 @@ contract RollupTest is DecoderBase { } function testMissingProofSlashesBond(uint256 _slotToHit) public setUpFor("mixed_block_1") { - Slot lower = rollup.getCurrentSlot() + Slot.wrap(2 * Constants.AZTEC_EPOCH_DURATION); + Slot lower = rollup.getCurrentSlot() + Slot.wrap(2 * EPOCH_DURATION); Slot upper = Slot.wrap( (type(uint256).max - Timestamp.unwrap(rollup.GENESIS_TIME())) / rollup.SLOT_DURATION() ); @@ -286,7 +297,7 @@ contract RollupTest is DecoderBase { function testClaimTwice() public setUpFor("mixed_block_1") { _testBlock("mixed_block_1", false, 1); - quote.validUntilSlot = Epoch.wrap(1e9).toSlots(); + quote.validUntilSlot = toSlots(Epoch.wrap(1e9)); signedQuote = _quoteToSignedQuote(quote); rollup.claimEpochProofRight(signedQuote); @@ -299,7 +310,7 @@ contract RollupTest is DecoderBase { rollup.claimEpochProofRight(signedQuote); // warp to epoch 1 - warpToL2Slot(Constants.AZTEC_EPOCH_DURATION); + warpToL2Slot(EPOCH_DURATION); assertEq(rollup.getCurrentEpoch(), 1, "Invalid current epoch"); // We should still be trying to prove epoch 0 in epoch 1 @@ -313,9 +324,9 @@ contract RollupTest is DecoderBase { function testClaimOutsideClaimPhase() public setUpFor("mixed_block_1") { _testBlock("mixed_block_1", false, 1); - quote.validUntilSlot = Epoch.wrap(1e9).toSlots(); + quote.validUntilSlot = toSlots(Epoch.wrap(1e9)); signedQuote = _quoteToSignedQuote(quote); - warpToL2Slot(Constants.AZTEC_EPOCH_DURATION + rollup.CLAIM_DURATION_IN_L2_SLOTS()); + warpToL2Slot(EPOCH_DURATION + rollup.CLAIM_DURATION_IN_L2_SLOTS()); vm.expectRevert( abi.encodeWithSelector( @@ -330,14 +341,14 @@ contract RollupTest is DecoderBase { function testNoPruneWhenClaimExists() public setUpFor("mixed_block_1") { _testBlock("mixed_block_1", false, 1); - quote.validUntilSlot = Epoch.wrap(2).toSlots(); + quote.validUntilSlot = toSlots(Epoch.wrap(2)); signedQuote = _quoteToSignedQuote(quote); - warpToL2Slot(Constants.AZTEC_EPOCH_DURATION + rollup.CLAIM_DURATION_IN_L2_SLOTS() - 1); + warpToL2Slot(EPOCH_DURATION + rollup.CLAIM_DURATION_IN_L2_SLOTS() - 1); rollup.claimEpochProofRight(signedQuote); - warpToL2Slot(Constants.AZTEC_EPOCH_DURATION + rollup.CLAIM_DURATION_IN_L2_SLOTS()); + warpToL2Slot(EPOCH_DURATION + rollup.CLAIM_DURATION_IN_L2_SLOTS()); vm.expectRevert(abi.encodeWithSelector(Errors.Rollup__NothingToPrune.selector)); rollup.prune(); @@ -346,14 +357,14 @@ contract RollupTest is DecoderBase { function testPruneWhenClaimExpires() public setUpFor("mixed_block_1") { _testBlock("mixed_block_1", false, 1); - quote.validUntilSlot = Epoch.wrap(2).toSlots(); + quote.validUntilSlot = toSlots(Epoch.wrap(2)); signedQuote = _quoteToSignedQuote(quote); - warpToL2Slot(Constants.AZTEC_EPOCH_DURATION + rollup.CLAIM_DURATION_IN_L2_SLOTS() - 1); + warpToL2Slot(EPOCH_DURATION + rollup.CLAIM_DURATION_IN_L2_SLOTS() - 1); rollup.claimEpochProofRight(signedQuote); - warpToL2Slot(Constants.AZTEC_EPOCH_DURATION * 2); + warpToL2Slot(EPOCH_DURATION * 2); // We should still be trying to prove epoch 0 in epoch 2 vm.expectRevert(abi.encodeWithSelector(Errors.Rollup__ProofRightAlreadyClaimed.selector)); @@ -368,36 +379,36 @@ contract RollupTest is DecoderBase { function testClaimAfterPrune() public setUpFor("mixed_block_1") { _testBlock("mixed_block_1", false, 1); - quote.validUntilSlot = Epoch.wrap(3).toSlots(); + quote.validUntilSlot = toSlots(Epoch.wrap(3)); signedQuote = _quoteToSignedQuote(quote); - warpToL2Slot(Constants.AZTEC_EPOCH_DURATION + rollup.CLAIM_DURATION_IN_L2_SLOTS() - 1); + warpToL2Slot(EPOCH_DURATION + rollup.CLAIM_DURATION_IN_L2_SLOTS() - 1); rollup.claimEpochProofRight(signedQuote); - warpToL2Slot(Constants.AZTEC_EPOCH_DURATION * 3); + warpToL2Slot(EPOCH_DURATION * 3); rollup.prune(); - _testBlock("mixed_block_1", false, Epoch.wrap(3).toSlots().unwrap()); + _testBlock("mixed_block_1", false, toSlots(Epoch.wrap(3)).unwrap()); quote.epochToProve = Epoch.wrap(3); signedQuote = _quoteToSignedQuote(quote); vm.expectEmit(true, true, true, true); emit IRollup.ProofRightClaimed( - quote.epochToProve, quote.prover, address(this), quote.bondAmount, Epoch.wrap(3).toSlots() + quote.epochToProve, quote.prover, address(this), quote.bondAmount, toSlots(Epoch.wrap(3)) ); rollup.claimEpochProofRight(signedQuote); } function testPruneWhenNoProofClaim() public setUpFor("mixed_block_1") { _testBlock("mixed_block_1", false, 1); - warpToL2Slot(Constants.AZTEC_EPOCH_DURATION + rollup.CLAIM_DURATION_IN_L2_SLOTS() - 1); + warpToL2Slot(EPOCH_DURATION + rollup.CLAIM_DURATION_IN_L2_SLOTS() - 1); vm.expectRevert(abi.encodeWithSelector(Errors.Rollup__NothingToPrune.selector)); rollup.prune(); - warpToL2Slot(Constants.AZTEC_EPOCH_DURATION + rollup.CLAIM_DURATION_IN_L2_SLOTS()); + warpToL2Slot(EPOCH_DURATION + rollup.CLAIM_DURATION_IN_L2_SLOTS()); rollup.prune(); } @@ -455,7 +466,7 @@ contract RollupTest is DecoderBase { bytes32 inboxRoot2 = inbox.getRoot(2); (,, Slot slot) = rollup.blocks(1); - Slot prunableAt = slot + Epoch.wrap(2).toSlots(); + Slot prunableAt = slot + toSlots(Epoch.wrap(2)); Timestamp timeOfPrune = rollup.getTimestampForSlot(prunableAt); vm.warp(Timestamp.unwrap(timeOfPrune)); @@ -510,11 +521,11 @@ contract RollupTest is DecoderBase { rollup.setAssumeProvenThroughBlockNumber(rollup.getPendingBlockNumber()); // jump to epoch 1 - warpToL2Slot(Constants.AZTEC_EPOCH_DURATION); - _testBlock("mixed_block_2", false, Constants.AZTEC_EPOCH_DURATION); + warpToL2Slot(EPOCH_DURATION); + _testBlock("mixed_block_2", false, EPOCH_DURATION); // jump to epoch 2 - warpToL2Slot(Constants.AZTEC_EPOCH_DURATION * 2); + warpToL2Slot(EPOCH_DURATION * 2); vm.expectRevert(abi.encodeWithSelector(Errors.Rollup__NothingToPrune.selector)); rollup.prune(); @@ -523,8 +534,8 @@ contract RollupTest is DecoderBase { function testPruneDuringPropose() public setUpFor("mixed_block_1") { _testBlock("mixed_block_1", false, 1); assertEq(rollup.getEpochToProve(), 0, "Invalid epoch to prove"); - warpToL2Slot(Constants.AZTEC_EPOCH_DURATION * 2); - _testBlock("mixed_block_1", false, Epoch.wrap(2).toSlots().unwrap()); + warpToL2Slot(EPOCH_DURATION * 2); + _testBlock("mixed_block_1", false, toSlots(Epoch.wrap(2)).unwrap()); assertEq(rollup.getPendingBlockNumber(), 1, "Invalid pending block number"); assertEq(rollup.getProvenBlockNumber(), 0, "Invalid proven block number"); @@ -599,10 +610,10 @@ contract RollupTest is DecoderBase { (bytes32 preArchive, bytes32 preBlockHash,) = rollup.blocks(0); quote.epochToProve = Epoch.wrap(1); - quote.validUntilSlot = Epoch.wrap(2).toSlots(); + quote.validUntilSlot = toSlots(Epoch.wrap(2)); signedQuote = _quoteToSignedQuote(quote); - warpToL2Slot(Constants.AZTEC_EPOCH_DURATION + rollup.CLAIM_DURATION_IN_L2_SLOTS() - 1); + warpToL2Slot(EPOCH_DURATION + rollup.CLAIM_DURATION_IN_L2_SLOTS() - 1); rollup.claimEpochProofRight(signedQuote); { @@ -996,7 +1007,7 @@ contract RollupTest is DecoderBase { _proverId ]; - bytes32[] memory fees = new bytes32[](Constants.AZTEC_EPOCH_DURATION * 2); + bytes32[] memory fees = new bytes32[](EPOCH_DURATION * 2); fees[0] = bytes32(uint256(uint160(_feeRecipient))); fees[1] = bytes32(_feeAmount); diff --git a/l1-contracts/test/fee_portal/depositToAztecPublic.t.sol b/l1-contracts/test/fee_portal/depositToAztecPublic.t.sol index 403e1cf63bb6..db038cfe772a 100644 --- a/l1-contracts/test/fee_portal/depositToAztecPublic.t.sol +++ b/l1-contracts/test/fee_portal/depositToAztecPublic.t.sol @@ -8,7 +8,7 @@ import {FeeJuicePortal} from "@aztec/core/FeeJuicePortal.sol"; import {IFeeJuicePortal} from "@aztec/core/interfaces/IFeeJuicePortal.sol"; import {Constants} from "@aztec/core/libraries/ConstantsGen.sol"; import {IERC20Errors} from "@oz/interfaces/draft-IERC6093.sol"; -import {Rollup} from "@aztec/core/Rollup.sol"; +import {Rollup} from "../harnesses/Rollup.sol"; import {DataStructures} from "@aztec/core/libraries/DataStructures.sol"; import {Hash} from "@aztec/core/libraries/crypto/Hash.sol"; import {IInbox} from "@aztec/core/interfaces/messagebridge/IInbox.sol"; diff --git a/l1-contracts/test/fee_portal/distributeFees.t.sol b/l1-contracts/test/fee_portal/distributeFees.t.sol index c469e8fe74a8..bfb366e21c73 100644 --- a/l1-contracts/test/fee_portal/distributeFees.t.sol +++ b/l1-contracts/test/fee_portal/distributeFees.t.sol @@ -8,7 +8,7 @@ import {FeeJuicePortal} from "@aztec/core/FeeJuicePortal.sol"; import {IFeeJuicePortal} from "@aztec/core/interfaces/IFeeJuicePortal.sol"; import {Constants} from "@aztec/core/libraries/ConstantsGen.sol"; import {IERC20Errors} from "@oz/interfaces/draft-IERC6093.sol"; -import {Rollup} from "@aztec/core/Rollup.sol"; +import {Rollup} from "../harnesses/Rollup.sol"; import {DataStructures} from "@aztec/core/libraries/DataStructures.sol"; import {Hash} from "@aztec/core/libraries/crypto/Hash.sol"; import {Errors} from "@aztec/core/libraries/Errors.sol"; diff --git a/l1-contracts/test/governance/governance-proposer/pushProposal.t.sol b/l1-contracts/test/governance/governance-proposer/pushProposal.t.sol index 025e7b69d09d..26de9a2f3432 100644 --- a/l1-contracts/test/governance/governance-proposer/pushProposal.t.sol +++ b/l1-contracts/test/governance/governance-proposer/pushProposal.t.sol @@ -4,7 +4,7 @@ pragma solidity >=0.8.27; import {IPayload} from "@aztec/governance/interfaces/IPayload.sol"; import {IGovernanceProposer} from "@aztec/governance/interfaces/IGovernanceProposer.sol"; import {GovernanceProposerBase} from "./Base.t.sol"; -import {Leonidas} from "@aztec/core/Leonidas.sol"; +import {Leonidas} from "../../harnesses/Leonidas.sol"; import {Errors} from "@aztec/governance/libraries/Errors.sol"; import {Slot, SlotLib, Timestamp} from "@aztec/core/libraries/TimeMath.sol"; diff --git a/l1-contracts/test/governance/governance-proposer/vote.t.sol b/l1-contracts/test/governance/governance-proposer/vote.t.sol index 7102d2f10b3e..f78f9f009e04 100644 --- a/l1-contracts/test/governance/governance-proposer/vote.t.sol +++ b/l1-contracts/test/governance/governance-proposer/vote.t.sol @@ -4,7 +4,7 @@ pragma solidity >=0.8.27; import {IPayload} from "@aztec/governance/interfaces/IPayload.sol"; import {IGovernanceProposer} from "@aztec/governance/interfaces/IGovernanceProposer.sol"; import {GovernanceProposerBase} from "./Base.t.sol"; -import {Leonidas} from "@aztec/core/Leonidas.sol"; +import {Leonidas} from "../../harnesses/Leonidas.sol"; import {Errors} from "@aztec/governance/libraries/Errors.sol"; import {Slot, SlotLib, Timestamp} from "@aztec/core/libraries/TimeMath.sol"; diff --git a/l1-contracts/test/governance/scenario/UpgradeGovernanceProposerTest.t.sol b/l1-contracts/test/governance/scenario/UpgradeGovernanceProposerTest.t.sol index 58a05584806c..aea558c9f564 100644 --- a/l1-contracts/test/governance/scenario/UpgradeGovernanceProposerTest.t.sol +++ b/l1-contracts/test/governance/scenario/UpgradeGovernanceProposerTest.t.sol @@ -4,7 +4,7 @@ pragma solidity >=0.8.27; import {IPayload} from "@aztec/governance/interfaces/IPayload.sol"; import {TestBase} from "@test/base/Base.sol"; import {IMintableERC20} from "@aztec/governance/interfaces/IMintableERC20.sol"; -import {Rollup} from "@aztec/core/Rollup.sol"; +import {Rollup} from "../../harnesses/Rollup.sol"; import {Governance} from "@aztec/governance/Governance.sol"; import {GovernanceProposer} from "@aztec/governance/GovernanceProposer.sol"; import {Registry} from "@aztec/governance/Registry.sol"; diff --git a/l1-contracts/test/harnesses/Leonidas.sol b/l1-contracts/test/harnesses/Leonidas.sol new file mode 100644 index 000000000000..f19deae25508 --- /dev/null +++ b/l1-contracts/test/harnesses/Leonidas.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2024 Aztec Labs. +pragma solidity >=0.8.27; + +import {Leonidas as RealLeonidas} from "@aztec/core/Leonidas.sol"; +import {TestConstants} from "./TestConstants.sol"; + +contract Leonidas is RealLeonidas { + constructor(address _ares) + RealLeonidas( + _ares, + TestConstants.AZTEC_SLOT_DURATION, + TestConstants.AZTEC_EPOCH_DURATION, + TestConstants.AZTEC_TARGET_COMMITTEE_SIZE + ) + {} +} diff --git a/l1-contracts/test/harnesses/Rollup.sol b/l1-contracts/test/harnesses/Rollup.sol new file mode 100644 index 000000000000..7897a61ad17b --- /dev/null +++ b/l1-contracts/test/harnesses/Rollup.sol @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2024 Aztec Labs. +pragma solidity >=0.8.27; + +import {IFeeJuicePortal} from "@aztec/core/interfaces/IFeeJuicePortal.sol"; +import {IRewardDistributor} from "@aztec/governance/interfaces/IRewardDistributor.sol"; +import {Rollup as RealRollup} from "@aztec/core/Rollup.sol"; +import {TestConstants} from "./TestConstants.sol"; + +contract Rollup is RealRollup { + constructor( + IFeeJuicePortal _fpcJuicePortal, + IRewardDistributor _rewardDistributor, + bytes32 _vkTreeRoot, + bytes32 _protocolContractTreeRoot, + address _ares, + address[] memory _validators + ) + RealRollup( + _fpcJuicePortal, + _rewardDistributor, + _vkTreeRoot, + _protocolContractTreeRoot, + _ares, + _validators, + RealRollup.Config({ + aztecSlotDuration: TestConstants.AZTEC_SLOT_DURATION, + aztecEpochDuration: TestConstants.AZTEC_EPOCH_DURATION, + targetCommitteeSize: TestConstants.AZTEC_TARGET_COMMITTEE_SIZE, + aztecEpochProofClaimWindowInL2Slots: TestConstants.AZTEC_EPOCH_PROOF_CLAIM_WINDOW_IN_L2_SLOTS + }) + ) + {} +} diff --git a/l1-contracts/test/harnesses/TestConstants.sol b/l1-contracts/test/harnesses/TestConstants.sol new file mode 100644 index 000000000000..4a79b3c97e7d --- /dev/null +++ b/l1-contracts/test/harnesses/TestConstants.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2024 Aztec Labs. + +pragma solidity >=0.8.27; + +library TestConstants { + uint256 internal constant ETHEREUM_SLOT_DURATION = 12; + 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_EPOCH_PROOF_CLAIM_WINDOW_IN_L2_SLOTS = 13; +} diff --git a/l1-contracts/test/portals/TokenPortal.t.sol b/l1-contracts/test/portals/TokenPortal.t.sol index 51df3054a537..105de7afe29d 100644 --- a/l1-contracts/test/portals/TokenPortal.t.sol +++ b/l1-contracts/test/portals/TokenPortal.t.sol @@ -3,7 +3,7 @@ pragma solidity >=0.8.27; import "forge-std/Test.sol"; // Rollup Processor -import {Rollup} from "@aztec/core/Rollup.sol"; +import {Rollup} from "../harnesses/Rollup.sol"; import {Constants} from "@aztec/core/libraries/ConstantsGen.sol"; import {Registry} from "@aztec/governance/Registry.sol"; import {DataStructures} from "@aztec/core/libraries/DataStructures.sol"; diff --git a/l1-contracts/test/portals/UniswapPortal.t.sol b/l1-contracts/test/portals/UniswapPortal.t.sol index 11783bead7d1..84afe101e347 100644 --- a/l1-contracts/test/portals/UniswapPortal.t.sol +++ b/l1-contracts/test/portals/UniswapPortal.t.sol @@ -3,7 +3,7 @@ pragma solidity >=0.8.27; import "forge-std/Test.sol"; // Rollup Processor -import {Rollup} from "@aztec/core/Rollup.sol"; +import {Rollup} from "../harnesses/Rollup.sol"; import {Registry} from "@aztec/governance/Registry.sol"; import {DataStructures} from "@aztec/core/libraries/DataStructures.sol"; import {DataStructures as PortalDataStructures} from "./DataStructures.sol"; diff --git a/l1-contracts/test/prover-coordination/ProofCommitmentEscrow.t.sol b/l1-contracts/test/prover-coordination/ProofCommitmentEscrow.t.sol index 3d0bdc529e42..45178dc9b9ea 100644 --- a/l1-contracts/test/prover-coordination/ProofCommitmentEscrow.t.sol +++ b/l1-contracts/test/prover-coordination/ProofCommitmentEscrow.t.sol @@ -7,7 +7,7 @@ import {Test} from "forge-std/Test.sol"; import {ProofCommitmentEscrow} from "@aztec/core/ProofCommitmentEscrow.sol"; import {Errors} from "@aztec/core/libraries/Errors.sol"; import {Timestamp} from "@aztec/core/libraries/TimeMath.sol"; - +import {TestConstants} from "../harnesses/TestConstants.sol"; import {TestERC20} from "@aztec/mock/TestERC20.sol"; // solhint-disable comprehensive-interface @@ -32,7 +32,9 @@ contract TestProofCommitmentEscrow is Test { function setUp() public { TOKEN = new TestERC20(); - ESCROW = new ProofCommitmentEscrow(TOKEN, address(this)); + ESCROW = new ProofCommitmentEscrow( + TOKEN, address(this), TestConstants.AZTEC_SLOT_DURATION, TestConstants.AZTEC_EPOCH_DURATION + ); } function testDeposit() public setupWithApproval(address(42), 100) { diff --git a/l1-contracts/test/sparta/Sparta.t.sol b/l1-contracts/test/sparta/Sparta.t.sol index caaac46645f4..b8893758cc79 100644 --- a/l1-contracts/test/sparta/Sparta.t.sol +++ b/l1-contracts/test/sparta/Sparta.t.sol @@ -12,8 +12,8 @@ import {Inbox} from "@aztec/core/messagebridge/Inbox.sol"; import {Outbox} from "@aztec/core/messagebridge/Outbox.sol"; import {Errors} from "@aztec/core/libraries/Errors.sol"; import {Registry} from "@aztec/governance/Registry.sol"; -import {Rollup} from "@aztec/core/Rollup.sol"; -import {Leonidas} from "@aztec/core/Leonidas.sol"; +import {Rollup} from "../harnesses/Rollup.sol"; +import {Leonidas} from "../harnesses/Leonidas.sol"; import {NaiveMerkle} from "../merkle/Naive.sol"; import {MerkleTestUtil} from "../merkle/TestUtil.sol"; import {TestERC20} from "@aztec/mock/TestERC20.sol"; diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/abis/block_root_or_block_merge_public_inputs.nr b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/abis/block_root_or_block_merge_public_inputs.nr index 2d8408737b91..d325e1d2ed3b 100644 --- a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/abis/block_root_or_block_merge_public_inputs.nr +++ b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/abis/block_root_or_block_merge_public_inputs.nr @@ -2,7 +2,8 @@ use dep::types::{ abis::{append_only_tree_snapshot::AppendOnlyTreeSnapshot, global_variables::GlobalVariables}, address::EthAddress, constants::{ - AZTEC_EPOCH_DURATION, BLOCK_ROOT_OR_BLOCK_MERGE_PUBLIC_INPUTS_LENGTH, FEE_RECIPIENT_LENGTH, + AZTEC_MAX_EPOCH_DURATION, BLOCK_ROOT_OR_BLOCK_MERGE_PUBLIC_INPUTS_LENGTH, + FEE_RECIPIENT_LENGTH, }, traits::{Deserialize, Empty, Serialize}, utils::reader::Reader, @@ -48,7 +49,7 @@ pub struct BlockRootOrBlockMergePublicInputs { start_global_variables: GlobalVariables, // Global variables for the first block in the range end_global_variables: GlobalVariables, // Global variables for the last block in the range out_hash: Field, // Merkle node of the L2-to-L1 messages merkle roots in the block range - fees: [FeeRecipient; AZTEC_EPOCH_DURATION], // Concatenation of all coinbase and fees for the block range + fees: [FeeRecipient; AZTEC_MAX_EPOCH_DURATION], // Concatenation of all coinbase and fees for the block range vk_tree_root: Field, // Root of allowed vk tree protocol_contract_tree_root: Field, // Root of protocol contract tree prover_id: Field, // TODO(#7346): Temporarily added prover_id while we verify block-root proofs on L1 @@ -70,7 +71,7 @@ impl Empty for BlockRootOrBlockMergePublicInputs { start_global_variables: GlobalVariables::empty(), end_global_variables: GlobalVariables::empty(), out_hash: 0, - fees: [FeeRecipient::empty(); AZTEC_EPOCH_DURATION], + fees: [FeeRecipient::empty(); AZTEC_MAX_EPOCH_DURATION], vk_tree_root: 0, protocol_contract_tree_root: 0, prover_id: 0, @@ -106,7 +107,7 @@ impl Serialize for BlockRootOrBl fields.extend_from_array(self.start_global_variables.serialize()); fields.extend_from_array(self.end_global_variables.serialize()); fields.push(self.out_hash as Field); - for i in 0..AZTEC_EPOCH_DURATION { + for i in 0..AZTEC_MAX_EPOCH_DURATION { fields.extend_from_array(self.fees[i].serialize()); } fields.push(self.vk_tree_root); @@ -133,7 +134,7 @@ impl Deserialize for BlockRootOr out_hash: reader.read(), fees: reader.read_struct_array( FeeRecipient::deserialize, - [FeeRecipient::empty(); AZTEC_EPOCH_DURATION], + [FeeRecipient::empty(); AZTEC_MAX_EPOCH_DURATION], ), vk_tree_root: reader.read(), protocol_contract_tree_root: reader.read(), diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/block_root/block_root_rollup_inputs.nr b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/block_root/block_root_rollup_inputs.nr index fc2c8e70d713..bd2ee6a1cb6e 100644 --- a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/block_root/block_root_rollup_inputs.nr +++ b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/block_root/block_root_rollup_inputs.nr @@ -9,7 +9,7 @@ use parity_lib::root::root_rollup_parity_input::RootRollupParityInput; use types::{ abis::append_only_tree_snapshot::AppendOnlyTreeSnapshot, constants::{ - ARCHIVE_HEIGHT, AZTEC_EPOCH_DURATION, L1_TO_L2_MSG_SUBTREE_HEIGHT, + ARCHIVE_HEIGHT, AZTEC_MAX_EPOCH_DURATION, L1_TO_L2_MSG_SUBTREE_HEIGHT, L1_TO_L2_MSG_SUBTREE_SIBLING_PATH_LENGTH, MERGE_ROLLUP_INDEX, NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP, PRIVATE_BASE_ROLLUP_VK_INDEX, PUBLIC_BASE_ROLLUP_VK_INDEX, @@ -134,7 +134,7 @@ impl BlockRootRollupInputs { 0, ); - let mut fee_arr = [FeeRecipient::empty(); AZTEC_EPOCH_DURATION]; + let mut fee_arr = [FeeRecipient::empty(); AZTEC_MAX_EPOCH_DURATION]; fee_arr[0] = FeeRecipient { recipient: left.constants.global_variables.coinbase, value: total_fees }; diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/block_root/empty_block_root_rollup_inputs.nr b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/block_root/empty_block_root_rollup_inputs.nr index 18ba14c564d5..bbd263c34e01 100644 --- a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/block_root/empty_block_root_rollup_inputs.nr +++ b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/block_root/empty_block_root_rollup_inputs.nr @@ -3,7 +3,7 @@ use crate::abis::block_root_or_block_merge_public_inputs::FeeRecipient; use types::abis::{ append_only_tree_snapshot::AppendOnlyTreeSnapshot, global_variables::GlobalVariables, }; -use types::constants::AZTEC_EPOCH_DURATION; +use types::constants::AZTEC_MAX_EPOCH_DURATION; pub struct EmptyBlockRootRollupInputs { archive: AppendOnlyTreeSnapshot, @@ -25,7 +25,7 @@ impl EmptyBlockRootRollupInputs { start_global_variables: self.global_variables, end_global_variables: self.global_variables, out_hash: 0, // out_hash is ignored when merging if the block proof is padding - fees: [FeeRecipient::empty(); AZTEC_EPOCH_DURATION], + fees: [FeeRecipient::empty(); AZTEC_MAX_EPOCH_DURATION], vk_tree_root: self.vk_tree_root, protocol_contract_tree_root: self.protocol_contract_tree_root, prover_id: self.prover_id, diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/components.nr b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/components.nr index 3e1a1027d4e6..ba64a36779d6 100644 --- a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/components.nr +++ b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/components.nr @@ -12,7 +12,7 @@ use dep::types::{ public_data_write::PublicDataWrite, }, constants::{ - AZTEC_EPOCH_DURATION, MAX_L2_TO_L1_MSGS_PER_TX, MAX_NOTE_HASHES_PER_TX, + AZTEC_MAX_EPOCH_DURATION, MAX_L2_TO_L1_MSGS_PER_TX, MAX_NOTE_HASHES_PER_TX, MAX_NULLIFIERS_PER_TX, MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, MAX_UNENCRYPTED_LOGS_PER_TX, }, @@ -143,11 +143,11 @@ pub fn accumulate_fees( pub fn accumulate_blocks_fees( left: BlockRootOrBlockMergePublicInputs, right: BlockRootOrBlockMergePublicInputs, -) -> [FeeRecipient; AZTEC_EPOCH_DURATION] { +) -> [FeeRecipient; AZTEC_MAX_EPOCH_DURATION] { let left_len = array_length(left.fees); let right_len = array_length(right.fees); assert( - left_len + right_len <= AZTEC_EPOCH_DURATION, + left_len + right_len <= AZTEC_MAX_EPOCH_DURATION, "too many fee payment structs accumulated in rollup", ); // TODO(Miranda): combine fees with same recipient depending on rollup structure diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/root/root_rollup_public_inputs.nr b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/root/root_rollup_public_inputs.nr index 69469e41881d..2383e1481e17 100644 --- a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/root/root_rollup_public_inputs.nr +++ b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/root/root_rollup_public_inputs.nr @@ -1,6 +1,6 @@ use crate::abis::block_root_or_block_merge_public_inputs::FeeRecipient; use dep::types::abis::append_only_tree_snapshot::AppendOnlyTreeSnapshot; -use dep::types::constants::AZTEC_EPOCH_DURATION; +use dep::types::constants::AZTEC_MAX_EPOCH_DURATION; pub struct RootRollupPublicInputs { // Snapshot of archive tree before/after this rollup has been processed @@ -11,7 +11,7 @@ pub struct RootRollupPublicInputs { end_timestamp: u64, end_block_number: Field, out_hash: Field, - fees: [FeeRecipient; AZTEC_EPOCH_DURATION], + fees: [FeeRecipient; AZTEC_MAX_EPOCH_DURATION], vk_tree_root: Field, protocol_contract_tree_root: Field, prover_id: Field, diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr b/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr index 869a90d4b21d..d896961d764b 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr @@ -135,13 +135,7 @@ pub global INITIALIZATION_SLOT_SEPARATOR: Field = 1000_000_000; pub global INITIAL_L2_BLOCK_NUM: Field = 1; pub global PRIVATE_LOG_SIZE_IN_BYTES: u32 = 576; // This is currently defined by aztec-nr/aztec/src/encrypted_logs/payload.nr. See the comment there for how this value is calculated. pub global BLOB_SIZE_IN_BYTES: Field = 31 * 4096; -pub global ETHEREUM_SLOT_DURATION: u32 = 12; -// AZTEC_SLOT_DURATION should be a multiple of ETHEREUM_SLOT_DURATION -pub global AZTEC_SLOT_DURATION: u32 = ETHEREUM_SLOT_DURATION * 2; -pub global AZTEC_EPOCH_DURATION: u32 = 16; -pub global AZTEC_TARGET_COMMITTEE_SIZE: u32 = 48; -// The number of AZTEC_SLOTS that we can wait for a proof of an epoch to be produced. -pub global AZTEC_EPOCH_PROOF_CLAIM_WINDOW_IN_L2_SLOTS: u32 = 13; +pub global AZTEC_MAX_EPOCH_DURATION: u32 = 32; // The following is taken from building a block and looking at the `lastArchive` value in it. // You can run the `integration_l1_publisher.test.ts` and look at the first blocks in the fixtures. pub global GENESIS_ARCHIVE_ROOT: Field = @@ -503,12 +497,12 @@ pub global BLOCK_ROOT_OR_BLOCK_MERGE_PUBLIC_INPUTS_LENGTH: u32 = 2 + 1 /* end_block_hash */ + 2 * GLOBAL_VARIABLES_LENGTH + 1 /* out_hash */ - + AZTEC_EPOCH_DURATION * FEE_RECIPIENT_LENGTH + + AZTEC_MAX_EPOCH_DURATION * FEE_RECIPIENT_LENGTH + 1 /* vk_tree_root */ + 1 /* protocol_contract_tree_root */ + 1 /* prover_id */; pub global ROOT_ROLLUP_PUBLIC_INPUTS_LENGTH: u32 = - 2 * APPEND_ONLY_TREE_SNAPSHOT_LENGTH + 8 + AZTEC_EPOCH_DURATION * FEE_RECIPIENT_LENGTH; + 2 * APPEND_ONLY_TREE_SNAPSHOT_LENGTH + 8 + AZTEC_MAX_EPOCH_DURATION * FEE_RECIPIENT_LENGTH; pub global GET_NOTES_ORACLE_RETURN_LENGTH: u32 = 674; pub global NOTE_HASHES_NUM_BYTES_PER_BASE_ROLLUP: u32 = 32 * MAX_NOTE_HASHES_PER_TX; diff --git a/yarn-project/archiver/src/archiver/archiver.test.ts b/yarn-project/archiver/src/archiver/archiver.test.ts index 54fa90293480..4338feb084c8 100644 --- a/yarn-project/archiver/src/archiver/archiver.test.ts +++ b/yarn-project/archiver/src/archiver/archiver.test.ts @@ -6,7 +6,8 @@ import { LogType, UnencryptedL2BlockL2Logs, } from '@aztec/circuit-types'; -import { ETHEREUM_SLOT_DURATION, GENESIS_ARCHIVE_ROOT } from '@aztec/circuits.js'; +import { GENESIS_ARCHIVE_ROOT } from '@aztec/circuits.js'; +import { DefaultL1ContractsConfig } from '@aztec/ethereum'; import { EthAddress } from '@aztec/foundation/eth-address'; import { Fr } from '@aztec/foundation/fields'; import { sleep } from '@aztec/foundation/sleep'; @@ -60,7 +61,7 @@ describe('Archiver', () => { now = +new Date(); publicClient = mock>({ getBlock: ((args: any) => ({ - timestamp: args.blockNumber * BigInt(ETHEREUM_SLOT_DURATION) + BigInt(now), + timestamp: args.blockNumber * BigInt(DefaultL1ContractsConfig.ethereumSlotDuration) + BigInt(now), })) as any, }); @@ -98,7 +99,10 @@ describe('Archiver', () => { let latestBlockNum = await archiver.getBlockNumber(); expect(latestBlockNum).toEqual(0); - blocks.forEach((b, i) => (b.header.globalVariables.timestamp = new Fr(now + ETHEREUM_SLOT_DURATION * (i + 1)))); + blocks.forEach( + (b, i) => + (b.header.globalVariables.timestamp = new Fr(now + DefaultL1ContractsConfig.ethereumSlotDuration * (i + 1))), + ); const rollupTxs = blocks.map(makeRollupTx); publicClient.getBlockNumber.mockResolvedValueOnce(2500n).mockResolvedValueOnce(2600n).mockResolvedValueOnce(2700n); diff --git a/yarn-project/archiver/src/archiver/archiver.ts b/yarn-project/archiver/src/archiver/archiver.ts index 08f91583e2f3..4c8adef9a6f3 100644 --- a/yarn-project/archiver/src/archiver/archiver.ts +++ b/yarn-project/archiver/src/archiver/archiver.ts @@ -23,7 +23,6 @@ import { type ContractDataSource, ContractInstanceDeployedEvent, type ContractInstanceWithAddress, - ETHEREUM_SLOT_DURATION, type ExecutablePrivateFunctionWithMembershipProof, type FunctionSelector, type Header, @@ -161,6 +160,8 @@ export class Archiver implements ArchiveSource { rollup.read.GENESIS_TIME(), ] as const); + const { aztecEpochDuration: epochDuration, aztecSlotDuration: slotDuration, ethereumSlotDuration } = config; + const archiver = new Archiver( publicClient, config.l1Contracts.rollupAddress, @@ -169,7 +170,7 @@ export class Archiver implements ArchiveSource { archiverStore, config.archiverPollingIntervalMS ?? 10_000, new ArchiverInstrumentation(telemetry), - { l1StartBlock, l1GenesisTime }, + { l1StartBlock, l1GenesisTime, epochDuration, slotDuration, ethereumSlotDuration }, ); await archiver.start(blockUntilSynced); return archiver; @@ -270,7 +271,7 @@ export class Archiver implements ArchiveSource { private async handleEpochPrune(provenBlockNumber: bigint, currentL1BlockNumber: bigint) { const localPendingBlockNumber = BigInt(await this.getBlockNumber()); - const time = (this.l1Timestamp ?? 0n) + BigInt(ETHEREUM_SLOT_DURATION); + const time = (this.l1Timestamp ?? 0n) + BigInt(this.l1constants.ethereumSlotDuration); const canPrune = localPendingBlockNumber > provenBlockNumber && @@ -502,7 +503,7 @@ export class Archiver implements ArchiveSource { } public async getBlocksForEpoch(epochNumber: bigint): Promise { - const [start, end] = getSlotRangeForEpoch(epochNumber); + const [start, end] = getSlotRangeForEpoch(epochNumber, this.l1constants); const blocks: L2Block[] = []; // Walk the list of blocks backwards and filter by slots matching the requested epoch. @@ -523,7 +524,7 @@ export class Archiver implements ArchiveSource { // The epoch is complete if the current L2 block is the last one in the epoch (or later) const header = await this.getBlockHeader('latest'); const slot = header?.globalVariables.slotNumber.toBigInt(); - const [_startSlot, endSlot] = getSlotRangeForEpoch(epochNumber); + const [_startSlot, endSlot] = getSlotRangeForEpoch(epochNumber, this.l1constants); if (slot && slot >= endSlot) { return true; } @@ -990,9 +991,15 @@ class ArchiverStoreHelper type L1RollupConstants = { l1StartBlock: bigint; l1GenesisTime: bigint; + slotDuration: number; + epochDuration: number; + ethereumSlotDuration: number; }; const EmptyL1RollupConstants: L1RollupConstants = { l1StartBlock: 0n, l1GenesisTime: 0n, + epochDuration: 0, + slotDuration: 0, + ethereumSlotDuration: 0, }; diff --git a/yarn-project/archiver/src/archiver/config.ts b/yarn-project/archiver/src/archiver/config.ts index 0412ddd7aa23..a3115f7da679 100644 --- a/yarn-project/archiver/src/archiver/config.ts +++ b/yarn-project/archiver/src/archiver/config.ts @@ -1,4 +1,10 @@ -import { type L1ContractAddresses, type L1ReaderConfig, l1ReaderConfigMappings } from '@aztec/ethereum'; +import { + type L1ContractAddresses, + type L1ContractsConfig, + type L1ReaderConfig, + l1ContractsConfigMappings, + l1ReaderConfigMappings, +} from '@aztec/ethereum'; import { type ConfigMappingsType, getConfigFromMappings, numberConfigHelper } from '@aztec/foundation/config'; /** @@ -39,7 +45,8 @@ export type ArchiverConfig = { /** The max number of logs that can be obtained in 1 "getUnencryptedLogs" call. */ maxLogs?: number; -} & L1ReaderConfig; +} & L1ReaderConfig & + L1ContractsConfig; export const archiverConfigMappings: ConfigMappingsType = { archiverUrl: { @@ -67,6 +74,7 @@ export const archiverConfigMappings: ConfigMappingsType = { description: 'The polling interval viem uses in ms', ...numberConfigHelper(1000), }, + ...l1ContractsConfigMappings, }; /** diff --git a/yarn-project/archiver/src/archiver/epoch_helpers.ts b/yarn-project/archiver/src/archiver/epoch_helpers.ts index 2612329dd067..f84bafeee38a 100644 --- a/yarn-project/archiver/src/archiver/epoch_helpers.ts +++ b/yarn-project/archiver/src/archiver/epoch_helpers.ts @@ -1,26 +1,30 @@ -import { AZTEC_EPOCH_DURATION, AZTEC_SLOT_DURATION } from '@aztec/circuits.js'; +type TimeConstants = { + l1GenesisTime: bigint; + epochDuration: number; + slotDuration: number; +}; /** Returns the slot number for a given timestamp. */ -export function getSlotAtTimestamp(ts: bigint, constants: { l1GenesisTime: bigint }) { - return ts < constants.l1GenesisTime ? 0n : (ts - constants.l1GenesisTime) / BigInt(AZTEC_SLOT_DURATION); +export function getSlotAtTimestamp(ts: bigint, constants: Pick) { + return ts < constants.l1GenesisTime ? 0n : (ts - constants.l1GenesisTime) / BigInt(constants.slotDuration); } /** Returns the epoch number for a given timestamp. */ -export function getEpochNumberAtTimestamp(ts: bigint, constants: { l1GenesisTime: bigint }) { - return getSlotAtTimestamp(ts, constants) / BigInt(AZTEC_EPOCH_DURATION); +export function getEpochNumberAtTimestamp(ts: bigint, constants: TimeConstants) { + return getSlotAtTimestamp(ts, constants) / BigInt(constants.epochDuration); } /** Returns the range of slots (inclusive) for a given epoch number. */ -export function getSlotRangeForEpoch(epochNumber: bigint) { - const startSlot = epochNumber * BigInt(AZTEC_EPOCH_DURATION); - return [startSlot, startSlot + BigInt(AZTEC_EPOCH_DURATION) - 1n]; +export function getSlotRangeForEpoch(epochNumber: bigint, constants: Pick) { + const startSlot = epochNumber * BigInt(constants.epochDuration); + return [startSlot, startSlot + BigInt(constants.epochDuration) - 1n]; } /** Returns the range of L1 timestamps (inclusive) for a given epoch number. */ -export function getTimestampRangeForEpoch(epochNumber: bigint, constants: { l1GenesisTime: bigint }) { - const [startSlot, endSlot] = getSlotRangeForEpoch(epochNumber); +export function getTimestampRangeForEpoch(epochNumber: bigint, constants: TimeConstants) { + const [startSlot, endSlot] = getSlotRangeForEpoch(epochNumber, constants); return [ - constants.l1GenesisTime + startSlot * BigInt(AZTEC_SLOT_DURATION), - constants.l1GenesisTime + endSlot * BigInt(AZTEC_SLOT_DURATION), + constants.l1GenesisTime + startSlot * BigInt(constants.slotDuration), + constants.l1GenesisTime + endSlot * BigInt(constants.slotDuration), ]; } diff --git a/yarn-project/archiver/src/test/mock_l2_block_source.ts b/yarn-project/archiver/src/test/mock_l2_block_source.ts index dc85bdfdc662..86ffce4ae507 100644 --- a/yarn-project/archiver/src/test/mock_l2_block_source.ts +++ b/yarn-project/archiver/src/test/mock_l2_block_source.ts @@ -1,5 +1,6 @@ import { L2Block, type L2BlockSource, type L2Tips, type TxHash, TxReceipt, TxStatus } from '@aztec/circuit-types'; import { EthAddress, type Header } from '@aztec/circuits.js'; +import { DefaultL1ContractsConfig } from '@aztec/ethereum'; import { createDebugLogger } from '@aztec/foundation/log'; import { getSlotRangeForEpoch } from '../archiver/epoch_helpers.js'; @@ -103,7 +104,8 @@ export class MockL2BlockSource implements L2BlockSource { } getBlocksForEpoch(epochNumber: bigint): Promise { - const [start, end] = getSlotRangeForEpoch(epochNumber); + const epochDuration = DefaultL1ContractsConfig.aztecEpochDuration; + const [start, end] = getSlotRangeForEpoch(epochNumber, { epochDuration }); const blocks = this.l2Blocks.filter(b => { const slot = b.header.globalVariables.slotNumber.toBigInt(); return slot >= start && slot <= end; diff --git a/yarn-project/aztec.js/src/utils/cheat_codes.ts b/yarn-project/aztec.js/src/utils/cheat_codes.ts index 8a89c790aa6e..f6079d19d360 100644 --- a/yarn-project/aztec.js/src/utils/cheat_codes.ts +++ b/yarn-project/aztec.js/src/utils/cheat_codes.ts @@ -1,5 +1,5 @@ import { type EpochProofClaim, type Note, type PXE } from '@aztec/circuit-types'; -import { AZTEC_EPOCH_DURATION, AZTEC_SLOT_DURATION, type AztecAddress, EthAddress, Fr } from '@aztec/circuits.js'; +import { type AztecAddress, EthAddress, Fr } from '@aztec/circuits.js'; import { deriveStorageSlotInMap } from '@aztec/circuits.js/hash'; import { type L1ContractAddresses } from '@aztec/ethereum'; import { toBigIntBE, toHex } from '@aztec/foundation/bigint-buffer'; @@ -169,7 +169,7 @@ export class EthCheatCodes { * Set the next block timestamp and mines the block * @param timestamp - The timestamp to set the next block to */ - public async warp(timestamp: number): Promise { + public async warp(timestamp: number | bigint): Promise { const res = await this.rpcCall('evm_setNextBlockTimestamp', [timestamp]); if (res.error) { throw new Error(`Error warping: ${res.error.message}`); @@ -298,7 +298,7 @@ export class RollupCheatCodes { private logger = createDebugLogger('aztec:js:cheat_codes'); - constructor(private ethCheatCodes: EthCheatCodes, private addresses: Pick) { + constructor(private ethCheatCodes: EthCheatCodes, addresses: Pick) { this.client = createWalletClient({ chain: foundry, transport: http(ethCheatCodes.rpcUrl) }).extend(publicActions); this.rollup = getContract({ abi: RollupAbi, @@ -333,11 +333,20 @@ export class RollupCheatCodes { return { pending, proven }; } + public async getConfig(): Promise<{ epochDuration: bigint; slotDuration: bigint }> { + const [epochDuration, slotDuration] = await Promise.all([ + this.rollup.read.EPOCH_DURATION(), + this.rollup.read.SLOT_DURATION(), + ]); + return { epochDuration, slotDuration }; + } + /** Warps time in L1 until the next epoch */ public async advanceToNextEpoch() { const slot = await this.getSlot(); - const slotsUntilNextEpoch = BigInt(AZTEC_EPOCH_DURATION) - (slot % BigInt(AZTEC_EPOCH_DURATION)) + 1n; - const timeToNextEpoch = slotsUntilNextEpoch * BigInt(AZTEC_SLOT_DURATION); + const { epochDuration, slotDuration } = await this.getConfig(); + const slotsUntilNextEpoch = epochDuration - (slot % epochDuration) + 1n; + const timeToNextEpoch = slotsUntilNextEpoch * slotDuration; const l1Timestamp = BigInt((await this.client.getBlock()).timestamp); await this.ethCheatCodes.warp(Number(l1Timestamp + timeToNextEpoch)); this.logger.verbose(`Advanced to next epoch`); @@ -348,8 +357,9 @@ export class RollupCheatCodes { * @param howMany - The number of slots to advance. */ public async advanceSlots(howMany: number) { - const l1Timestamp = Number((await this.client.getBlock()).timestamp); - const timeToWarp = howMany * AZTEC_SLOT_DURATION; + const l1Timestamp = (await this.client.getBlock()).timestamp; + const slotDuration = await this.rollup.read.SLOT_DURATION(); + const timeToWarp = BigInt(howMany) * slotDuration; await this.ethCheatCodes.warp(l1Timestamp + timeToWarp); const [slot, epoch] = await Promise.all([this.getSlot(), this.getEpoch()]); this.logger.verbose(`Advanced ${howMany} slots up to slot ${slot} in epoch ${epoch}`); diff --git a/yarn-project/aztec/src/sandbox.ts b/yarn-project/aztec/src/sandbox.ts index 4282774361dd..f1afd126b827 100644 --- a/yarn-project/aztec/src/sandbox.ts +++ b/yarn-project/aztec/src/sandbox.ts @@ -4,7 +4,13 @@ import { AnvilTestWatcher, EthCheatCodes, SignerlessWallet, retryUntil } from '@ import { DefaultMultiCallEntrypoint } from '@aztec/aztec.js/entrypoint'; import { type AztecNode } from '@aztec/circuit-types'; import { setupCanonicalL2FeeJuice } from '@aztec/cli/misc'; -import { type DeployL1Contracts, NULL_KEY, createEthereumChain, deployL1Contracts } from '@aztec/ethereum'; +import { + type DeployL1Contracts, + NULL_KEY, + createEthereumChain, + deployL1Contracts, + getL1ContractsConfigEnvVars, +} from '@aztec/ethereum'; import { createDebugLogger } from '@aztec/foundation/log'; import { getVKTreeRoot } from '@aztec/noir-protocol-circuits-types'; import { ProtocolContractAddress, protocolContractTreeRoot } from '@aztec/protocol-contracts'; @@ -80,6 +86,7 @@ export async function deployContractsToL1( protocolContractTreeRoot, assumeProvenThrough: opts.assumeProvenThroughBlockNumber, salt: opts.salt, + ...getL1ContractsConfigEnvVars(), }), ); diff --git a/yarn-project/circuits.js/src/constants.gen.ts b/yarn-project/circuits.js/src/constants.gen.ts index a0c8f2558cd4..28dc8a49a37c 100644 --- a/yarn-project/circuits.js/src/constants.gen.ts +++ b/yarn-project/circuits.js/src/constants.gen.ts @@ -81,11 +81,7 @@ export const INITIALIZATION_SLOT_SEPARATOR = 1000000000; export const INITIAL_L2_BLOCK_NUM = 1; export const PRIVATE_LOG_SIZE_IN_BYTES = 576; export const BLOB_SIZE_IN_BYTES = 126976; -export const ETHEREUM_SLOT_DURATION = 12; -export const AZTEC_SLOT_DURATION = 24; -export const AZTEC_EPOCH_DURATION = 16; -export const AZTEC_TARGET_COMMITTEE_SIZE = 48; -export const AZTEC_EPOCH_PROOF_CLAIM_WINDOW_IN_L2_SLOTS = 13; +export const AZTEC_MAX_EPOCH_DURATION = 32; export const GENESIS_ARCHIVE_ROOT = 8142738430000951296386584486068033372964809139261822027365426310856631083550n; export const FEE_JUICE_INITIAL_MINT = 20000000000; export const PUBLIC_DISPATCH_SELECTOR = 3578010381; @@ -214,7 +210,7 @@ export const VM_CIRCUIT_PUBLIC_INPUTS_LENGTH = 2341; export const KERNEL_CIRCUIT_PUBLIC_INPUTS_LENGTH = 601; export const CONSTANT_ROLLUP_DATA_LENGTH = 13; export const BASE_OR_MERGE_PUBLIC_INPUTS_LENGTH = 30; -export const BLOCK_ROOT_OR_BLOCK_MERGE_PUBLIC_INPUTS_LENGTH = 60; +export const BLOCK_ROOT_OR_BLOCK_MERGE_PUBLIC_INPUTS_LENGTH = 58; export const ROOT_ROLLUP_PUBLIC_INPUTS_LENGTH = 44; export const GET_NOTES_ORACLE_RETURN_LENGTH = 674; export const NOTE_HASHES_NUM_BYTES_PER_BASE_ROLLUP = 2048; diff --git a/yarn-project/circuits.js/src/structs/rollup/block_root_or_block_merge_public_inputs.ts b/yarn-project/circuits.js/src/structs/rollup/block_root_or_block_merge_public_inputs.ts index 62533754e5ea..88b23ff53632 100644 --- a/yarn-project/circuits.js/src/structs/rollup/block_root_or_block_merge_public_inputs.ts +++ b/yarn-project/circuits.js/src/structs/rollup/block_root_or_block_merge_public_inputs.ts @@ -3,7 +3,7 @@ import { Fr } from '@aztec/foundation/fields'; import { BufferReader, type Tuple, serializeToBuffer, serializeToFields } from '@aztec/foundation/serialize'; import { type FieldsOf } from '@aztec/foundation/types'; -import { AZTEC_EPOCH_DURATION } from '../../constants.gen.js'; +import { AZTEC_MAX_EPOCH_DURATION } from '../../constants.gen.js'; import { GlobalVariables } from '../global_variables.js'; import { AppendOnlyTreeSnapshot } from './append_only_tree_snapshot.js'; @@ -44,7 +44,7 @@ export class BlockRootOrBlockMergePublicInputs { /** * The summed `transaction_fee`s and recipients of the constituent blocks. */ - public fees: Tuple, + public fees: Tuple, /** * Root of the verification key tree. */ @@ -74,7 +74,7 @@ export class BlockRootOrBlockMergePublicInputs { reader.readObject(GlobalVariables), reader.readObject(GlobalVariables), Fr.fromBuffer(reader), - reader.readArray(AZTEC_EPOCH_DURATION, FeeRecipient), + reader.readArray(AZTEC_MAX_EPOCH_DURATION, FeeRecipient), Fr.fromBuffer(reader), Fr.fromBuffer(reader), Fr.fromBuffer(reader), diff --git a/yarn-project/circuits.js/src/structs/rollup/root_rollup.ts b/yarn-project/circuits.js/src/structs/rollup/root_rollup.ts index 26af2c490d8a..0cc295154608 100644 --- a/yarn-project/circuits.js/src/structs/rollup/root_rollup.ts +++ b/yarn-project/circuits.js/src/structs/rollup/root_rollup.ts @@ -2,7 +2,7 @@ import { Fr } from '@aztec/foundation/fields'; import { BufferReader, type Tuple, serializeToBuffer, serializeToFields } from '@aztec/foundation/serialize'; import { type FieldsOf } from '@aztec/foundation/types'; -import { AZTEC_EPOCH_DURATION } from '../../constants.gen.js'; +import { AZTEC_MAX_EPOCH_DURATION } from '../../constants.gen.js'; import { AppendOnlyTreeSnapshot } from './append_only_tree_snapshot.js'; import { FeeRecipient } from './block_root_or_block_merge_public_inputs.js'; import { PreviousRollupBlockData } from './previous_rollup_block_data.js'; @@ -95,7 +95,7 @@ export class RootRollupPublicInputs { public endTimestamp: Fr, public endBlockNumber: Fr, public outHash: Fr, - public fees: Tuple, + public fees: Tuple, public vkTreeRoot: Fr, public protocolContractTreeRoot: Fr, public proverId: Fr, @@ -144,7 +144,7 @@ export class RootRollupPublicInputs { Fr.fromBuffer(reader), Fr.fromBuffer(reader), Fr.fromBuffer(reader), - reader.readArray(AZTEC_EPOCH_DURATION, FeeRecipient), + reader.readArray(AZTEC_MAX_EPOCH_DURATION, FeeRecipient), Fr.fromBuffer(reader), Fr.fromBuffer(reader), Fr.fromBuffer(reader), diff --git a/yarn-project/circuits.js/src/tests/factories.ts b/yarn-project/circuits.js/src/tests/factories.ts index 99851fe45545..e8658b246d88 100644 --- a/yarn-project/circuits.js/src/tests/factories.ts +++ b/yarn-project/circuits.js/src/tests/factories.ts @@ -19,7 +19,7 @@ import { import { ARCHIVE_HEIGHT, AVM_PROOF_LENGTH_IN_FIELDS, - AZTEC_EPOCH_DURATION, + AZTEC_MAX_EPOCH_DURATION, AppendOnlyTreeSnapshot, AvmCircuitInputs, AvmContractInstanceHint, @@ -1014,7 +1014,7 @@ export function makeBlockRootOrBlockMergeRollupPublicInputs( globalVariables ?? makeGlobalVariables(seed + 0x501), globalVariables ?? makeGlobalVariables(seed + 0x502), fr(seed + 0x600), - makeTuple(AZTEC_EPOCH_DURATION, () => makeFeeRecipient(seed), 0x700), + makeTuple(AZTEC_MAX_EPOCH_DURATION, () => makeFeeRecipient(seed), 0x700), fr(seed + 0x800), fr(seed + 0x801), fr(seed + 0x900), @@ -1159,7 +1159,7 @@ export function makeRootRollupPublicInputs(seed = 0): RootRollupPublicInputs { fr(seed + 0x600), fr(seed + 0x700), fr(seed + 0x800), - makeTuple(AZTEC_EPOCH_DURATION, () => makeFeeRecipient(seed), 0x900), + makeTuple(AZTEC_MAX_EPOCH_DURATION, () => makeFeeRecipient(seed), 0x900), fr(seed + 0x100), fr(seed + 0x101), fr(seed + 0x200), diff --git a/yarn-project/cli/src/cmds/l1/deploy_l1_contracts.ts b/yarn-project/cli/src/cmds/l1/deploy_l1_contracts.ts index cb16ce26dc23..2d7165ff442e 100644 --- a/yarn-project/cli/src/cmds/l1/deploy_l1_contracts.ts +++ b/yarn-project/cli/src/cmds/l1/deploy_l1_contracts.ts @@ -1,3 +1,4 @@ +import { getL1ContractsConfigEnvVars } from '@aztec/ethereum'; import { type EthAddress } from '@aztec/foundation/eth-address'; import { type DebugLogger, type LogFn } from '@aztec/foundation/log'; @@ -14,6 +15,8 @@ export async function deployL1Contracts( log: LogFn, debugLogger: DebugLogger, ) { + const config = getL1ContractsConfigEnvVars(); + const { l1ContractAddresses } = await deployAztecContracts( rpcUrl, chainId, @@ -21,6 +24,7 @@ export async function deployL1Contracts( mnemonic, salt, initialValidators, + config, debugLogger, ); diff --git a/yarn-project/cli/src/cmds/l1/update_l1_validators.ts b/yarn-project/cli/src/cmds/l1/update_l1_validators.ts index e51863b39b9b..9827721418e2 100644 --- a/yarn-project/cli/src/cmds/l1/update_l1_validators.ts +++ b/yarn-project/cli/src/cmds/l1/update_l1_validators.ts @@ -1,6 +1,6 @@ import { EthCheatCodes } from '@aztec/aztec.js'; -import { ETHEREUM_SLOT_DURATION, type EthAddress } from '@aztec/circuits.js'; -import { createEthereumChain } from '@aztec/ethereum'; +import { type EthAddress } from '@aztec/circuits.js'; +import { createEthereumChain, getL1ContractsConfigEnvVars } from '@aztec/ethereum'; import { type DebugLogger, type LogFn } from '@aztec/foundation/log'; import { RollupAbi } from '@aztec/l1-artifacts'; @@ -142,6 +142,7 @@ export async function fastForwardEpochs({ } export async function debugRollup({ rpcUrl, chainId, rollupAddress, log }: RollupCommandArgs & LoggerArgs) { + const config = getL1ContractsConfigEnvVars(); const publicClient = getPublicClient(rpcUrl, chainId); const rollup = getContract({ address: rollupAddress.toString(), @@ -167,7 +168,7 @@ export async function debugRollup({ rpcUrl, chainId, rollupAddress, log }: Rollu log(`Current slot: ${slot}`); const proposerDuringPrevL1Block = await rollup.read.getCurrentProposer(); log(`Proposer during previous L1 block: ${proposerDuringPrevL1Block}`); - const nextBlockTS = BigInt((await publicClient.getBlock()).timestamp + BigInt(ETHEREUM_SLOT_DURATION)); + const nextBlockTS = BigInt((await publicClient.getBlock()).timestamp + BigInt(config.ethereumSlotDuration)); const proposer = await rollup.read.getProposerAt([nextBlockTS]); log(`Proposer NOW: ${proposer.toString()}`); } diff --git a/yarn-project/cli/src/utils/aztec.ts b/yarn-project/cli/src/utils/aztec.ts index 13ccf0515b59..10b0d7c22aff 100644 --- a/yarn-project/cli/src/utils/aztec.ts +++ b/yarn-project/cli/src/utils/aztec.ts @@ -1,6 +1,6 @@ import { type ContractArtifact, type FunctionArtifact, loadContractArtifact } from '@aztec/aztec.js/abi'; import { type PXE } from '@aztec/circuit-types'; -import { type DeployL1Contracts } from '@aztec/ethereum'; +import { type DeployL1Contracts, L1ContractsConfig } from '@aztec/ethereum'; import { FunctionType } from '@aztec/foundation/abi'; import { type EthAddress } from '@aztec/foundation/eth-address'; import { type DebugLogger, type LogFn } from '@aztec/foundation/log'; @@ -58,6 +58,7 @@ export async function deployAztecContracts( mnemonic: string, salt: number | undefined, initialValidators: EthAddress[], + config: L1ContractsConfig, debugLogger: DebugLogger, ): Promise { const { createEthereumChain, deployL1Contracts } = await import('@aztec/ethereum'); @@ -76,6 +77,7 @@ export async function deployAztecContracts( protocolContractTreeRoot, salt, initialValidators, + ...config, }); } diff --git a/yarn-project/end-to-end/src/composed/integration_l1_publisher.test.ts b/yarn-project/end-to-end/src/composed/integration_l1_publisher.test.ts index 5f7c23936b69..f46d62d848a4 100644 --- a/yarn-project/end-to-end/src/composed/integration_l1_publisher.test.ts +++ b/yarn-project/end-to-end/src/composed/integration_l1_publisher.test.ts @@ -10,7 +10,6 @@ import { } from '@aztec/circuit-types'; import { makeBloatedProcessedTx } from '@aztec/circuit-types/test'; import { - ETHEREUM_SLOT_DURATION, EthAddress, GENESIS_ARCHIVE_ROOT, GasFees, @@ -160,6 +159,7 @@ describe('L1Publisher integration', () => { l1PublishRetryIntervalMS: 100, l1ChainId: 31337, viemPollingIntervalMS: 100, + ethereumSlotDuration: config.ethereumSlotDuration, }, new NoopTelemetryClient(), ); @@ -340,7 +340,7 @@ describe('L1Publisher integration', () => { ]; const ts = (await publicClient.getBlock()).timestamp; - const slot = await rollup.read.getSlotAt([ts + BigInt(ETHEREUM_SLOT_DURATION)]); + const slot = await rollup.read.getSlotAt([ts + BigInt(config.ethereumSlotDuration)]); const globalVariables = new GlobalVariables( new Fr(chainId), new Fr(config.version), @@ -445,7 +445,7 @@ describe('L1Publisher integration', () => { const txs = [makeEmptyProcessedTx(), makeEmptyProcessedTx()]; const ts = (await publicClient.getBlock()).timestamp; - const slot = await rollup.read.getSlotAt([ts + BigInt(ETHEREUM_SLOT_DURATION)]); + const slot = await rollup.read.getSlotAt([ts + BigInt(config.ethereumSlotDuration)]); const globalVariables = new GlobalVariables( new Fr(chainId), new Fr(config.version), @@ -515,7 +515,7 @@ describe('L1Publisher integration', () => { const txs = [makeEmptyProcessedTx(), makeEmptyProcessedTx()]; const ts = (await publicClient.getBlock()).timestamp; - const slot = await rollup.read.getSlotAt([ts + BigInt(ETHEREUM_SLOT_DURATION)]); + const slot = await rollup.read.getSlotAt([ts + BigInt(config.ethereumSlotDuration)]); const globalVariables = new GlobalVariables( new Fr(chainId), new Fr(config.version), diff --git a/yarn-project/end-to-end/src/e2e_block_building.test.ts b/yarn-project/end-to-end/src/e2e_block_building.test.ts index 9b372ce0af6a..605644f13fb2 100644 --- a/yarn-project/end-to-end/src/e2e_block_building.test.ts +++ b/yarn-project/end-to-end/src/e2e_block_building.test.ts @@ -17,7 +17,7 @@ import { retryUntil, sleep, } from '@aztec/aztec.js'; -import { AZTEC_EPOCH_PROOF_CLAIM_WINDOW_IN_L2_SLOTS } from '@aztec/circuits.js'; +import { getL1ContractsConfigEnvVars } from '@aztec/ethereum'; import { times } from '@aztec/foundation/collection'; import { poseidon2HashWithSeparator } from '@aztec/foundation/crypto'; import { StatefulTestContract, StatefulTestContractArtifact } from '@aztec/noir-contracts.js'; @@ -38,6 +38,8 @@ describe('e2e_block_building', () => { let aztecNode: AztecNode; let teardown: () => Promise; + const { aztecEpochProofClaimWindowInL2Slots } = getL1ContractsConfigEnvVars(); + describe('multi-txs block', () => { const artifact = StatefulTestContractArtifact; @@ -446,7 +448,7 @@ describe('e2e_block_building', () => { // Now move to a new epoch and past the proof claim window to cause a reorg logger.info('Advancing past the proof claim window'); await cheatCodes.rollup.advanceToNextEpoch(); - await cheatCodes.rollup.advanceSlots(AZTEC_EPOCH_PROOF_CLAIM_WINDOW_IN_L2_SLOTS + 1); // off-by-one? + await cheatCodes.rollup.advanceSlots(aztecEpochProofClaimWindowInL2Slots + 1); // off-by-one? // Wait a bit before spawning a new pxe await sleep(2000); diff --git a/yarn-project/end-to-end/src/e2e_l1_with_wall_time.test.ts b/yarn-project/end-to-end/src/e2e_l1_with_wall_time.test.ts index 1a69be554cb3..15abecfb4467 100644 --- a/yarn-project/end-to-end/src/e2e_l1_with_wall_time.test.ts +++ b/yarn-project/end-to-end/src/e2e_l1_with_wall_time.test.ts @@ -1,6 +1,7 @@ import { getSchnorrAccount } from '@aztec/accounts/schnorr'; import { type DebugLogger, Fr, GrumpkinScalar, type PXE, TxStatus } from '@aztec/aztec.js'; -import { ETHEREUM_SLOT_DURATION, EthAddress } from '@aztec/circuits.js'; +import { EthAddress } from '@aztec/circuits.js'; +import { getL1ContractsConfigEnvVars } from '@aztec/ethereum'; import { type PXEService } from '@aztec/pxe'; import { privateKeyToAccount } from 'viem/accounts'; @@ -15,8 +16,13 @@ describe('e2e_l1_with_wall_time', () => { beforeEach(async () => { const account = privateKeyToAccount(`0x${getPrivateKeyFromIndex(0)!.toString('hex')}`); const initialValidators = [EthAddress.fromString(account.address)]; + const { ethereumSlotDuration } = getL1ContractsConfigEnvVars(); - ({ teardown, logger, pxe } = await setup(0, { initialValidators, l1BlockTime: ETHEREUM_SLOT_DURATION, salt: 420 })); + ({ teardown, logger, pxe } = await setup(0, { + initialValidators, + l1BlockTime: ethereumSlotDuration, + salt: 420, + })); }); afterEach(() => teardown()); diff --git a/yarn-project/end-to-end/src/e2e_lending_contract.test.ts b/yarn-project/end-to-end/src/e2e_lending_contract.test.ts index 4d1f37ccdc52..cb356d6ebda6 100644 --- a/yarn-project/end-to-end/src/e2e_lending_contract.test.ts +++ b/yarn-project/end-to-end/src/e2e_lending_contract.test.ts @@ -56,7 +56,8 @@ describe('e2e_lending_contract', () => { }; beforeAll(async () => { - ({ teardown, logger, cheatCodes: cc, wallet, deployL1ContractsValues } = await setup(1)); + const ctx = await setup(1); + ({ teardown, logger, cheatCodes: cc, wallet, deployL1ContractsValues } = ctx); ({ lendingContract, priceFeedContract, collateralAsset, stableCoin } = await deployContracts()); await ensureAccountsPubliclyDeployed(wallet, [wallet]); @@ -74,6 +75,7 @@ describe('e2e_lending_contract', () => { cc, lendingAccount, rate, + ctx.config.ethereumSlotDuration, rollup, lendingContract, new TokenSimulator(collateralAsset, wallet, logger, [lendingContract.address, wallet.getAddress()]), diff --git a/yarn-project/end-to-end/src/e2e_p2p/p2p_network.ts b/yarn-project/end-to-end/src/e2e_p2p/p2p_network.ts index 58eeec87c188..529e27762948 100644 --- a/yarn-project/end-to-end/src/e2e_p2p/p2p_network.ts +++ b/yarn-project/end-to-end/src/e2e_p2p/p2p_network.ts @@ -1,6 +1,7 @@ import { type AztecNodeConfig, type AztecNodeService } from '@aztec/aztec-node'; import { EthCheatCodes } from '@aztec/aztec.js'; -import { AZTEC_SLOT_DURATION, ETHEREUM_SLOT_DURATION, EthAddress } from '@aztec/circuits.js'; +import { EthAddress } from '@aztec/circuits.js'; +import { getL1ContractsConfigEnvVars } from '@aztec/ethereum'; import { type DebugLogger, createDebugLogger } from '@aztec/foundation/log'; import { RollupAbi } from '@aztec/l1-artifacts'; import { type BootstrapNode } from '@aztec/p2p'; @@ -21,7 +22,8 @@ import { getEndToEndTestTelemetryClient } from '../fixtures/with_telemetry_utils // Use a fixed bootstrap node private key so that we can re-use the same snapshot and the nodes can find each other const BOOTSTRAP_NODE_PRIVATE_KEY = '080212208f988fc0899e4a73a5aee4d271a5f20670603a756ad8d84f2c94263a6427c591'; -export const WAIT_FOR_TX_TIMEOUT = AZTEC_SLOT_DURATION * 3; +const l1ContractsConfig = getL1ContractsConfigEnvVars(); +export const WAIT_FOR_TX_TIMEOUT = l1ContractsConfig.aztecSlotDuration * 3; export class P2PNetworkTest { private snapshotManager: ISnapshotManager; @@ -58,7 +60,7 @@ export class P2PNetworkTest { this.snapshotManager = createSnapshotManager(`e2e_p2p_network/${testName}`, process.env.E2E_DATA_PATH, { ...initialValidatorConfig, - l1BlockTime: ETHEREUM_SLOT_DURATION, + l1BlockTime: l1ContractsConfig.ethereumSlotDuration, salt: 420, initialValidators, metricsPort: metricsPort, diff --git a/yarn-project/end-to-end/src/e2e_synching.test.ts b/yarn-project/end-to-end/src/e2e_synching.test.ts index 5be8f5d4397a..cc526217ef8f 100644 --- a/yarn-project/end-to-end/src/e2e_synching.test.ts +++ b/yarn-project/end-to-end/src/e2e_synching.test.ts @@ -49,7 +49,8 @@ import { } from '@aztec/aztec.js'; // eslint-disable-next-line no-restricted-imports import { ExtendedNote, L2Block, LogType, Note, type TxHash } from '@aztec/circuit-types'; -import { type AztecAddress, ETHEREUM_SLOT_DURATION } from '@aztec/circuits.js'; +import { type AztecAddress } from '@aztec/circuits.js'; +import { getL1ContractsConfigEnvVars } from '@aztec/ethereum'; import { Timer } from '@aztec/foundation/timer'; import { RollupAbi } from '@aztec/l1-artifacts'; import { SchnorrHardcodedAccountContract, SpamContract, TokenContract } from '@aztec/noir-contracts.js'; @@ -69,7 +70,7 @@ const SALT = 420; const AZTEC_GENERATE_TEST_DATA = !!process.env.AZTEC_GENERATE_TEST_DATA; const START_TIME = 1893456000; // 2030 01 01 00 00 const RUN_THE_BIG_ONE = !!process.env.RUN_THE_BIG_ONE; - +const ETHEREUM_SLOT_DURATION = getL1ContractsConfigEnvVars().ethereumSlotDuration; const MINT_AMOUNT = 1000n; enum TxComplexity { @@ -402,6 +403,7 @@ describe('e2e_synching', () => { l1PublishRetryIntervalMS: 100, l1ChainId: 31337, viemPollingIntervalMS: 100, + ethereumSlotDuration: ETHEREUM_SLOT_DURATION, }, new NoopTelemetryClient(), ); diff --git a/yarn-project/end-to-end/src/fixtures/setup_l1_contracts.ts b/yarn-project/end-to-end/src/fixtures/setup_l1_contracts.ts index 8c4b6a8a8664..b3fdb26403e9 100644 --- a/yarn-project/end-to-end/src/fixtures/setup_l1_contracts.ts +++ b/yarn-project/end-to-end/src/fixtures/setup_l1_contracts.ts @@ -1,5 +1,5 @@ import { type DebugLogger, deployL1Contracts } from '@aztec/aztec.js'; -import { type DeployL1ContractsArgs } from '@aztec/ethereum'; +import { type DeployL1ContractsArgs, type L1ContractsConfig } from '@aztec/ethereum'; import { getVKTreeRoot } from '@aztec/noir-protocol-circuits-types'; import { ProtocolContractAddress, protocolContractTreeRoot } from '@aztec/protocol-contracts'; @@ -12,7 +12,7 @@ export const setupL1Contracts = async ( l1RpcUrl: string, account: HDAccount | PrivateKeyAccount, logger: DebugLogger, - args: Pick, + args: Pick & L1ContractsConfig, ) => { const l1Data = await deployL1Contracts(l1RpcUrl, account, foundry, logger, { l2FeeJuiceAddress: ProtocolContractAddress.FeeJuice, diff --git a/yarn-project/end-to-end/src/fixtures/snapshot_manager.ts b/yarn-project/end-to-end/src/fixtures/snapshot_manager.ts index 6809dcab9e60..ee16764f278e 100644 --- a/yarn-project/end-to-end/src/fixtures/snapshot_manager.ts +++ b/yarn-project/end-to-end/src/fixtures/snapshot_manager.ts @@ -17,7 +17,7 @@ import { type Wallet, } from '@aztec/aztec.js'; import { deployInstance, registerContractClass } from '@aztec/aztec.js/deployment'; -import { type DeployL1ContractsArgs, createL1Clients, l1Artifacts } from '@aztec/ethereum'; +import { type DeployL1ContractsArgs, createL1Clients, getL1ContractsConfigEnvVars, l1Artifacts } from '@aztec/ethereum'; import { asyncMap } from '@aztec/foundation/async-map'; import { type Logger, createDebugLogger } from '@aztec/foundation/log'; import { resolver, reviver } from '@aztec/foundation/serialize'; @@ -341,6 +341,7 @@ async function setupFromFresh( salt: opts.salt, initialValidators: opts.initialValidators, ...deployL1ContractsArgs, + ...getL1ContractsConfigEnvVars(), }); aztecNodeConfig.l1Contracts = deployL1ContractsValues.l1ContractAddresses; aztecNodeConfig.l1PublishRetryIntervalMS = 100; diff --git a/yarn-project/end-to-end/src/fixtures/utils.ts b/yarn-project/end-to-end/src/fixtures/utils.ts index 6818c4a8628a..b47671ad72dd 100644 --- a/yarn-project/end-to-end/src/fixtures/utils.ts +++ b/yarn-project/end-to-end/src/fixtures/utils.ts @@ -30,7 +30,7 @@ import { deployInstance, registerContractClass } from '@aztec/aztec.js/deploymen import { DefaultMultiCallEntrypoint } from '@aztec/aztec.js/entrypoint'; import { type BBNativePrivateKernelProver } from '@aztec/bb-prover'; import { type EthAddress, GasSettings, getContractClassFromArtifact } from '@aztec/circuits.js'; -import { NULL_KEY, isAnvilTestChain, l1Artifacts } from '@aztec/ethereum'; +import { NULL_KEY, getL1ContractsConfigEnvVars, isAnvilTestChain, l1Artifacts } from '@aztec/ethereum'; import { makeBackoff, retry, retryUntil } from '@aztec/foundation/retry'; import { FeeJuiceContract } from '@aztec/noir-contracts.js/FeeJuice'; import { getVKTreeRoot } from '@aztec/noir-protocol-circuits-types'; @@ -103,6 +103,7 @@ export const setupL1Contracts = async ( salt: args.salt, initialValidators: args.initialValidators, assumeProvenThrough: args.assumeProvenThrough, + ...getL1ContractsConfigEnvVars(), }); return l1Data; diff --git a/yarn-project/end-to-end/src/prover-coordination/e2e_prover_coordination.test.ts b/yarn-project/end-to-end/src/prover-coordination/e2e_prover_coordination.test.ts index f5b15cfb4e51..e53a63273093 100644 --- a/yarn-project/end-to-end/src/prover-coordination/e2e_prover_coordination.test.ts +++ b/yarn-project/end-to-end/src/prover-coordination/e2e_prover_coordination.test.ts @@ -9,7 +9,7 @@ import { createDebugLogger, sleep, } from '@aztec/aztec.js'; -import { AZTEC_EPOCH_DURATION, AZTEC_SLOT_DURATION, type AztecAddress, EthAddress } from '@aztec/circuits.js'; +import { type AztecAddress, EthAddress } from '@aztec/circuits.js'; import { Buffer32 } from '@aztec/foundation/buffer'; import { times } from '@aztec/foundation/collection'; import { Secp256k1Signer, keccak256, randomBigInt, randomInt } from '@aztec/foundation/crypto'; @@ -205,8 +205,9 @@ describe('e2e_prover_coordination', () => { const advanceToNextEpoch = async () => { const slot = await getSlot(); - const slotsUntilNextEpoch = BigInt(AZTEC_EPOCH_DURATION) - (slot % BigInt(AZTEC_EPOCH_DURATION)) + 1n; - const timeToNextEpoch = slotsUntilNextEpoch * BigInt(AZTEC_SLOT_DURATION); + const slotsUntilNextEpoch = + BigInt(ctx.aztecNodeConfig.aztecEpochDuration) - (slot % BigInt(ctx.aztecNodeConfig.aztecEpochDuration)) + 1n; + const timeToNextEpoch = slotsUntilNextEpoch * BigInt(ctx.aztecNodeConfig.aztecSlotDuration); const l1Timestamp = await getL1Timestamp(); await ctx.cheatCodes.eth.warp(Number(l1Timestamp + timeToNextEpoch)); await logState(); @@ -248,7 +249,7 @@ describe('e2e_prover_coordination', () => { // Here we are creating a proof quote for epoch 0 const quoteForEpoch0 = await makeEpochProofQuote({ epochToProve: 0n, - validUntilSlot: BigInt(AZTEC_EPOCH_DURATION + 10), + validUntilSlot: BigInt(ctx.aztecNodeConfig.aztecEpochDuration + 10), bondAmount: 10000n, basisPointFee: 1, signer: proverSigner, @@ -360,7 +361,7 @@ describe('e2e_prover_coordination', () => { // Here we are creating a proof quote for epoch 0 const quoteForEpoch0 = await makeEpochProofQuote({ epochToProve: 0n, - validUntilSlot: BigInt(AZTEC_EPOCH_DURATION + 10), + validUntilSlot: BigInt(ctx.aztecNodeConfig.aztecEpochDuration + 10), bondAmount: 10000n, basisPointFee: 1, signer: proverSigner, @@ -432,7 +433,7 @@ describe('e2e_prover_coordination', () => { // Submit proof claim for the new epoch const quoteForEpoch4 = await makeEpochProofQuote({ epochToProve: 4n, - validUntilSlot: BigInt(AZTEC_EPOCH_DURATION * 4 + 10), + validUntilSlot: BigInt(ctx.aztecNodeConfig.aztecEpochDuration * 4 + 10), bondAmount: 10000n, basisPointFee: 1, signer: proverSigner, diff --git a/yarn-project/end-to-end/src/simulators/lending_simulator.ts b/yarn-project/end-to-end/src/simulators/lending_simulator.ts index 9340a1655539..288cac32eaf0 100644 --- a/yarn-project/end-to-end/src/simulators/lending_simulator.ts +++ b/yarn-project/end-to-end/src/simulators/lending_simulator.ts @@ -1,6 +1,5 @@ // Convenience struct to hold an account's address and secret that can easily be passed around. import { type AztecAddress, type CheatCodes, Fr } from '@aztec/aztec.js'; -import { ETHEREUM_SLOT_DURATION } from '@aztec/circuits.js'; import { pedersenHash } from '@aztec/foundation/crypto'; import { type RollupAbi } from '@aztec/l1-artifacts'; import { type LendingContract } from '@aztec/noir-contracts.js/Lending'; @@ -81,6 +80,7 @@ export class LendingSimulator { private cc: CheatCodes, private account: LendingAccount, private rate: bigint, + private ethereumSlotDuration: number, /** the rollup contract */ public rollup: GetContractReturnType>, /** the lending contract */ @@ -94,7 +94,7 @@ export class LendingSimulator { async prepare() { this.accumulator = BASE; const slot = await this.rollup.read.getSlotAt([ - BigInt(await this.cc.eth.timestamp()) + BigInt(ETHEREUM_SLOT_DURATION), + BigInt(await this.cc.eth.timestamp()) + BigInt(this.ethereumSlotDuration), ]); this.time = Number(await this.rollup.read.getTimestampForSlot([slot])); } @@ -110,7 +110,7 @@ export class LendingSimulator { this.time = ts; // Mine ethereum blocks such that the next block will be in a new slot - await this.cc.eth.warp(this.time - ETHEREUM_SLOT_DURATION); + await this.cc.eth.warp(this.time - this.ethereumSlotDuration); await this.rollup.write.setAssumeProvenThroughBlockNumber([(await this.rollup.read.getPendingBlockNumber()) + 1n]); this.accumulator = muldivDown(this.accumulator, computeMultiplier(this.rate, BigInt(timeDiff)), BASE); diff --git a/yarn-project/end-to-end/src/spartan/4epochs.test.ts b/yarn-project/end-to-end/src/spartan/4epochs.test.ts index 4555fb755cc8..9c601e1fa269 100644 --- a/yarn-project/end-to-end/src/spartan/4epochs.test.ts +++ b/yarn-project/end-to-end/src/spartan/4epochs.test.ts @@ -1,5 +1,5 @@ import { EthCheatCodes, readFieldCompressedString } from '@aztec/aztec.js'; -import { AZTEC_EPOCH_DURATION } from '@aztec/circuits.js'; +import { getL1ContractsConfigEnvVars } from '@aztec/ethereum'; import { createDebugLogger } from '@aztec/foundation/log'; import { TokenContract } from '@aztec/noir-contracts.js'; @@ -15,11 +15,13 @@ describe('token transfer test', () => { jest.setTimeout(10 * 60 * 4000); // 40 minutes const logger = createDebugLogger(`aztec:spartan:4epochs`); + const l1Config = getL1ContractsConfigEnvVars(); + // We want plenty of minted tokens for a lot of slots that fill up multiple epochs const MINT_AMOUNT = 2000000n; const TEST_EPOCHS = 4; const MAX_MISSED_SLOTS = 10n; - const ROUNDS = BigInt(AZTEC_EPOCH_DURATION * TEST_EPOCHS); + const ROUNDS = BigInt(l1Config.aztecEpochDuration * TEST_EPOCHS); let testWallets: TestWallets; let PXE_URL: string; diff --git a/yarn-project/end-to-end/src/spartan/reorg.test.ts b/yarn-project/end-to-end/src/spartan/reorg.test.ts index d3a591c9cb01..046f51a13576 100644 --- a/yarn-project/end-to-end/src/spartan/reorg.test.ts +++ b/yarn-project/end-to-end/src/spartan/reorg.test.ts @@ -1,5 +1,4 @@ import { EthCheatCodes, sleep } from '@aztec/aztec.js'; -import { AZTEC_EPOCH_DURATION, AZTEC_SLOT_DURATION } from '@aztec/circuits.js'; import { createDebugLogger } from '@aztec/foundation/log'; import { expect, jest } from '@jest/globals'; @@ -65,14 +64,15 @@ describe('reorg test', () => { ethCheatCodes, await testWallets.pxe.getNodeInfo().then(n => n.l1ContractAddresses), ); + const { epochDuration, slotDuration } = await rollupCheatCodes.getConfig(); await performTransfers({ testWallets, - rounds: AZTEC_EPOCH_DURATION * SETUP_EPOCHS, + rounds: Number(epochDuration) * SETUP_EPOCHS, transferAmount: TRANSFER_AMOUNT, logger: debugLogger, }); - await checkBalances(testWallets, MINT_AMOUNT, TRANSFER_AMOUNT * BigInt(AZTEC_EPOCH_DURATION * SETUP_EPOCHS)); + await checkBalances(testWallets, MINT_AMOUNT, TRANSFER_AMOUNT * epochDuration * BigInt(SETUP_EPOCHS)); // get the tips before the reorg const { pending: preReorgPending, proven: preReorgProven } = await rollupCheatCodes.getTips(); @@ -81,14 +81,14 @@ describe('reorg test', () => { const stdout = await applyKillProvers({ namespace: NAMESPACE, spartanDir: SPARTAN_DIR, - durationSeconds: AZTEC_EPOCH_DURATION * AZTEC_SLOT_DURATION * 2, + durationSeconds: Number(epochDuration * slotDuration) * 2, }); debugLogger.info(stdout); // We only need 2 epochs for a reorg to be triggered, but 3 gives time for the bot to be restarted and the chain to re-stabilize // TODO(#9613): why do we need to wait for 3 epochs? debugLogger.info(`Waiting for 3 epochs to pass`); - await sleep(AZTEC_EPOCH_DURATION * AZTEC_SLOT_DURATION * 3 * 1000); + await sleep(Number(epochDuration * slotDuration) * 3 * 1000); // TODO(#9327): begin delete // The bot must be restarted because the PXE does not handle reorgs without a restart. @@ -108,7 +108,7 @@ describe('reorg test', () => { await performTransfers({ testWallets, - rounds: AZTEC_EPOCH_DURATION * SETUP_EPOCHS, + rounds: Number(epochDuration) * SETUP_EPOCHS, transferAmount: TRANSFER_AMOUNT, logger: debugLogger, }); @@ -116,8 +116,8 @@ describe('reorg test', () => { // expect the block height to be at least 4 epochs worth of slots const { pending: newPending, proven: newProven } = await rollupCheatCodes.getTips(); expect(newPending).toBeGreaterThan(preReorgPending); - expect(newPending).toBeGreaterThan(4 * AZTEC_EPOCH_DURATION); + expect(newPending).toBeGreaterThan(4 * Number(epochDuration)); expect(newProven).toBeGreaterThan(preReorgProven); - expect(newProven).toBeGreaterThan(3 * AZTEC_EPOCH_DURATION); + expect(newProven).toBeGreaterThan(3 * Number(epochDuration)); }); }); diff --git a/yarn-project/ethereum/src/config.ts b/yarn-project/ethereum/src/config.ts new file mode 100644 index 000000000000..a7b0733f1907 --- /dev/null +++ b/yarn-project/ethereum/src/config.ts @@ -0,0 +1,54 @@ +import { type ConfigMappingsType, getConfigFromMappings } from '@aztec/foundation/config'; + +export type L1ContractsConfig = { + /** How many seconds an L1 slot lasts. */ + ethereumSlotDuration: number; + /** How many seconds an L2 slots lasts (must be multiple of ethereum slot duration). */ + aztecSlotDuration: number; + /** How many L2 slots an epoch lasts. */ + aztecEpochDuration: number; + /** The target validator committee size. */ + aztecTargetCommitteeSize: number; + /** The number of L2 slots that we can wait for a proof of an epoch to be produced. */ + aztecEpochProofClaimWindowInL2Slots: number; +}; + +export const DefaultL1ContractsConfig: L1ContractsConfig = { + ethereumSlotDuration: 12, + aztecSlotDuration: 24, + aztecEpochDuration: 16, + aztecTargetCommitteeSize: 48, + aztecEpochProofClaimWindowInL2Slots: 13, +}; + +export const l1ContractsConfigMappings: ConfigMappingsType = { + ethereumSlotDuration: { + env: 'ETHEREUM_SLOT_DURATION', + description: 'How many seconds an L1 slot lasts.', + defaultValue: DefaultL1ContractsConfig.ethereumSlotDuration, + }, + aztecSlotDuration: { + env: 'AZTEC_SLOT_DURATION', + description: 'How many seconds an L2 slots lasts (must be multiple of ethereum slot duration).', + defaultValue: DefaultL1ContractsConfig.aztecSlotDuration, + }, + aztecEpochDuration: { + env: 'AZTEC_EPOCH_DURATION', + description: `How many L2 slots an epoch lasts (maximum AZTEC_MAX_EPOCH_DURATION).`, + defaultValue: DefaultL1ContractsConfig.aztecEpochDuration, + }, + aztecTargetCommitteeSize: { + env: 'AZTEC_TARGET_COMMITTEE_SIZE', + description: 'The target validator committee size.', + defaultValue: DefaultL1ContractsConfig.aztecTargetCommitteeSize, + }, + aztecEpochProofClaimWindowInL2Slots: { + env: 'AZTEC_EPOCH_PROOF_CLAIM_WINDOW_IN_L2_SLOTS', + description: 'The number of L2 slots that we can wait for a proof of an epoch to be produced.', + defaultValue: DefaultL1ContractsConfig.aztecEpochProofClaimWindowInL2Slots, + }, +}; + +export function getL1ContractsConfigEnvVars(): L1ContractsConfig { + return getConfigFromMappings(l1ContractsConfigMappings); +} diff --git a/yarn-project/ethereum/src/deploy_l1_contracts.ts b/yarn-project/ethereum/src/deploy_l1_contracts.ts index 8a23efc46baa..d4f44e3a88e9 100644 --- a/yarn-project/ethereum/src/deploy_l1_contracts.ts +++ b/yarn-project/ethereum/src/deploy_l1_contracts.ts @@ -50,6 +50,7 @@ import { import { type HDAccount, type PrivateKeyAccount, mnemonicToAccount, privateKeyToAccount } from 'viem/accounts'; import { foundry } from 'viem/chains'; +import { type L1ContractsConfig } from './config.js'; import { isAnvilTestChain } from './ethereum_chain.js'; import { type L1ContractAddresses } from './l1_contract_addresses.js'; @@ -201,30 +202,18 @@ export const l1Artifacts: L1ContractArtifactsForDeployment = { }, }; -export interface DeployL1ContractsArgs { - /** - * The address of the L2 Fee Juice contract. - */ +export interface DeployL1ContractsArgs extends L1ContractsConfig { + /** The address of the L2 Fee Juice contract. */ l2FeeJuiceAddress: AztecAddress; - /** - * The vk tree root. - */ + /** The vk tree root. */ vkTreeRoot: Fr; - /** - * The protocol contract tree root. - */ + /** The protocol contract tree root. */ protocolContractTreeRoot: Fr; - /** - * The block number to assume proven through. - */ + /** The block number to assume proven through. */ assumeProvenThrough?: number; - /** - * The salt for CREATE2 deployment. - */ + /** The salt for CREATE2 deployment. */ salt: number | undefined; - /** - * The initial validators for the rollup contract. - */ + /** The initial validators for the rollup contract. */ initialValidators?: EthAddress[]; } @@ -365,6 +354,12 @@ export const deployL1Contracts = async ( args.protocolContractTreeRoot.toString(), account.address.toString(), args.initialValidators?.map(v => v.toString()) ?? [], + { + aztecSlotDuration: args.aztecSlotDuration, + aztecEpochDuration: args.aztecEpochDuration, + targetCommitteeSize: args.aztecTargetCommitteeSize, + aztecEpochProofClaimWindowInL2Slots: args.aztecEpochProofClaimWindowInL2Slots, + }, ]); logger.info(`Deployed Rollup at ${rollupAddress}`); diff --git a/yarn-project/ethereum/src/index.ts b/yarn-project/ethereum/src/index.ts index 2e39ebaa8f59..80bc84bcbc12 100644 --- a/yarn-project/ethereum/src/index.ts +++ b/yarn-project/ethereum/src/index.ts @@ -4,3 +4,4 @@ export * from './l1_contract_addresses.js'; export * from './l1_reader.js'; export * from './ethereum_chain.js'; export * from './utils.js'; +export * from './config.js'; diff --git a/yarn-project/ethereum/src/l1_reader.ts b/yarn-project/ethereum/src/l1_reader.ts index 251f57ad1a5b..f2f481968244 100644 --- a/yarn-project/ethereum/src/l1_reader.ts +++ b/yarn-project/ethereum/src/l1_reader.ts @@ -2,26 +2,15 @@ import { type ConfigMappingsType, numberConfigHelper } from '@aztec/foundation/c import { type L1ContractAddresses, l1ContractAddressesMapping } from './l1_contract_addresses.js'; -/** - * Configuration of the L1GlobalReader. - */ +/** Configuration of the L1GlobalReader. */ export interface L1ReaderConfig { - /** - * The RPC Url of the ethereum host. - */ + /** The RPC Url of the ethereum host. */ l1RpcUrl: string; - /** - * The chain ID of the ethereum host. - */ + /** The chain ID of the ethereum host. */ l1ChainId: number; - - /** - * The deployed l1 contract addresses - */ + /** The deployed l1 contract addresses */ l1Contracts: L1ContractAddresses; - /** - * The polling interval viem uses in ms - */ + /** The polling interval viem uses in ms */ viemPollingIntervalMS: number; } diff --git a/yarn-project/foundation/src/config/env_var.ts b/yarn-project/foundation/src/config/env_var.ts index f6368037724d..60967110193a 100644 --- a/yarn-project/foundation/src/config/env_var.ts +++ b/yarn-project/foundation/src/config/env_var.ts @@ -150,4 +150,9 @@ export type EnvVar = | 'VERIFIER_VIEM_POLLING_INTERVAL_MS' | 'L1_READER_VIEM_POLLING_INTERVAL_MS' | 'PROVER_VIEM_POLLING_INTERVAL_MS' - | 'SEQ_VIEM_POLLING_INTERVAL_MS'; + | 'SEQ_VIEM_POLLING_INTERVAL_MS' + | 'ETHEREUM_SLOT_DURATION' + | 'AZTEC_SLOT_DURATION' + | 'AZTEC_EPOCH_DURATION' + | 'AZTEC_TARGET_COMMITTEE_SIZE' + | 'AZTEC_EPOCH_PROOF_CLAIM_WINDOW_IN_L2_SLOTS'; diff --git a/yarn-project/ivc-integration/package.json b/yarn-project/ivc-integration/package.json index 0eb7a4c6abff..c74cfa2e5be5 100644 --- a/yarn-project/ivc-integration/package.json +++ b/yarn-project/ivc-integration/package.json @@ -18,7 +18,7 @@ "formatting:fix:types": "NODE_OPTIONS='--max-old-space-size=8096' run -T eslint --fix ./src/types && run -T prettier -w ./src/types", "generate": "yarn generate:noir-circuits", "generate:noir-circuits": "mkdir -p ./artifacts && cp -r ../../noir-projects/mock-protocol-circuits/target/* ./artifacts && node --no-warnings --loader ts-node/esm src/scripts/generate_declaration_files.ts && node --no-warnings --loader ts-node/esm src/scripts/generate_ts_from_abi.ts && run -T prettier -w ./src/types", - "test:non-browser":"NODE_NO_WARNINGS=1 node --experimental-vm-modules ../node_modules/.bin/jest --passWithNoTests --testPathIgnorePatterns=browser", + "test:non-browser": "NODE_NO_WARNINGS=1 node --experimental-vm-modules ../node_modules/.bin/jest --passWithNoTests --testPathIgnorePatterns=browser", "test:browser": "./run_browser_tests.sh", "test": "yarn test:non-browser", "codegen": "yarn noir-codegen", diff --git a/yarn-project/noir-protocol-circuits-types/src/type_conversion.ts b/yarn-project/noir-protocol-circuits-types/src/type_conversion.ts index d67d6ade8f65..16069a07b261 100644 --- a/yarn-project/noir-protocol-circuits-types/src/type_conversion.ts +++ b/yarn-project/noir-protocol-circuits-types/src/type_conversion.ts @@ -1,7 +1,7 @@ import { type AVM_PROOF_LENGTH_IN_FIELDS, AVM_VERIFICATION_KEY_LENGTH_IN_FIELDS, - AZTEC_EPOCH_DURATION, + AZTEC_MAX_EPOCH_DURATION, AppendOnlyTreeSnapshot, type AvmAccumulatedData, type AvmCircuitPublicInputs, @@ -2273,7 +2273,7 @@ export function mapBlockRootOrBlockMergePublicInputsFromNoir( mapGlobalVariablesFromNoir(blockRootOrBlockMergePublicInputs.start_global_variables), mapGlobalVariablesFromNoir(blockRootOrBlockMergePublicInputs.end_global_variables), mapFieldFromNoir(blockRootOrBlockMergePublicInputs.out_hash), - mapTupleFromNoir(blockRootOrBlockMergePublicInputs.fees, AZTEC_EPOCH_DURATION, mapFeeRecipientFromNoir), + mapTupleFromNoir(blockRootOrBlockMergePublicInputs.fees, AZTEC_MAX_EPOCH_DURATION, mapFeeRecipientFromNoir), mapFieldFromNoir(blockRootOrBlockMergePublicInputs.vk_tree_root), mapFieldFromNoir(blockRootOrBlockMergePublicInputs.protocol_contract_tree_root), mapFieldFromNoir(blockRootOrBlockMergePublicInputs.prover_id), @@ -2450,7 +2450,7 @@ export function mapRootRollupPublicInputsFromNoir( mapFieldFromNoir(rootRollupPublicInputs.end_timestamp), mapFieldFromNoir(rootRollupPublicInputs.end_block_number), mapFieldFromNoir(rootRollupPublicInputs.out_hash), - mapTupleFromNoir(rootRollupPublicInputs.fees, AZTEC_EPOCH_DURATION, mapFeeRecipientFromNoir), + mapTupleFromNoir(rootRollupPublicInputs.fees, AZTEC_MAX_EPOCH_DURATION, mapFeeRecipientFromNoir), mapFieldFromNoir(rootRollupPublicInputs.vk_tree_root), mapFieldFromNoir(rootRollupPublicInputs.protocol_contract_tree_root), mapFieldFromNoir(rootRollupPublicInputs.prover_id), diff --git a/yarn-project/sequencer-client/src/config.ts b/yarn-project/sequencer-client/src/config.ts index 5daaa910aae9..761300832115 100644 --- a/yarn-project/sequencer-client/src/config.ts +++ b/yarn-project/sequencer-client/src/config.ts @@ -1,12 +1,18 @@ import { type AllowedElement } from '@aztec/circuit-types'; import { AztecAddress, Fr, FunctionSelector, getContractClassFromArtifact } from '@aztec/circuits.js'; -import { type L1ReaderConfig, l1ReaderConfigMappings } from '@aztec/ethereum'; +import { + type L1ContractsConfig, + type L1ReaderConfig, + l1ContractsConfigMappings, + l1ReaderConfigMappings, +} from '@aztec/ethereum'; import { type ConfigMappingsType, booleanConfigHelper, getConfigFromMappings, numberConfigHelper, } from '@aztec/foundation/config'; +import { pickConfigMappings } from '@aztec/foundation/config'; import { EthAddress } from '@aztec/foundation/eth-address'; import { FPCContract } from '@aztec/noir-contracts.js/FPC'; import { TokenContractArtifact } from '@aztec/noir-contracts.js/Token'; @@ -31,7 +37,12 @@ type ChainConfig = { /** * Configuration settings for the SequencerClient. */ -export type SequencerClientConfig = PublisherConfig & TxSenderConfig & SequencerConfig & L1ReaderConfig & ChainConfig; +export type SequencerClientConfig = PublisherConfig & + TxSenderConfig & + SequencerConfig & + L1ReaderConfig & + ChainConfig & + Pick; export const sequencerConfigMappings: ConfigMappingsType = { transactionPollingIntervalMS: { @@ -132,6 +143,7 @@ export const sequencerClientConfigMappings: ConfigMappingsType>; private publicClient: PublicClient; + private ethereumSlotDuration: number; - constructor(config: L1ReaderConfig) { + constructor(config: L1ReaderConfig & Pick) { const { l1RpcUrl, l1ChainId: chainId, l1Contracts } = config; const chain = createEthereumChain(l1RpcUrl, chainId); + this.ethereumSlotDuration = config.ethereumSlotDuration; + this.publicClient = createPublicClient({ chain: chain.chainInfo, transport: http(chain.rpcUrl), @@ -67,7 +64,7 @@ export class GlobalVariableBuilder implements GlobalVariableBuilderInterface { const chainId = new Fr(this.publicClient.chain.id); if (slotNumber === undefined) { - const ts = BigInt((await this.publicClient.getBlock()).timestamp + BigInt(ETHEREUM_SLOT_DURATION)); + const ts = BigInt((await this.publicClient.getBlock()).timestamp + BigInt(this.ethereumSlotDuration)); slotNumber = await this.rollupContract.read.getSlotAt([ts]); } diff --git a/yarn-project/sequencer-client/src/publisher/l1-publisher.test.ts b/yarn-project/sequencer-client/src/publisher/l1-publisher.test.ts index a3e6d007c428..3fd8ced08374 100644 --- a/yarn-project/sequencer-client/src/publisher/l1-publisher.test.ts +++ b/yarn-project/sequencer-client/src/publisher/l1-publisher.test.ts @@ -1,5 +1,6 @@ import { L2Block } from '@aztec/circuit-types'; import { EthAddress } from '@aztec/circuits.js'; +import { type L1ContractsConfig, getL1ContractsConfigEnvVars } from '@aztec/ethereum'; import { type ViemSignature } from '@aztec/foundation/eth-signature'; import { sleep } from '@aztec/foundation/sleep'; import { RollupAbi } from '@aztec/l1-artifacts'; @@ -91,11 +92,10 @@ describe('L1Publisher', () => { l1RpcUrl: `http://127.0.0.1:8545`, l1ChainId: 1, publisherPrivateKey: `0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80`, - l1Contracts: { - rollupAddress: EthAddress.ZERO.toString(), - }, + l1Contracts: { rollupAddress: EthAddress.ZERO.toString() }, l1PublishRetryIntervalMS: 1, - } as unknown as TxSenderConfig & PublisherConfig; + ethereumSlotDuration: getL1ContractsConfigEnvVars().ethereumSlotDuration, + } as unknown as TxSenderConfig & PublisherConfig & Pick; publisher = new L1Publisher(config, new NoopTelemetryClient()); diff --git a/yarn-project/sequencer-client/src/publisher/l1-publisher.ts b/yarn-project/sequencer-client/src/publisher/l1-publisher.ts index 1553e4e07a53..229ba4f7d752 100644 --- a/yarn-project/sequencer-client/src/publisher/l1-publisher.ts +++ b/yarn-project/sequencer-client/src/publisher/l1-publisher.ts @@ -10,15 +10,14 @@ import { import { type L1PublishBlockStats, type L1PublishProofStats } from '@aztec/circuit-types/stats'; import { AGGREGATION_OBJECT_LENGTH, - AZTEC_EPOCH_DURATION, - ETHEREUM_SLOT_DURATION, + AZTEC_MAX_EPOCH_DURATION, EthAddress, type FeeRecipient, type Header, type Proof, type RootRollupPublicInputs, } from '@aztec/circuits.js'; -import { createEthereumChain } from '@aztec/ethereum'; +import { type L1ContractsConfig, createEthereumChain } from '@aztec/ethereum'; import { makeTuple } from '@aztec/foundation/array'; import { areArraysEqual, compactArray, times } from '@aztec/foundation/collection'; import { type Signature } from '@aztec/foundation/eth-signature'; @@ -123,7 +122,7 @@ export type L1SubmitEpochProofArgs = { endTimestamp: Fr; outHash: Fr; proverId: Fr; - fees: Tuple; + fees: Tuple; proof: Proof; }; @@ -158,12 +157,17 @@ export class L1Publisher { private publicClient: PublicClient; private walletClient: WalletClient; private account: PrivateKeyAccount; + private ethereumSlotDuration: bigint; public static PROPOSE_GAS_GUESS: bigint = 12_000_000n; public static PROPOSE_AND_CLAIM_GAS_GUESS: bigint = this.PROPOSE_GAS_GUESS + 100_000n; - constructor(config: TxSenderConfig & PublisherConfig, client: TelemetryClient) { + constructor( + config: TxSenderConfig & PublisherConfig & Pick, + client: TelemetryClient, + ) { this.sleepTimeMs = config?.l1PublishRetryIntervalMS ?? 60_000; + this.ethereumSlotDuration = BigInt(config.ethereumSlotDuration); this.metrics = new L1PublisherMetrics(client, 'L1Publisher'); const { l1RpcUrl: rpcUrl, l1ChainId: chainId, publisherPrivateKey, l1Contracts } = config; @@ -240,7 +244,7 @@ export class L1Publisher { // FIXME: This should not throw if unable to propose but return a falsey value, so // we can differentiate between errors when hitting the L1 rollup contract (eg RPC error) // which may require a retry, vs actually not being the turn for proposing. - const timeOfNextL1Slot = BigInt((await this.publicClient.getBlock()).timestamp + BigInt(ETHEREUM_SLOT_DURATION)); + const timeOfNextL1Slot = BigInt((await this.publicClient.getBlock()).timestamp + this.ethereumSlotDuration); const [slot, blockNumber] = await this.rollupContract.read.canProposeAtTime([ timeOfNextL1Slot, `0x${archive.toString('hex')}`, @@ -306,7 +310,7 @@ export class L1Publisher { } public async validateProofQuote(quote: EpochProofQuote): Promise { - const timeOfNextL1Slot = BigInt((await this.publicClient.getBlock()).timestamp + BigInt(ETHEREUM_SLOT_DURATION)); + const timeOfNextL1Slot = BigInt((await this.publicClient.getBlock()).timestamp + this.ethereumSlotDuration); const args = [timeOfNextL1Slot, quote.toViemArgs()] as const; try { await this.rollupContract.read.validateEpochProofRightClaimAtTime(args, { account: this.account }); @@ -334,7 +338,7 @@ export class L1Publisher { signatures: [], }, ): Promise { - const ts = BigInt((await this.publicClient.getBlock()).timestamp + BigInt(ETHEREUM_SLOT_DURATION)); + const ts = BigInt((await this.publicClient.getBlock()).timestamp + this.ethereumSlotDuration); const formattedSignatures = attestationData.signatures.map(attest => attest.toViemSignature()); const flags = { ignoreDA: true, ignoreSignatures: formattedSignatures.length == 0 }; @@ -756,7 +760,7 @@ export class L1Publisher { args.publicInputs.outHash.toString(), args.publicInputs.proverId.toString(), ], - makeTuple(AZTEC_EPOCH_DURATION * 2, i => + makeTuple(AZTEC_MAX_EPOCH_DURATION * 2, i => i % 2 === 0 ? args.publicInputs.fees[i / 2].recipient.toField().toString() : args.publicInputs.fees[(i - 1) / 2].value.toString(), diff --git a/yarn-project/sequencer-client/src/sequencer/sequencer.test.ts b/yarn-project/sequencer-client/src/sequencer/sequencer.test.ts index 57ce2ad02b3c..c043ce5b0d82 100644 --- a/yarn-project/sequencer-client/src/sequencer/sequencer.test.ts +++ b/yarn-project/sequencer-client/src/sequencer/sequencer.test.ts @@ -21,7 +21,6 @@ import { mockTxForRollup, } from '@aztec/circuit-types'; import { - AZTEC_EPOCH_DURATION, AztecAddress, type ContractDataSource, EthAddress, @@ -30,6 +29,7 @@ import { GlobalVariables, NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP, } from '@aztec/circuits.js'; +import { DefaultL1ContractsConfig } from '@aztec/ethereum'; import { Buffer32 } from '@aztec/foundation/buffer'; import { times } from '@aztec/foundation/collection'; import { randomBytes } from '@aztec/foundation/crypto'; @@ -69,6 +69,7 @@ describe('sequencer', () => { let sequencer: TestSubject; + const epochDuration = DefaultL1ContractsConfig.aztecEpochDuration; const chainId = new Fr(12345); const version = Fr.ZERO; const coinbase = EthAddress.random(); @@ -547,7 +548,7 @@ describe('sequencer', () => { let txHash: TxHash; let currentEpoch = 0n; const setupForBlockNumber = (blockNumber: number) => { - currentEpoch = BigInt(blockNumber) / BigInt(AZTEC_EPOCH_DURATION); + currentEpoch = BigInt(blockNumber) / BigInt(epochDuration); // Create a new block and header block = L2Block.random(blockNumber); @@ -584,7 +585,7 @@ describe('sequencer', () => { ]); publisher.getEpochForSlotNumber.mockImplementation((slotNumber: bigint) => - Promise.resolve(slotNumber / BigInt(AZTEC_EPOCH_DURATION)), + Promise.resolve(slotNumber / BigInt(epochDuration)), ); const tx = mockTxForRollup(); @@ -596,7 +597,7 @@ describe('sequencer', () => { }; it('submits a valid proof quote with a block', async () => { - const blockNumber = AZTEC_EPOCH_DURATION + 1; + const blockNumber = epochDuration + 1; setupForBlockNumber(blockNumber); const proofQuote = mockEpochProofQuote( @@ -641,7 +642,7 @@ describe('sequencer', () => { }); it('does not submit a quote with an expired slot number', async () => { - const blockNumber = AZTEC_EPOCH_DURATION + 1; + const blockNumber = epochDuration + 1; setupForBlockNumber(blockNumber); const proofQuote = mockEpochProofQuote( @@ -665,7 +666,7 @@ describe('sequencer', () => { }); it('does not submit a valid quote if unable to claim epoch', async () => { - const blockNumber = AZTEC_EPOCH_DURATION + 1; + const blockNumber = epochDuration + 1; setupForBlockNumber(blockNumber); const proofQuote = mockEpochProofQuote( @@ -687,7 +688,7 @@ describe('sequencer', () => { }); it('does not submit an invalid quote', async () => { - const blockNumber = AZTEC_EPOCH_DURATION + 1; + const blockNumber = epochDuration + 1; setupForBlockNumber(blockNumber); const proofQuote = mockEpochProofQuote( @@ -712,7 +713,7 @@ describe('sequencer', () => { }); it('only selects valid quotes', async () => { - const blockNumber = AZTEC_EPOCH_DURATION + 1; + const blockNumber = epochDuration + 1; setupForBlockNumber(blockNumber); // Create 1 valid quote and 3 that have a higher fee but are invalid @@ -767,7 +768,7 @@ describe('sequencer', () => { }); it('selects the lowest cost valid quote', async () => { - const blockNumber = AZTEC_EPOCH_DURATION + 1; + const blockNumber = epochDuration + 1; setupForBlockNumber(blockNumber); // Create 3 valid quotes with different fees. diff --git a/yarn-project/sequencer-client/src/sequencer/sequencer.ts b/yarn-project/sequencer-client/src/sequencer/sequencer.ts index 17224529de70..1be8fd7041a7 100644 --- a/yarn-project/sequencer-client/src/sequencer/sequencer.ts +++ b/yarn-project/sequencer-client/src/sequencer/sequencer.ts @@ -12,7 +12,6 @@ import { import type { AllowedElement, Signature, WorldStateSynchronizerStatus } from '@aztec/circuit-types/interfaces'; import { type L2BlockBuiltStats } from '@aztec/circuit-types/stats'; import { - AZTEC_SLOT_DURATION, AppendOnlyTreeSnapshot, ContentCommitment, GENESIS_ARCHIVE_ROOT, @@ -85,7 +84,8 @@ export class Sequencer { private maxBlockSizeInBytes: number = 1024 * 1024; private metrics: SequencerMetrics; private isFlushing: boolean = false; - protected l1GenesisTime: number; + protected l1GenesisTime: number = 0; + protected aztecSlotDuration: number = 0; /** * The maximum number of seconds that the sequencer can be into a slot to transition to a particular state. * For example, in order to transition into WAITING_FOR_ATTESTATIONS, the sequencer can be at most 3 seconds into the slot. @@ -108,11 +108,9 @@ export class Sequencer { private config: SequencerConfig = {}, private log = createDebugLogger('aztec:sequencer'), ) { - this.l1GenesisTime = 0; this.updateConfig(config); this.metrics = new SequencerMetrics(telemetry, () => this.state, 'Sequencer'); this.log.verbose(`Initialized sequencer with ${this.minTxsPerBLock}-${this.maxTxsPerBlock} txs per block.`); - this.setTimeTable(); } get tracer(): Tracer { @@ -162,19 +160,19 @@ export class Sequencer { this.config = config; } - public setTimeTable() { + private setTimeTable() { const newTimeTable: Record = { - [SequencerState.STOPPED]: AZTEC_SLOT_DURATION, - [SequencerState.IDLE]: AZTEC_SLOT_DURATION, - [SequencerState.SYNCHRONIZING]: AZTEC_SLOT_DURATION, - [SequencerState.PROPOSER_CHECK]: AZTEC_SLOT_DURATION, // We always want to allow the full slot to check if we are the proposer + [SequencerState.STOPPED]: this.aztecSlotDuration, + [SequencerState.IDLE]: this.aztecSlotDuration, + [SequencerState.SYNCHRONIZING]: this.aztecSlotDuration, + [SequencerState.PROPOSER_CHECK]: this.aztecSlotDuration, // We always want to allow the full slot to check if we are the proposer [SequencerState.WAITING_FOR_TXS]: 3, [SequencerState.CREATING_BLOCK]: 5, [SequencerState.PUBLISHING_BLOCK_TO_PEERS]: 5 + this.maxTxsPerBlock * 2, // if we take 5 seconds to create block, then 4 transactions at 2 seconds each [SequencerState.WAITING_FOR_ATTESTATIONS]: 5 + this.maxTxsPerBlock * 2 + 3, // it shouldn't take 3 seconds to publish to peers [SequencerState.PUBLISHING_BLOCK]: 5 + this.maxTxsPerBlock * 2 + 3 + 5, // wait 5 seconds for attestations }; - if (this.enforceTimeTable && newTimeTable[SequencerState.PUBLISHING_BLOCK] > AZTEC_SLOT_DURATION) { + if (this.enforceTimeTable && newTimeTable[SequencerState.PUBLISHING_BLOCK] > this.aztecSlotDuration) { throw new Error('Sequencer cannot publish block in less than a slot'); } this.timeTable = newTimeTable; @@ -184,8 +182,13 @@ export class Sequencer { */ public async start() { const rollup = this.publisher.getRollupContract(); - const [l1GenesisTime] = await Promise.all([rollup.read.GENESIS_TIME()] as const); + const [l1GenesisTime, slotDuration] = await Promise.all([ + rollup.read.GENESIS_TIME(), + rollup.read.SLOT_DURATION(), + ] as const); this.l1GenesisTime = Number(l1GenesisTime); + this.aztecSlotDuration = Number(slotDuration); + this.setTimeTable(); this.runningPromise = new RunningPromise(this.work.bind(this), this.pollingIntervalMs); this.runningPromise.start(); this.setState(SequencerState.IDLE, true /** force */); @@ -376,7 +379,7 @@ export class Sequencer { return true; } - if (this.timeTable[proposedState] === AZTEC_SLOT_DURATION) { + if (this.timeTable[proposedState] === this.aztecSlotDuration) { return true; } @@ -402,7 +405,7 @@ export class Sequencer { ); return; } - const secondsIntoSlot = getSecondsIntoSlot(this.l1GenesisTime); + const secondsIntoSlot = getSecondsIntoSlot(this.l1GenesisTime, this.aztecSlotDuration); if (!this.doIHaveEnoughTimeLeft(proposedState, secondsIntoSlot)) { throw new SequencerTooSlowError(this.state, proposedState, this.timeTable[proposedState], secondsIntoSlot); } diff --git a/yarn-project/sequencer-client/src/sequencer/utils.ts b/yarn-project/sequencer-client/src/sequencer/utils.ts index eee02811b3a1..c423c29ace46 100644 --- a/yarn-project/sequencer-client/src/sequencer/utils.ts +++ b/yarn-project/sequencer-client/src/sequencer/utils.ts @@ -1,5 +1,4 @@ import type { BlockAttestation, EthAddress } from '@aztec/circuit-types'; -import { AZTEC_SLOT_DURATION } from '@aztec/circuits.js'; import { Signature } from '@aztec/foundation/eth-signature'; export enum SequencerState { @@ -74,6 +73,6 @@ export function orderAttestations(attestations: BlockAttestation[], orderAddress return orderedAttestations; } -export function getSecondsIntoSlot(l1GenesisTime: number): number { - return (Date.now() / 1000 - l1GenesisTime) % AZTEC_SLOT_DURATION; +export function getSecondsIntoSlot(l1GenesisTime: number, aztecSlotDuration: number): number { + return (Date.now() / 1000 - l1GenesisTime) % aztecSlotDuration; } diff --git a/yarn-project/validator-client/src/config.ts b/yarn-project/validator-client/src/config.ts index 305f9948c346..4adb17a82e63 100644 --- a/yarn-project/validator-client/src/config.ts +++ b/yarn-project/validator-client/src/config.ts @@ -1,10 +1,10 @@ -import { AZTEC_SLOT_DURATION } from '@aztec/circuits.js'; -import { NULL_KEY } from '@aztec/ethereum'; +import { NULL_KEY, l1ContractsConfigMappings } from '@aztec/ethereum'; import { type ConfigMappingsType, booleanConfigHelper, getConfigFromMappings, numberConfigHelper, + pickConfigMappings, } from '@aztec/foundation/config'; /** @@ -43,7 +43,10 @@ export const validatorClientConfigMappings: ConfigMappingsType