From b0f922492d2f2c69581494ebed2254f57098a300 Mon Sep 17 00:00:00 2001 From: LHerskind Date: Mon, 2 Sep 2024 19:11:14 +0000 Subject: [PATCH] fix: address issues when using wall-time --- l1-contracts/src/core/Rollup.sol | 18 ++++------ l1-contracts/src/core/interfaces/IRollup.sol | 5 +-- .../src/core/libraries/ConstantsGen.sol | 1 + .../src/core/sequencer_selection/Leonidas.sol | 4 +-- .../crates/types/src/constants.nr | 1 + yarn-project/circuits.js/src/constants.gen.ts | 2 ++ .../src/e2e_l1_with_wall_time.test.ts | 4 +-- .../end-to-end/src/e2e_p2p_network.test.ts | 27 +++++++++++++-- yarn-project/end-to-end/src/fixtures/utils.ts | 6 +++- .../end-to-end/src/fixtures/watcher.ts | 2 +- .../src/publisher/l1-publisher.ts | 33 +------------------ .../src/sequencer/sequencer.ts | 6 +++- 12 files changed, 52 insertions(+), 57 deletions(-) diff --git a/l1-contracts/src/core/Rollup.sol b/l1-contracts/src/core/Rollup.sol index e4bc8ebb589..4d7efa4ee38 100644 --- a/l1-contracts/src/core/Rollup.sol +++ b/l1-contracts/src/core/Rollup.sol @@ -217,9 +217,6 @@ contract Rollup is Leonidas, IRollup, ITestRollup { * * @dev TODO(#7346): Verify root proofs rather than block root when batch rollups are integrated. * - * @dev Will call `_progressState` to update the proven chain. Notice this have potentially - * unbounded gas consumption. - * * @dev Will emit `L2ProofVerified` if the proof is valid * * @dev Will throw if: @@ -368,16 +365,15 @@ contract Rollup is Leonidas, IRollup, ITestRollup { } /** - * @notice Check if a proposer can propose at a given time + * @notice Check if msg.sender can propose at a given time * * @param _ts - The timestamp to check - * @param _proposer - The proposer to check * @param _archive - The archive to check (should be the latest archive) * * @return uint256 - The slot at the given timestamp * @return uint256 - The block number at the given timestamp */ - function canProposeAtTime(uint256 _ts, address _proposer, bytes32 _archive) + function canProposeAtTime(uint256 _ts, bytes32 _archive) external view override(IRollup) @@ -395,10 +391,10 @@ contract Rollup is Leonidas, IRollup, ITestRollup { revert Errors.Rollup__InvalidArchive(tipArchive, _archive); } - address proposer = getProposerAt(_ts); - if (proposer != address(0) && proposer != _proposer) { - revert Errors.Leonidas__InvalidProposer(proposer, _proposer); - } + SignatureLib.Signature[] memory sigs = new SignatureLib.Signature[](0); + DataStructures.ExecutionFlags memory flags = + DataStructures.ExecutionFlags({ignoreDA: true, ignoreSignatures: true}); + _validateLeonidas(slot, sigs, _archive, flags); return (slot, pendingBlockCount); } @@ -575,7 +571,7 @@ contract Rollup is Leonidas, IRollup, ITestRollup { revert Errors.Rollup__InvalidEpoch(currentEpoch, epochNumber); } - _proposePendingBlock(_slot, _signatures, _digest, _flags); + _validateLeonidas(_slot, _signatures, _digest, _flags); } /** diff --git a/l1-contracts/src/core/interfaces/IRollup.sol b/l1-contracts/src/core/interfaces/IRollup.sol index 33e5640537b..febd60047fd 100644 --- a/l1-contracts/src/core/interfaces/IRollup.sol +++ b/l1-contracts/src/core/interfaces/IRollup.sol @@ -19,10 +19,7 @@ interface IRollup { event L2ProofVerified(uint256 indexed blockNumber, bytes32 indexed proverId); event PrunedPending(uint256 provenBlockCount, uint256 pendingBlockCount); - function canProposeAtTime(uint256 _ts, address _proposer, bytes32 _archive) - external - view - returns (uint256, uint256); + function canProposeAtTime(uint256 _ts, bytes32 _archive) external view returns (uint256, uint256); function validateHeader( bytes calldata _header, SignatureLib.Signature[] memory _signatures, diff --git a/l1-contracts/src/core/libraries/ConstantsGen.sol b/l1-contracts/src/core/libraries/ConstantsGen.sol index e85d0854c55..d954c8b778f 100644 --- a/l1-contracts/src/core/libraries/ConstantsGen.sol +++ b/l1-contracts/src/core/libraries/ConstantsGen.sol @@ -104,6 +104,7 @@ library Constants { uint256 internal constant ETHEREUM_SLOT_DURATION = 12; uint256 internal constant AZTEC_SLOT_DURATION = 12; uint256 internal constant AZTEC_EPOCH_DURATION = 48; + uint256 internal constant AZTEC_TARGET_COMMITTEE_SIZE = 48; uint256 internal constant GENESIS_ARCHIVE_ROOT = 8142738430000951296386584486068033372964809139261822027365426310856631083550; uint256 internal constant FEE_JUICE_INITIAL_MINT = 20000000000; diff --git a/l1-contracts/src/core/sequencer_selection/Leonidas.sol b/l1-contracts/src/core/sequencer_selection/Leonidas.sol index 03fc9ec9bd2..1d1d26b61bd 100644 --- a/l1-contracts/src/core/sequencer_selection/Leonidas.sol +++ b/l1-contracts/src/core/sequencer_selection/Leonidas.sol @@ -62,7 +62,7 @@ contract Leonidas is Ownable, ILeonidas { // The target number of validators in a committee // @todo #8021 - uint256 public constant TARGET_COMMITTEE_SIZE = EPOCH_DURATION; + uint256 public constant TARGET_COMMITTEE_SIZE = Constants.AZTEC_TARGET_COMMITTEE_SIZE; // The time that the contract was deployed uint256 public immutable GENESIS_TIME; @@ -344,7 +344,7 @@ contract Leonidas is Ownable, ILeonidas { * @param _signatures - The signatures of the committee members * @param _digest - The digest of the block */ - function _proposePendingBlock( + function _validateLeonidas( uint256 _slot, SignatureLib.Signature[] memory _signatures, bytes32 _digest, 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 0183f973880..68f2ed5ea3e 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr @@ -137,6 +137,7 @@ global ETHEREUM_SLOT_DURATION: u32 = 12; // AZTEC_SLOT_DURATION should be a multiple of ETHEREUM_SLOT_DURATION global AZTEC_SLOT_DURATION: u32 = ETHEREUM_SLOT_DURATION * 1; global AZTEC_EPOCH_DURATION: u32 = 48; +global AZTEC_TARGET_COMMITTEE_SIZE: u32 = 48; // 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. global GENESIS_ARCHIVE_ROOT: Field = 0x1200a06aae1368abe36530b585bd7a4d2ba4de5037b82076412691a187d7621e; diff --git a/yarn-project/circuits.js/src/constants.gen.ts b/yarn-project/circuits.js/src/constants.gen.ts index 10c2bd38b55..9a55c2fee31 100644 --- a/yarn-project/circuits.js/src/constants.gen.ts +++ b/yarn-project/circuits.js/src/constants.gen.ts @@ -90,6 +90,7 @@ export const BLOB_SIZE_IN_BYTES = 126976; export const ETHEREUM_SLOT_DURATION = 12; export const AZTEC_SLOT_DURATION = 12; export const AZTEC_EPOCH_DURATION = 48; +export const AZTEC_TARGET_COMMITTEE_SIZE = 48; export const GENESIS_ARCHIVE_ROOT = 8142738430000951296386584486068033372964809139261822027365426310856631083550n; export const FEE_JUICE_INITIAL_MINT = 20000000000; export const MAX_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS = 20000; @@ -395,6 +396,7 @@ export enum GeneratorIndex { SIDE_EFFECT = 29, FEE_PAYLOAD = 30, COMBINED_PAYLOAD = 31, + TX_NULLIFIER = 32, TX_REQUEST = 33, SIGNATURE_PAYLOAD = 34, VK = 41, 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 70ec2f7f6b4..3b73125b92a 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,6 @@ import { getSchnorrAccount } from '@aztec/accounts/schnorr'; import { type DebugLogger, Fr, GrumpkinScalar, type PXE, type SentTx, TxStatus } from '@aztec/aztec.js'; -import { EthAddress } from '@aztec/circuits.js'; +import { ETHEREUM_SLOT_DURATION, EthAddress } from '@aztec/circuits.js'; import { type PXEService } from '@aztec/pxe'; import { privateKeyToAccount } from 'viem/accounts'; @@ -16,7 +16,7 @@ describe('e2e_l1_with_wall_time', () => { const account = privateKeyToAccount(`0x${getPrivateKeyFromIndex(0)!.toString('hex')}`); const initialValidators = [EthAddress.fromString(account.address)]; - ({ teardown, logger, pxe } = await setup(0, { initialValidators, l1BlockTime: 12 })); + ({ teardown, logger, pxe } = await setup(0, { initialValidators, l1BlockTime: ETHEREUM_SLOT_DURATION, salt: 420 })); }); afterEach(() => teardown()); diff --git a/yarn-project/end-to-end/src/e2e_p2p_network.test.ts b/yarn-project/end-to-end/src/e2e_p2p_network.test.ts index 2037f9a200d..8e720410550 100644 --- a/yarn-project/end-to-end/src/e2e_p2p_network.test.ts +++ b/yarn-project/end-to-end/src/e2e_p2p_network.test.ts @@ -11,7 +11,7 @@ import { TxStatus, sleep, } from '@aztec/aztec.js'; -import { EthAddress } from '@aztec/circuits.js'; +import { ETHEREUM_SLOT_DURATION, EthAddress } from '@aztec/circuits.js'; import { RollupAbi } from '@aztec/l1-artifacts'; import { type BootstrapNode } from '@aztec/p2p'; import { type PXEService, createPXEService, getPXEServiceConfig as getRpcConfig } from '@aztec/pxe'; @@ -60,7 +60,12 @@ describe('e2e_p2p_network', () => { const initialValidators = [EthAddress.fromString(account.address)]; - ({ teardown, config, logger, deployL1ContractsValues } = await setup(0, { initialValidators, ...options })); + ({ teardown, config, logger, deployL1ContractsValues } = await setup(0, { + initialValidators, + l1BlockTime: ETHEREUM_SLOT_DURATION, + salt: 420, + ...options, + })); bootstrapNode = await createBootstrapNode(BOOT_NODE_UDP_PORT); bootstrapNodeEnr = bootstrapNode.getENR().encodeTxt(); @@ -77,15 +82,31 @@ describe('e2e_p2p_network', () => { for (let i = 0; i < NUM_NODES; i++) { const account = privateKeyToAccount(`0x${getPrivateKeyFromIndex(i + 1)!.toString('hex')}`); await rollup.write.addValidator([account.address]); + logger.debug(`Adding ${account.address} as validator`); } + // Remove the initial sequencer from the set! This was the sequencer we used for perform the setup. + logger.debug(`Removing ${account.address} as validator`); + const txHash = await rollup.write.removeValidator([account.address]); + + await deployL1ContractsValues.publicClient.waitForTransactionReceipt({ hash: txHash }); + //@note Now we jump ahead to the next epoch such that the validator committee is picked // INTERVAL MINING: If we are using anvil interval mining this will NOT progress the time! // Which means that the validator set will still be empty! So anyone can propose. const slotsInEpoch = await rollup.read.EPOCH_DURATION(); const timestamp = await rollup.read.getTimestampForSlot([slotsInEpoch]); const cheatCodes = new EthCheatCodes(config.l1RpcUrl); - await cheatCodes.warp(Number(timestamp)); + try { + await cheatCodes.warp(Number(timestamp)); + } catch (err) { + logger.debug('Warp failed, time already satisfied'); + } + + // Send and await a tx to make sure we mine a block for the warp to correctly progress. + await deployL1ContractsValues.publicClient.waitForTransactionReceipt({ + hash: await deployL1ContractsValues.walletClient.sendTransaction({ to: account.address, value: 1n, account }), + }); }); afterEach(() => teardown()); diff --git a/yarn-project/end-to-end/src/fixtures/utils.ts b/yarn-project/end-to-end/src/fixtures/utils.ts index dfbebc93636..17ca30c3300 100644 --- a/yarn-project/end-to-end/src/fixtures/utils.ts +++ b/yarn-project/end-to-end/src/fixtures/utils.ts @@ -455,7 +455,11 @@ export async function setup( deployL1ContractsValues.l1ContractAddresses.rollupAddress, deployL1ContractsValues.publicClient, ); - watcher.start(); + + // If we are NOT using wall time, we should start the watcher to jump in time as needed. + if (!opts.l1BlockTime) { + watcher.start(); + } const wallets = numberOfAccounts > 0 ? await createAccounts(pxe, numberOfAccounts) : []; const cheatCodes = CheatCodes.create(config.l1RpcUrl, pxe!); diff --git a/yarn-project/end-to-end/src/fixtures/watcher.ts b/yarn-project/end-to-end/src/fixtures/watcher.ts index 0d254fbf5ff..2934be8b531 100644 --- a/yarn-project/end-to-end/src/fixtures/watcher.ts +++ b/yarn-project/end-to-end/src/fixtures/watcher.ts @@ -58,7 +58,7 @@ export class Watcher { this.logger.info(`Slot ${currentSlot} was filled, jumped to next slot`); } } catch (err) { - this.logger.error('mineIfSlotFilled failed', err); + this.logger.error('mineIfSlotFilled failed'); } } } diff --git a/yarn-project/sequencer-client/src/publisher/l1-publisher.ts b/yarn-project/sequencer-client/src/publisher/l1-publisher.ts index b8521cf5247..5c617ab3624 100644 --- a/yarn-project/sequencer-client/src/publisher/l1-publisher.ts +++ b/yarn-project/sequencer-client/src/publisher/l1-publisher.ts @@ -88,13 +88,6 @@ export type L1SubmitProofArgs = { aggregationObject: Buffer; }; -export type MetadataForSlot = { - proposer: EthAddress; - slot: bigint; - pendingBlockNumber: bigint; - archive: Buffer; -}; - /** * Publishes L2 blocks to L1. This implementation does *not* retry a transaction in * the event of network congestion, but should work for local development. @@ -176,11 +169,7 @@ export class L1Publisher { */ public async canProposeAtNextEthBlock(archive: Buffer): Promise<[bigint, bigint]> { const ts = BigInt((await this.publicClient.getBlock()).timestamp + BigInt(ETHEREUM_SLOT_DURATION)); - const [slot, blockNumber] = await this.rollupContract.read.canProposeAtTime([ - ts, - this.account.address, - `0x${archive.toString('hex')}`, - ]); + const [slot, blockNumber] = await this.rollupContract.read.canProposeAtTime([ts, `0x${archive.toString('hex')}`]); return [slot, blockNumber]; } @@ -227,26 +216,6 @@ export class L1Publisher { } } - // @note Assumes that all ethereum slots have blocks - // Using next Ethereum block so we do NOT need to wait for it being mined before seeing the effect - public async getMetadataForSlotAtNextEthBlock(): Promise { - const ts = BigInt((await this.publicClient.getBlock()).timestamp + BigInt(ETHEREUM_SLOT_DURATION)); - - const [submitter, slot, pendingBlockCount, archive] = await Promise.all([ - this.rollupContract.read.getProposerAt([ts]), - this.rollupContract.read.getSlotAt([ts]), - this.rollupContract.read.pendingBlockCount(), - this.rollupContract.read.archive(), - ]); - - return { - proposer: EthAddress.fromString(submitter), - slot, - pendingBlockNumber: pendingBlockCount - 1n, - archive: Buffer.from(archive.replace('0x', ''), 'hex'), - }; - } - public async getCurrentEpochCommittee(): Promise { const committee = await this.rollupContract.read.getCurrentEpochCommittee(); return committee.map(EthAddress.fromString); diff --git a/yarn-project/sequencer-client/src/sequencer/sequencer.ts b/yarn-project/sequencer-client/src/sequencer/sequencer.ts index acc7851b43f..57dd47d59ff 100644 --- a/yarn-project/sequencer-client/src/sequencer/sequencer.ts +++ b/yarn-project/sequencer-client/src/sequencer/sequencer.ts @@ -314,7 +314,11 @@ export class Sequencer { const revertError = err.walk(err => err instanceof ContractFunctionRevertedError); if (revertError instanceof ContractFunctionRevertedError) { const errorName = revertError.data?.errorName ?? ''; - this.log.debug(`canProposeAtTime failed with "${errorName}"`); + const args = + revertError.metaMessages && revertError.metaMessages?.length > 1 + ? revertError.metaMessages[1].trimStart() + : ''; + this.log.debug(`canProposeAtTime failed with "${errorName}${args}"`); } } throw err;