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..5b8a5e3722a9 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[]; } 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