Skip to content

Commit 3d3fabb

Browse files
authored
chore: Remove sinon in favor of a date provider (#10705)
Sinon fake timers were altering log timestamps. Instead of tweaking the global system clock in p2p e2e tests, we introduce a date provider that is injected as a dependency into the epoch cache (the only component that relied on its time being in sync with L1), and we tweak that.
1 parent 5c8e017 commit 3d3fabb

File tree

17 files changed

+121
-47
lines changed

17 files changed

+121
-47
lines changed

yarn-project/aztec-node/src/aztec-node/server.ts

+6-3
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ import { type ContractArtifact } from '@aztec/foundation/abi';
5858
import { AztecAddress } from '@aztec/foundation/aztec-address';
5959
import { padArrayEnd } from '@aztec/foundation/collection';
6060
import { type Logger, createLogger } from '@aztec/foundation/log';
61-
import { Timer } from '@aztec/foundation/timer';
61+
import { DateProvider, Timer } from '@aztec/foundation/timer';
6262
import { type AztecKVStore } from '@aztec/kv-store';
6363
import { openTmpStore } from '@aztec/kv-store/lmdb';
6464
import { SHA256Trunc, StandardTree, UnbalancedTree } from '@aztec/merkle-tree';
@@ -138,10 +138,12 @@ export class AztecNodeService implements AztecNode, Traceable {
138138
telemetry?: TelemetryClient;
139139
logger?: Logger;
140140
publisher?: L1Publisher;
141+
dateProvider?: DateProvider;
141142
} = {},
142143
): Promise<AztecNodeService> {
143144
const telemetry = deps.telemetry ?? new NoopTelemetryClient();
144145
const log = deps.logger ?? createLogger('node');
146+
const dateProvider = deps.dateProvider ?? new DateProvider();
145147
const ethereumChain = createEthereumChain(config.l1RpcUrl, config.l1ChainId);
146148
//validate that the actual chain id matches that specified in configuration
147149
if (config.l1ChainId !== ethereumChain.chainInfo.id) {
@@ -154,7 +156,8 @@ export class AztecNodeService implements AztecNode, Traceable {
154156

155157
// we identify the P2P transaction protocol by using the rollup contract address.
156158
// this may well change in future
157-
config.transactionProtocol = `/aztec/tx/${config.l1Contracts.rollupAddress.toString()}`;
159+
const rollupAddress = config.l1Contracts.rollupAddress;
160+
config.transactionProtocol = `/aztec/tx/${rollupAddress.toString()}`;
158161

159162
// now create the merkle trees and the world state synchronizer
160163
const worldStateSynchronizer = await createWorldStateSynchronizer(config, archiver, telemetry);
@@ -169,7 +172,7 @@ export class AztecNodeService implements AztecNode, Traceable {
169172
// start both and wait for them to sync from the block source
170173
await Promise.all([p2pClient.start(), worldStateSynchronizer.start()]);
171174

172-
const validatorClient = await createValidatorClient(config, config.l1Contracts.rollupAddress, p2pClient, telemetry);
175+
const validatorClient = await createValidatorClient(config, rollupAddress, { p2pClient, telemetry, dateProvider });
173176

174177
// now create the sequencer
175178
const sequencer = config.disableValidator

yarn-project/end-to-end/package.json

-1
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,6 @@
9898
"devDependencies": {
9999
"0x": "^5.7.0",
100100
"@jest/globals": "^29.5.0",
101-
"@sinonjs/fake-timers": "^13.0.5",
102101
"@types/jest": "^29.5.0",
103102
"@types/js-yaml": "^4.0.9",
104103
"@types/lodash.chunk": "^4.2.9",

yarn-project/end-to-end/src/e2e_p2p/gossip_network.test.ts

+1
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ describe('e2e_p2p_network', () => {
7676
t.logger.info('Creating nodes');
7777
nodes = await createNodes(
7878
t.ctx.aztecNodeConfig,
79+
t.ctx.dateProvider,
7980
t.bootstrapNodeEnr,
8081
NUM_NODES,
8182
BOOT_NODE_UDP_PORT,

yarn-project/end-to-end/src/e2e_p2p/p2p_network.ts

+6-7
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ export class P2PNetworkTest {
9393
*/
9494
public async syncMockSystemTime() {
9595
this.logger.info('Syncing mock system time');
96-
const { timer, deployL1ContractsValues } = this.ctx!;
96+
const { dateProvider, deployL1ContractsValues } = this.ctx!;
9797
// Send a tx and only update the time after the tx is mined, as eth time is not continuous
9898
const tx = await deployL1ContractsValues.walletClient.sendTransaction({
9999
to: this.baseAccount.address,
@@ -104,8 +104,7 @@ export class P2PNetworkTest {
104104
hash: tx,
105105
});
106106
const timestamp = await deployL1ContractsValues.publicClient.getBlock({ blockNumber: receipt.blockNumber });
107-
timer.setSystemTime(Number(timestamp.timestamp) * 1000);
108-
this.logger.info(`Synced mock system time to ${timestamp.timestamp * 1000n}`);
107+
dateProvider.setTime(Number(timestamp.timestamp) * 1000);
109108
}
110109

111110
static async create({
@@ -133,7 +132,7 @@ export class P2PNetworkTest {
133132
async applyBaseSnapshots() {
134133
await this.snapshotManager.snapshot(
135134
'add-validators',
136-
async ({ deployL1ContractsValues, aztecNodeConfig, timer }) => {
135+
async ({ deployL1ContractsValues, aztecNodeConfig, dateProvider }) => {
137136
const rollup = getContract({
138137
address: deployL1ContractsValues.l1ContractAddresses.rollupAddress.toString(),
139138
abi: RollupAbi,
@@ -203,7 +202,7 @@ export class P2PNetworkTest {
203202

204203
// Set the system time in the node, only after we have warped the time and waited for a block
205204
// Time is only set in the NEXT block
206-
timer.setSystemTime(Number(timestamp) * 1000);
205+
dateProvider.setTime(Number(timestamp) * 1000);
207206
},
208207
);
209208
}
@@ -244,7 +243,7 @@ export class P2PNetworkTest {
244243
async removeInitialNode() {
245244
await this.snapshotManager.snapshot(
246245
'remove-inital-validator',
247-
async ({ deployL1ContractsValues, aztecNode, timer }) => {
246+
async ({ deployL1ContractsValues, aztecNode, dateProvider }) => {
248247
// Send and await a tx to make sure we mine a block for the warp to correctly progress.
249248
const receipt = await deployL1ContractsValues.publicClient.waitForTransactionReceipt({
250249
hash: await deployL1ContractsValues.walletClient.sendTransaction({
@@ -256,7 +255,7 @@ export class P2PNetworkTest {
256255
const block = await deployL1ContractsValues.publicClient.getBlock({
257256
blockNumber: receipt.blockNumber,
258257
});
259-
timer.setSystemTime(Number(block.timestamp) * 1000);
258+
dateProvider.setTime(Number(block.timestamp) * 1000);
260259

261260
await aztecNode.stop();
262261
},

yarn-project/end-to-end/src/e2e_p2p/rediscovery.test.ts

+2
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ describe('e2e_p2p_rediscovery', () => {
4646
const contexts: NodeContext[] = [];
4747
nodes = await createNodes(
4848
t.ctx.aztecNodeConfig,
49+
t.ctx.dateProvider,
4950
t.bootstrapNodeEnr,
5051
NUM_NODES,
5152
BOOT_NODE_UDP_PORT,
@@ -72,6 +73,7 @@ describe('e2e_p2p_rediscovery', () => {
7273

7374
const newNode = await createNode(
7475
t.ctx.aztecNodeConfig,
76+
t.ctx.dateProvider,
7577
i + 1 + BOOT_NODE_UDP_PORT,
7678
undefined,
7779
i,

yarn-project/end-to-end/src/e2e_p2p/reex.test.ts

+1
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ describe('e2e_p2p_reex', () => {
6565

6666
nodes = await createNodes(
6767
t.ctx.aztecNodeConfig,
68+
t.ctx.dateProvider,
6869
t.bootstrapNodeEnr,
6970
NUM_NODES,
7071
BOOT_NODE_UDP_PORT,

yarn-project/end-to-end/src/e2e_p2p/reqresp.test.ts

+1
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ describe('e2e_p2p_reqresp_tx', () => {
6565
t.logger.info('Creating nodes');
6666
nodes = await createNodes(
6767
t.ctx.aztecNodeConfig,
68+
t.ctx.dateProvider,
6869
t.bootstrapNodeEnr,
6970
NUM_NODES,
7071
BOOT_NODE_UDP_PORT,

yarn-project/end-to-end/src/e2e_p2p/upgrade_governance_proposer.test.ts

+1
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ describe('e2e_p2p_governance_proposer', () => {
130130
t.logger.info('Creating nodes');
131131
nodes = await createNodes(
132132
{ ...t.ctx.aztecNodeConfig, governanceProposerPayload: newPayloadAddress },
133+
t.ctx.dateProvider,
133134
t.bootstrapNodeEnr,
134135
NUM_NODES,
135136
BOOT_NODE_UDP_PORT,

yarn-project/end-to-end/src/fixtures/setup_p2p_test.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import { type AztecNodeConfig, AztecNodeService } from '@aztec/aztec-node';
55
import { type SentTx, createLogger } from '@aztec/aztec.js';
66
import { type AztecAddress } from '@aztec/circuits.js';
7+
import { type DateProvider } from '@aztec/foundation/timer';
78
import { type PXEService } from '@aztec/pxe';
89

910
import getPort from 'get-port';
@@ -34,6 +35,7 @@ export function generatePrivateKeys(startIndex: number, numberOfKeys: number): `
3435

3536
export function createNodes(
3637
config: AztecNodeConfig,
38+
dateProvider: DateProvider,
3739
bootstrapNodeEnr: string,
3840
numNodes: number,
3941
bootNodePort: number,
@@ -46,7 +48,7 @@ export function createNodes(
4648
const port = bootNodePort + i + 1;
4749

4850
const dataDir = dataDirectory ? `${dataDirectory}-${i}` : undefined;
49-
const nodePromise = createNode(config, port, bootstrapNodeEnr, i, dataDir, metricsPort);
51+
const nodePromise = createNode(config, dateProvider, port, bootstrapNodeEnr, i, dataDir, metricsPort);
5052
nodePromises.push(nodePromise);
5153
}
5254
return Promise.all(nodePromises);
@@ -55,6 +57,7 @@ export function createNodes(
5557
// creates a P2P enabled instance of Aztec Node Service
5658
export async function createNode(
5759
config: AztecNodeConfig,
60+
dateProvider: DateProvider,
5861
tcpPort: number,
5962
bootstrapNode: string | undefined,
6063
accountIndex: number,
@@ -68,6 +71,7 @@ export async function createNode(
6871
return await AztecNodeService.createAndSync(validatorConfig, {
6972
telemetry: telemetryClient,
7073
logger: createLogger(`node:${tcpPort}`),
74+
dateProvider,
7175
});
7276
}
7377

yarn-project/end-to-end/src/fixtures/snapshot_manager.ts

+8-13
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,11 @@ import { startAnvil } from '@aztec/ethereum/test';
2020
import { asyncMap } from '@aztec/foundation/async-map';
2121
import { createLogger } from '@aztec/foundation/log';
2222
import { resolver, reviver } from '@aztec/foundation/serialize';
23+
import { TestDateProvider } from '@aztec/foundation/timer';
2324
import { type ProverNode } from '@aztec/prover-node';
2425
import { type PXEService, createPXEService, getPXEServiceConfig } from '@aztec/pxe';
2526
import { createAndStartTelemetryClient, getConfigEnvVars as getTelemetryConfig } from '@aztec/telemetry-client/start';
2627

27-
import { type InstalledClock, install } from '@sinonjs/fake-timers';
2828
import { type Anvil } from '@viem/anvil';
2929
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
3030
import { copySync, removeSync } from 'fs-extra/esm';
@@ -50,7 +50,7 @@ export type SubsystemsContext = {
5050
proverNode?: ProverNode;
5151
watcher: AnvilTestWatcher;
5252
cheatCodes: CheatCodes;
53-
timer: InstalledClock;
53+
dateProvider: TestDateProvider;
5454
};
5555

5656
type SnapshotEntry = {
@@ -250,7 +250,6 @@ async function teardown(context: SubsystemsContext | undefined) {
250250
await context.acvmConfig?.cleanup();
251251
await context.anvil.stop();
252252
await context.watcher.stop();
253-
context.timer?.uninstall();
254253
} catch (err) {
255254
getLogger().error('Error during teardown', err);
256255
}
@@ -272,9 +271,6 @@ async function setupFromFresh(
272271
): Promise<SubsystemsContext> {
273272
logger.verbose(`Initializing state...`);
274273

275-
// Use sinonjs fake timers
276-
const timer = install({ shouldAdvanceTime: true, advanceTimeDelta: 20, toFake: ['Date'] });
277-
278274
// Fetch the AztecNode config.
279275
// TODO: For some reason this is currently the union of a bunch of subsystems. That needs fixing.
280276
const aztecNodeConfig: AztecNodeConfig & SetupOptions = { ...getConfigEnvVars(), ...opts };
@@ -358,7 +354,8 @@ async function setupFromFresh(
358354
const telemetry = await getEndToEndTestTelemetryClient(opts.metricsPort);
359355

360356
logger.verbose('Creating and synching an aztec node...');
361-
const aztecNode = await AztecNodeService.createAndSync(aztecNodeConfig, { telemetry });
357+
const dateProvider = new TestDateProvider();
358+
const aztecNode = await AztecNodeService.createAndSync(aztecNodeConfig, { telemetry, dateProvider });
362359

363360
let proverNode: ProverNode | undefined = undefined;
364361
if (opts.startProverNode) {
@@ -392,7 +389,7 @@ async function setupFromFresh(
392389
proverNode,
393390
watcher,
394391
cheatCodes,
395-
timer,
392+
dateProvider,
396393
};
397394
}
398395

@@ -402,9 +399,6 @@ async function setupFromFresh(
402399
async function setupFromState(statePath: string, logger: Logger): Promise<SubsystemsContext> {
403400
logger.verbose(`Initializing with saved state at ${statePath}...`);
404401

405-
// TODO: make one function
406-
const timer = install({ shouldAdvanceTime: true, advanceTimeDelta: 20, toFake: ['Date'] });
407-
408402
// TODO: For some reason this is currently the union of a bunch of subsystems. That needs fixing.
409403
const aztecNodeConfig: AztecNodeConfig & SetupOptions = JSON.parse(
410404
readFileSync(`${statePath}/aztec_node_config.json`, 'utf-8'),
@@ -445,7 +439,8 @@ async function setupFromState(statePath: string, logger: Logger): Promise<Subsys
445439

446440
logger.verbose('Creating aztec node...');
447441
const telemetry = await createAndStartTelemetryClient(getTelemetryConfig());
448-
const aztecNode = await AztecNodeService.createAndSync(aztecNodeConfig, { telemetry });
442+
const dateProvider = new TestDateProvider();
443+
const aztecNode = await AztecNodeService.createAndSync(aztecNodeConfig, { telemetry, dateProvider });
449444

450445
let proverNode: ProverNode | undefined = undefined;
451446
if (aztecNodeConfig.startProverNode) {
@@ -477,7 +472,7 @@ async function setupFromState(statePath: string, logger: Logger): Promise<Subsys
477472
},
478473
watcher,
479474
cheatCodes,
480-
timer,
475+
dateProvider,
481476
};
482477
}
483478

yarn-project/end-to-end/src/fixtures/utils.ts

+8-2
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ import {
3838
} from '@aztec/ethereum';
3939
import { startAnvil } from '@aztec/ethereum/test';
4040
import { retryUntil } from '@aztec/foundation/retry';
41+
import { TestDateProvider } from '@aztec/foundation/timer';
4142
import { FeeJuiceContract } from '@aztec/noir-contracts.js/FeeJuice';
4243
import { getVKTreeRoot } from '@aztec/noir-protocol-circuits-types';
4344
import { ProtocolContractAddress, protocolContractTreeRoot } from '@aztec/protocol-contracts';
@@ -225,6 +226,7 @@ async function setupWithRemoteEnvironment(
225226
logger,
226227
cheatCodes,
227228
watcher: undefined,
229+
dateProvider: undefined,
228230
teardown,
229231
};
230232
}
@@ -277,8 +279,10 @@ export type EndToEndContext = {
277279
logger: Logger;
278280
/** The cheat codes. */
279281
cheatCodes: CheatCodes;
280-
/** The anvil test watcher (undefined if connected to remove environment) */
282+
/** The anvil test watcher (undefined if connected to remote environment) */
281283
watcher: AnvilTestWatcher | undefined;
284+
/** Allows tweaking current system time, used by the epoch cache only (undefined if connected to remote environment) */
285+
dateProvider: TestDateProvider | undefined;
282286
/** Function to stop the started services. */
283287
teardown: () => Promise<void>;
284288
};
@@ -415,7 +419,8 @@ export async function setup(
415419

416420
const telemetry = await telemetryPromise;
417421
const publisher = new TestL1Publisher(config, telemetry);
418-
const aztecNode = await AztecNodeService.createAndSync(config, { telemetry, publisher });
422+
const dateProvider = new TestDateProvider();
423+
const aztecNode = await AztecNodeService.createAndSync(config, { telemetry, publisher, dateProvider });
419424
const sequencer = aztecNode.getSequencer();
420425

421426
let proverNode: ProverNode | undefined = undefined;
@@ -466,6 +471,7 @@ export async function setup(
466471
cheatCodes,
467472
sequencer,
468473
watcher,
474+
dateProvider,
469475
teardown,
470476
};
471477
}

yarn-project/epoch-cache/src/epoch_cache.ts

+15-5
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
import { RollupContract, createEthereumChain } from '@aztec/ethereum';
88
import { EthAddress } from '@aztec/foundation/eth-address';
99
import { type Logger, createLogger } from '@aztec/foundation/log';
10+
import { DateProvider } from '@aztec/foundation/timer';
1011

1112
import { EventEmitter } from 'node:events';
1213
import { createPublicClient, encodeAbiParameters, http, keccak256 } from 'viem';
@@ -40,17 +41,22 @@ export class EpochCache extends EventEmitter<{ committeeChanged: [EthAddress[],
4041
initialValidators: EthAddress[] = [],
4142
initialSampleSeed: bigint = 0n,
4243
private readonly l1constants: L1RollupConstants = EmptyL1RollupConstants,
44+
private readonly dateProvider: DateProvider = new DateProvider(),
4345
) {
4446
super();
4547
this.committee = initialValidators;
4648
this.cachedSampleSeed = initialSampleSeed;
4749

4850
this.log.debug(`Initialized EpochCache with constants and validators`, { l1constants, initialValidators });
4951

50-
this.cachedEpoch = getEpochNumberAtTimestamp(BigInt(Math.floor(Date.now() / 1000)), this.l1constants);
52+
this.cachedEpoch = getEpochNumberAtTimestamp(this.nowInSeconds(), this.l1constants);
5153
}
5254

53-
static async create(rollupAddress: EthAddress, config?: EpochCacheConfig) {
55+
static async create(
56+
rollupAddress: EthAddress,
57+
config?: EpochCacheConfig,
58+
deps: { dateProvider?: DateProvider } = {},
59+
) {
5460
config = config ?? getEpochCacheConfigEnvVars();
5561

5662
const chain = createEthereumChain(config.l1RpcUrl, config.l1ChainId);
@@ -81,16 +87,20 @@ export class EpochCache extends EventEmitter<{ committeeChanged: [EthAddress[],
8187
initialValidators.map(v => EthAddress.fromString(v)),
8288
sampleSeed,
8389
l1RollupConstants,
90+
deps.dateProvider,
8491
);
8592
}
8693

94+
private nowInSeconds(): bigint {
95+
return BigInt(Math.floor(this.dateProvider.now() / 1000));
96+
}
97+
8798
getEpochAndSlotNow(): EpochAndSlot {
88-
const now = BigInt(Math.floor(Date.now() / 1000));
89-
return this.getEpochAndSlotAtTimestamp(now);
99+
return this.getEpochAndSlotAtTimestamp(this.nowInSeconds());
90100
}
91101

92102
getEpochAndSlotInNextSlot(): EpochAndSlot {
93-
const nextSlotTs = BigInt(Math.floor(Date.now() / 1000) + this.l1constants.slotDuration);
103+
const nextSlotTs = this.nowInSeconds() + BigInt(this.l1constants.slotDuration);
94104
return this.getEpochAndSlotAtTimestamp(nextSlotTs);
95105
}
96106

0 commit comments

Comments
 (0)