Skip to content

Commit

Permalink
Extract chain monitor and add to p2p tests
Browse files Browse the repository at this point in the history
  • Loading branch information
spalladino committed Jan 7, 2025
1 parent a799466 commit 0026464
Show file tree
Hide file tree
Showing 5 changed files with 106 additions and 39 deletions.
74 changes: 74 additions & 0 deletions yarn-project/aztec.js/src/utils/chain_monitor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { type RollupContract } from '@aztec/ethereum';
import { createLogger } from '@aztec/foundation/log';

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 {
private readonly l1Client: PublicClient;
private handle: NodeJS.Timeout | undefined;

/** Current L1 block number */
public l1BlockNumber!: number;
/** Current L2 block number */
public l2BlockNumber!: number;
/** Current L2 proven block number */
public l2ProvenBlockNumber!: number;

constructor(
private readonly rollup: RollupContract,
private logger = createLogger('aztecjs:utils:chain_monitor'),
private readonly intervalMs = 200,
) {
this.l1Client = rollup.client;
}

start() {
if (this.handle) {
throw new Error('Chain monitor already started');
}
this.handle = setInterval(() => this.run(), this.intervalMs);
}

stop() {
if (this.handle) {
clearInterval(this.handle!);
this.handle = undefined;
}
}

async run() {
const newL1BlockNumber = Number(await this.l1Client.getBlockNumber({ cacheTime: 0 }));
if (this.l1BlockNumber === newL1BlockNumber) {
return;
}
this.l1BlockNumber = newL1BlockNumber;

const block = await this.l1Client.getBlock({ blockNumber: BigInt(newL1BlockNumber), includeTransactions: false });
const timestamp = block.timestamp;
const timestampString = new Date(Number(timestamp) * 1000).toTimeString().split(' ')[0];

let msg = `L1 block ${newL1BlockNumber} mined at ${timestampString}`;

const newL2BlockNumber = Number(await this.rollup.getBlockNumber());
if (this.l2BlockNumber !== newL2BlockNumber) {
const epochNumber = await this.rollup.getEpochNumber(BigInt(newL2BlockNumber));
msg += ` with new L2 block ${newL2BlockNumber} for epoch ${epochNumber}`;
this.l2BlockNumber = newL2BlockNumber;
}

const newL2ProvenBlockNumber = Number(await this.rollup.getProvenBlockNumber());
if (this.l2ProvenBlockNumber !== newL2ProvenBlockNumber) {
const epochNumber = await this.rollup.getEpochNumber(BigInt(newL2ProvenBlockNumber));
msg += ` with proof up to L2 block ${newL2ProvenBlockNumber} for epoch ${epochNumber}`;
this.l2ProvenBlockNumber = newL2ProvenBlockNumber;
}

this.logger.info(msg, {
l1Timestamp: timestamp,
l1BlockNumber: this.l1BlockNumber,
l2BlockNumber: this.l2BlockNumber,
l2ProvenBlockNumber: this.l2ProvenBlockNumber,
});
}
}
1 change: 1 addition & 0 deletions yarn-project/aztec.js/src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ export * from './node.js';
export * from './anvil_test_watcher.js';
export * from './field_compressed_string.js';
export * from './portal_manager.js';
export * from './chain_monitor.js';
46 changes: 9 additions & 37 deletions yarn-project/end-to-end/src/e2e_epochs.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// eslint-disable-next-line no-restricted-imports
import { type EpochConstants, type Logger, getTimestampRangeForEpoch, retryUntil } from '@aztec/aztec.js';
import { ChainMonitor } from '@aztec/aztec.js/utils';
import { RollupContract } from '@aztec/ethereum/contracts';
import { type Delayer, waitUntilL1Timestamp } from '@aztec/ethereum/test';

Expand All @@ -18,11 +19,7 @@ describe('e2e_epochs', () => {
let logger: Logger;
let proverDelayer: Delayer;
let sequencerDelayer: Delayer;

let l2BlockNumber: number = 0;
let l2ProvenBlockNumber: number = 0;
let l1BlockNumber: number;
let handle: NodeJS.Timeout;
let monitor: ChainMonitor;

const EPOCH_DURATION_IN_L2_SLOTS = 4;
const L2_SLOT_DURATION_IN_L1_SLOTS = 2;
Expand Down Expand Up @@ -52,33 +49,8 @@ describe('e2e_epochs', () => {
rollup = RollupContract.getFromConfig(context.config);

// Loop that tracks L1 and L2 block numbers and logs whenever there's a new one.
// We could refactor this out to an utility if we want to use this in other tests.
handle = setInterval(async () => {
const newL1BlockNumber = Number(await l1Client.getBlockNumber({ cacheTime: 0 }));
if (l1BlockNumber === newL1BlockNumber) {
return;
}
const block = await l1Client.getBlock({ blockNumber: BigInt(newL1BlockNumber), includeTransactions: false });
const timestamp = block.timestamp;
l1BlockNumber = newL1BlockNumber;

let msg = `L1 block ${newL1BlockNumber} mined at ${timestamp}`;

const newL2BlockNumber = Number(await rollup.getBlockNumber());
if (l2BlockNumber !== newL2BlockNumber) {
const epochNumber = await rollup.getEpochNumber(BigInt(newL2BlockNumber));
msg += ` with new L2 block ${newL2BlockNumber} for epoch ${epochNumber}`;
l2BlockNumber = newL2BlockNumber;
}

const newL2ProvenBlockNumber = Number(await rollup.getProvenBlockNumber());
if (l2ProvenBlockNumber !== newL2ProvenBlockNumber) {
const epochNumber = await rollup.getEpochNumber(BigInt(newL2ProvenBlockNumber));
msg += ` with proof up to L2 block ${newL2ProvenBlockNumber} for epoch ${epochNumber}`;
l2ProvenBlockNumber = newL2ProvenBlockNumber;
}
logger.info(msg);
}, 200);
monitor = new ChainMonitor(rollup, logger);
monitor.start();

// The "as any" cast sucks, but it saves us from having to define test-only types for the provernode
// and sequencer that are exactly like the real ones but with the publisher exposed. We should
Expand All @@ -101,7 +73,7 @@ describe('e2e_epochs', () => {
});

afterEach(async () => {
clearInterval(handle);
monitor.stop();
await context.teardown();
});

Expand All @@ -115,12 +87,12 @@ describe('e2e_epochs', () => {

/** Waits until the given L2 block number is mined. */
const waitUntilL2BlockNumber = async (target: number) => {
await retryUntil(() => Promise.resolve(target === l2BlockNumber), `Wait until L2 block ${target}`, 60, 0.1);
await retryUntil(() => Promise.resolve(target === monitor.l2BlockNumber), `Wait until L2 block ${target}`, 60, 0.1);
};

/** Waits until the given L2 block number is marked as proven. */
const waitUntilProvenL2BlockNumber = async (target: number) => {
await retryUntil(() => Promise.resolve(target === l2ProvenBlockNumber), `Wait proven L2 block ${target}`, 60, 0.1);
const waitUntilProvenL2BlockNumber = async (t: number) => {
await retryUntil(() => Promise.resolve(t === monitor.l2ProvenBlockNumber), `Wait proven L2 block ${t}`, 60, 0.1);
};

it('does not allow submitting proof after epoch end', async () => {
Expand Down Expand Up @@ -163,7 +135,7 @@ describe('e2e_epochs', () => {
logger.info(`Starting epoch 1 after L2 block ${blockNumberAtEndOfEpoch0}`);

await waitUntilProvenL2BlockNumber(blockNumberAtEndOfEpoch0);
expect(l2BlockNumber).toEqual(blockNumberAtEndOfEpoch0);
expect(monitor.l2BlockNumber).toEqual(blockNumberAtEndOfEpoch0);
logger.info(`Test succeeded`);
});
});
8 changes: 7 additions & 1 deletion yarn-project/end-to-end/src/e2e_p2p/p2p_network.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { getSchnorrAccount } from '@aztec/accounts/schnorr';
import { type AztecNodeConfig, type AztecNodeService } from '@aztec/aztec-node';
import { type AccountWalletWithSecretKey } from '@aztec/aztec.js';
import { L1TxUtils, getL1ContractsConfigEnvVars } from '@aztec/ethereum';
import { ChainMonitor } from '@aztec/aztec.js/utils';
import { L1TxUtils, RollupContract, getL1ContractsConfigEnvVars } from '@aztec/ethereum';
import { EthCheatCodesWithState } from '@aztec/ethereum/test';
import { type Logger, createLogger } from '@aztec/foundation/log';
import { RollupAbi, TestERC20Abi } from '@aztec/l1-artifacts';
Expand Down Expand Up @@ -38,6 +39,7 @@ export class P2PNetworkTest {
private baseAccount;

public logger: Logger;
public monitor!: ChainMonitor;

public ctx!: SubsystemsContext;
public attesterPrivateKeys: `0x${string}`[] = [];
Expand Down Expand Up @@ -308,6 +310,9 @@ export class P2PNetworkTest {
stallTimeMs: 1000,
},
);

this.monitor = new ChainMonitor(RollupContract.getFromL1ContractsValues(this.ctx.deployL1ContractsValues));
this.monitor.start();
}

async stopNodes(nodes: AztecNodeService[]) {
Expand All @@ -325,6 +330,7 @@ export class P2PNetworkTest {
}

async teardown() {
this.monitor.stop();
await this.bootstrapNode.stop();
await this.snapshotManager.teardown();
if (this.cleanupInterval) {
Expand Down
16 changes: 15 additions & 1 deletion yarn-project/ethereum/src/contracts/rollup.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { AztecAddress } from '@aztec/foundation/aztec-address';
import { memoize } from '@aztec/foundation/decorators';
import { RollupAbi } from '@aztec/l1-artifacts';

Expand All @@ -12,16 +13,21 @@ import {
http,
} from 'viem';

import { type DeployL1Contracts } from '../deploy_l1_contracts.js';
import { createEthereumChain } from '../ethereum_chain.js';
import { type L1ReaderConfig } from '../l1_reader.js';

export class RollupContract {
private readonly rollup: GetContractReturnType<typeof RollupAbi, PublicClient<HttpTransport, Chain>>;

constructor(client: PublicClient, address: Hex) {
constructor(public readonly client: PublicClient, address: Hex) {
this.rollup = getContract({ address, abi: RollupAbi, client });
}

public get address() {
return AztecAddress.fromString(this.rollup.address);
}

@memoize
getL1StartBlock() {
return this.rollup.read.L1_BLOCK_AT_GENESIS();
Expand Down Expand Up @@ -69,6 +75,14 @@ export class RollupContract {
return this.rollup.read.getEpochForBlock([BigInt(blockNumber)]);
}

static getFromL1ContractsValues(deployL1ContractsValues: DeployL1Contracts) {
const {
publicClient,
l1ContractAddresses: { rollupAddress },
} = deployL1ContractsValues;
return new RollupContract(publicClient, rollupAddress.toString());
}

static getFromConfig(config: L1ReaderConfig) {
const client = createPublicClient({
transport: http(config.l1RpcUrl),
Expand Down

0 comments on commit 0026464

Please sign in to comment.