From 54066d4739846db25be5693b3bd693a718e1797a Mon Sep 17 00:00:00 2001 From: Santiago Palladino Date: Wed, 16 Apr 2025 16:40:04 -0300 Subject: [PATCH] chore: Use chain monitor to sync system time in p2p tests Instead of sending a tx and awaiting its receipt, we monitor for new l1 blocks and update time then. Should fix a [flake in p2p tests](http://ci.aztec-labs.com/e0ca323545d90e02) where `syncMockSystemTime` was called twice simultaneously and caused two txs from the same address to be sent at the same time, leading to a nonce clash: ``` 19:30:55 FormattedViemError: Nonce provided for the transaction is lower than the current nonce of the account. 19:30:55 Try increasing the nonce or find the latest nonce with `getTransactionCount`. 19:30:55 19:30:55 Request Arguments: 19:30:55 from: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 19:30:55 to: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 19:30:55 value: 0.000000000000000001 ETH 19:30:55 data: 0x 19:30:55 gas: 25201 19:30:55 maxFeePerGas: 1.365169136 gwei 19:30:55 maxPriorityFeePerGas: 1.2 gwei 19:30:55 19:30:55 Details: transaction already imported 19:30:55 Version: viem@2.23.7 19:30:55 19:30:55 298 | } 19:30:55 299 | } 19:30:55 > 300 | return new FormattedViemError(formattedRes.replace(/\\n/g, '\n'), error?.metaMessages); 19:30:55 | ^ 19:30:55 301 | } 19:30:55 302 | export function tryGetCustomErrorName(err) { 19:30:55 303 | try { 19:30:55 19:30:55 at formatViemError (../../ethereum/dest/utils.js:300:12) 19:30:55 at L1TxUtilsWithBlobs.sendTransaction (../../ethereum/dest/l1_tx_utils.js:177:31) 19:30:55 at L1TxUtilsWithBlobs.sendAndMonitorTransaction (../../ethereum/dest/l1_tx_utils.js:326:48) 19:30:55 at P2PNetworkTest.syncMockSystemTime (e2e_p2p/p2p_network.ts:163:25) 19:30:55 at e2e_p2p/reex.test.ts:219:9 ``` --- .../end-to-end/src/e2e_p2p/p2p_network.ts | 50 +------------------ .../end-to-end/src/e2e_p2p/reex.test.ts | 4 +- .../ethereum/src/test/chain_monitor.ts | 12 ++++- 3 files changed, 14 insertions(+), 52 deletions(-) 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 709304ae1bb0..e709bea79933 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 @@ -3,7 +3,6 @@ import type { InitialAccountData } from '@aztec/accounts/testing'; import type { AztecNodeConfig, AztecNodeService } from '@aztec/aztec-node'; import type { AccountWalletWithSecretKey } from '@aztec/aztec.js'; import { RollupContract, getExpectedAddress, getL1ContractsConfigEnvVars } from '@aztec/ethereum'; -import { L1TxUtilsWithBlobs } from '@aztec/ethereum/l1-tx-utils-with-blobs'; import { ChainMonitor, EthCheatCodesWithState } from '@aztec/ethereum/test'; import { type Logger, createLogger } from '@aztec/foundation/log'; import { ForwarderAbi, ForwarderBytecode, RollupAbi, TestERC20Abi } from '@aztec/l1-artifacts'; @@ -66,10 +65,6 @@ export class P2PNetworkTest { public bootstrapNode?: BootstrapNode; - private cleanupInterval: NodeJS.Timeout | undefined = undefined; - - private gasUtils: L1TxUtilsWithBlobs | undefined = undefined; - constructor( testName: string, public bootstrapNodeEnr: string, @@ -147,32 +142,6 @@ export class P2PNetworkTest { return this.deployedAccounts[0]; } - /** - * Start a loop to sync the mock system time with the L1 block time - */ - public startSyncMockSystemTimeInterval() { - this.cleanupInterval = setInterval(() => { - void this.syncMockSystemTime().catch(err => this.logger.error('Error syncing mock system time', err)); - }, l1ContractsConfig.aztecSlotDuration * 1000); - } - - /** - * When using fake timers, we need to keep the system and anvil clocks in sync. - */ - public async syncMockSystemTime() { - this.logger.info('Syncing mock system time'); - const { dateProvider, deployL1ContractsValues } = this.ctx!; - // Send a tx and only update the time after the tx is mined, as eth time is not continuous - const { receipt } = await this.gasUtils!.sendAndMonitorTransaction({ - to: this.baseAccount.address, - data: '0x', - value: 1n, - }); - const timestamp = await deployL1ContractsValues.publicClient.getBlock({ blockNumber: receipt.blockNumber }); - this.logger.info(`Timestamp: ${timestamp.timestamp}`); - dateProvider.setTime(Number(timestamp.timestamp) * 1000); - } - async addBootstrapNode() { await this.snapshotManager.snapshot('add-bootstrap-node', async ({ aztecNodeConfig }) => { const telemetry = getEndToEndTestTelemetryClient(this.metricsPort); @@ -341,22 +310,8 @@ export class P2PNetworkTest { const { prefilledPublicData } = await getGenesisValues(initialFundedAccounts); this.prefilledPublicData = prefilledPublicData; - this.startSyncMockSystemTimeInterval(); - - this.gasUtils = new L1TxUtilsWithBlobs( - this.ctx.deployL1ContractsValues.publicClient, - this.ctx.deployL1ContractsValues.walletClient, - this.logger, - { - gasLimitBufferPercentage: 20, - maxGwei: 500n, - maxAttempts: 3, - checkIntervalMs: 100, - stallTimeMs: 1000, - }, - ); - this.monitor = new ChainMonitor(RollupContract.getFromL1ContractsValues(this.ctx.deployL1ContractsValues)).start(); + this.monitor.on('l1-block', ({ timestamp }) => this.ctx.dateProvider.setTime(Number(timestamp) * 1000)); } async stopNodes(nodes: AztecNodeService[]) { @@ -376,8 +331,5 @@ export class P2PNetworkTest { this.monitor.stop(); await this.bootstrapNode?.stop(); await this.snapshotManager.teardown(); - if (this.cleanupInterval) { - clearInterval(this.cleanupInterval); - } } } diff --git a/yarn-project/end-to-end/src/e2e_p2p/reex.test.ts b/yarn-project/end-to-end/src/e2e_p2p/reex.test.ts index 434b1cec0c6f..00c4407b6024 100644 --- a/yarn-project/end-to-end/src/e2e_p2p/reex.test.ts +++ b/yarn-project/end-to-end/src/e2e_p2p/reex.test.ts @@ -215,8 +215,8 @@ describe('e2e_p2p_reex', () => { } // Start a fresh slot and resume proposals - await t.ctx.cheatCodes.rollup.advanceToNextSlot(); - await t.syncMockSystemTime(); + const [ts] = await t.ctx.cheatCodes.rollup.advanceToNextSlot(); + t.ctx.dateProvider.setTime(Number(ts) * 1000); await resumeProposals(); diff --git a/yarn-project/ethereum/src/test/chain_monitor.ts b/yarn-project/ethereum/src/test/chain_monitor.ts index 4d408b3febb8..e5badc2920ee 100644 --- a/yarn-project/ethereum/src/test/chain_monitor.ts +++ b/yarn-project/ethereum/src/test/chain_monitor.ts @@ -1,10 +1,11 @@ import type { RollupContract } from '@aztec/ethereum/contracts'; import { createLogger } from '@aztec/foundation/log'; +import { EventEmitter } from 'events'; import type { PublicClient } from 'viem'; /** Utility class that polls the chain on quick intervals and logs new L1 blocks, L2 blocks, and L2 proofs. */ -export class ChainMonitor { +export class ChainMonitor extends EventEmitter { private readonly l1Client: PublicClient; private handle: NodeJS.Timeout | undefined; @@ -24,6 +25,7 @@ export class ChainMonitor { private logger = createLogger('aztecjs:utils:chain_monitor'), private readonly intervalMs = 200, ) { + super(); this.l1Client = rollup.client; } @@ -36,6 +38,7 @@ export class ChainMonitor { } stop() { + this.removeAllListeners(); if (this.handle) { clearInterval(this.handle!); this.handle = undefined; @@ -59,6 +62,7 @@ export class ChainMonitor { const timestamp = block.timestamp; const timestampString = new Date(Number(timestamp) * 1000).toTimeString().split(' ')[0]; + this.emit('l1-block', { l1BlockNumber: newL1BlockNumber, timestamp }); let msg = `L1 block ${newL1BlockNumber} mined at ${timestampString}`; const newL2BlockNumber = Number(await this.rollup.getBlockNumber()); @@ -67,6 +71,7 @@ export class ChainMonitor { msg += ` with new L2 block ${newL2BlockNumber} for epoch ${epochNumber}`; this.l2BlockNumber = newL2BlockNumber; this.l2BlockTimestamp = timestamp; + this.emit('l2-block', { l2BlockNumber: newL2BlockNumber, l1BlockNumber: newL1BlockNumber, timestamp }); } const newL2ProvenBlockNumber = Number(await this.rollup.getProvenBlockNumber()); @@ -75,6 +80,11 @@ export class ChainMonitor { msg += ` with proof up to L2 block ${newL2ProvenBlockNumber} for epoch ${epochNumber}`; this.l2ProvenBlockNumber = newL2ProvenBlockNumber; this.l2ProvenBlockTimestamp = timestamp; + this.emit('l2-block-proven', { + l2ProvenBlockNumber: newL2ProvenBlockNumber, + l1BlockNumber: newL1BlockNumber, + timestamp, + }); } this.logger.info(msg, {