From 5f2091154bad1df90e7876e5a78299e73170f829 Mon Sep 17 00:00:00 2001 From: PhilWindle Date: Sun, 15 Dec 2024 18:39:48 +0000 Subject: [PATCH 1/6] Cleanup after e2e tests --- .../src/fixtures/snapshot_manager.ts | 35 +++++++++++-- yarn-project/end-to-end/src/fixtures/utils.ts | 50 +++++++++++++++++-- yarn-project/kv-store/src/lmdb/store.ts | 11 ++-- 3 files changed, 84 insertions(+), 12 deletions(-) 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 5581397e60cd..37e49819e1b9 100644 --- a/yarn-project/end-to-end/src/fixtures/snapshot_manager.ts +++ b/yarn-project/end-to-end/src/fixtures/snapshot_manager.ts @@ -18,6 +18,7 @@ import { deployInstance, registerContractClass } from '@aztec/aztec.js/deploymen import { type DeployL1ContractsArgs, createL1Clients, getL1ContractsConfigEnvVars, l1Artifacts } from '@aztec/ethereum'; import { startAnvil } from '@aztec/ethereum/test'; import { asyncMap } from '@aztec/foundation/async-map'; +import { randomBytes } from '@aztec/foundation/crypto'; import { createLogger } from '@aztec/foundation/log'; import { resolver, reviver } from '@aztec/foundation/serialize'; import { TestDateProvider } from '@aztec/foundation/timer'; @@ -28,7 +29,9 @@ import { createAndStartTelemetryClient, getConfigEnvVars as getTelemetryConfig } import { type Anvil } from '@viem/anvil'; import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs'; import { copySync, removeSync } from 'fs-extra/esm'; -import { join } from 'path'; +import fs from 'fs/promises'; +import { tmpdir } from 'os'; +import path, { join } from 'path'; import { type Hex, getContract } from 'viem'; import { mnemonicToAccount } from 'viem/accounts'; @@ -51,6 +54,7 @@ export type SubsystemsContext = { watcher: AnvilTestWatcher; cheatCodes: CheatCodes; dateProvider: TestDateProvider; + directoryToCleanup?: string; }; type SnapshotEntry = { @@ -248,8 +252,12 @@ async function teardown(context: SubsystemsContext | undefined) { await context.proverNode?.stop(); await context.aztecNode.stop(); await context.acvmConfig?.cleanup(); + await context.bbConfig?.cleanup(); await context.anvil.stop(); await context.watcher.stop(); + if (context.directoryToCleanup) { + await fs.rm(context.directoryToCleanup, { recursive: true, force: true }); + } } catch (err) { getLogger().error('Error during teardown', err); } @@ -274,7 +282,15 @@ async function setupFromFresh( // Fetch the AztecNode config. // TODO: For some reason this is currently the union of a bunch of subsystems. That needs fixing. const aztecNodeConfig: AztecNodeConfig & SetupOptions = { ...getConfigEnvVars(), ...opts }; - aztecNodeConfig.dataDirectory = statePath; + + // Create a temp directory for all ephemeral state and cleanup afterwards + const directoryToCleanup = path.join(tmpdir(), randomBytes(8).toString('hex')); + await fs.mkdir(directoryToCleanup, { recursive: true }); + if (statePath === undefined) { + aztecNodeConfig.dataDirectory = directoryToCleanup; + } else { + aztecNodeConfig.dataDirectory = statePath; + } // Start anvil. We go via a wrapper script to ensure if the parent dies, anvil dies. logger.verbose('Starting anvil...'); @@ -364,12 +380,13 @@ async function setupFromFresh( `0x${proverNodePrivateKey!.toString('hex')}`, aztecNodeConfig, aztecNode, + path.join(directoryToCleanup, randomBytes(8).toString('hex')), ); } logger.verbose('Creating pxe...'); const pxeConfig = getPXEServiceConfig(); - pxeConfig.dataDirectory = statePath; + pxeConfig.dataDirectory = statePath ?? path.join(directoryToCleanup, randomBytes(8).toString('hex')); const pxe = await createPXEService(aztecNode, pxeConfig); const cheatCodes = await CheatCodes.create(aztecNodeConfig.l1RpcUrl, pxe); @@ -390,6 +407,7 @@ async function setupFromFresh( watcher, cheatCodes, dateProvider, + directoryToCleanup, }; } @@ -399,6 +417,9 @@ async function setupFromFresh( async function setupFromState(statePath: string, logger: Logger): Promise { logger.verbose(`Initializing with saved state at ${statePath}...`); + const directoryToCleanup = path.join(tmpdir(), randomBytes(8).toString('hex')); + await fs.mkdir(directoryToCleanup, { recursive: true }); + // TODO: For some reason this is currently the union of a bunch of subsystems. That needs fixing. const aztecNodeConfig: AztecNodeConfig & SetupOptions = JSON.parse( readFileSync(`${statePath}/aztec_node_config.json`, 'utf-8'), @@ -447,7 +468,12 @@ async function setupFromState(statePath: string, logger: Logger): Promise Promise; }> { const pxeServiceConfig = { ...getPXEServiceConfig(), ...opts }; + + // If no data directory provided, create a temp directory and clean up afterwards + const configuredDataDirectory = pxeServiceConfig.dataDirectory; + if (!configuredDataDirectory) { + pxeServiceConfig.dataDirectory = path.join(tmpdir(), randomBytes(8).toString('hex')); + } + const pxe = await createPXEService(aztecNode, pxeServiceConfig, useLogSuffix, proofCreator); - const teardown = async () => {}; + const teardown = async () => { + if (!configuredDataDirectory) { + await fs.rm(pxeServiceConfig.dataDirectory!, { recursive: true, force: true }); + } + }; return { pxe, @@ -255,6 +269,8 @@ export type SetupOptions = { startProverNode?: boolean; /** Whether to fund the rewardDistributor */ fundRewardDistributor?: boolean; + /** Data directory for the components under test */ + dataDirectory?: string; } & Partial; /** Context for an end-to-end test as returned by the `setup` function */ @@ -304,6 +320,13 @@ export async function setup( const config = { ...getConfigEnvVars(), ...opts }; const logger = getLogger(); + // Create a temp directory for any services that need it and cleanup later + const directoryToCleanup = path.join(tmpdir(), randomBytes(8).toString('hex')); + await fs.mkdir(directoryToCleanup, { recursive: true }); + if (!config.dataDirectory) { + config.dataDirectory = directoryToCleanup; + } + let anvil: Anvil | undefined; if (!config.l1RpcUrl) { @@ -428,11 +451,16 @@ export async function setup( logger.verbose('Creating and syncing a simulated prover node...'); const proverNodePrivateKey = getPrivateKeyFromIndex(2); const proverNodePrivateKeyHex: Hex = `0x${proverNodePrivateKey!.toString('hex')}`; - proverNode = await createAndSyncProverNode(proverNodePrivateKeyHex, config, aztecNode); + proverNode = await createAndSyncProverNode( + proverNodePrivateKeyHex, + config, + aztecNode, + path.join(directoryToCleanup, randomBytes(8).toString('hex')), + ); } logger.verbose('Creating a pxe...'); - const { pxe } = await setupPXEService(aztecNode!, pxeOpts, logger); + const { pxe, teardown: pxeTeardown } = await setupPXEService(aztecNode!, pxeOpts, logger); if (!config.skipProtocolContracts) { logger.verbose('Setting up Fee Juice...'); @@ -445,6 +473,8 @@ export async function setup( const cheatCodes = await CheatCodes.create(config.l1RpcUrl, pxe!); const teardown = async () => { + await pxeTeardown(); + if (aztecNode instanceof AztecNodeService) { await aztecNode?.stop(); } @@ -455,8 +485,19 @@ export async function setup( await acvmConfig.cleanup(); } + if (bbConfig?.cleanup) { + // remove the temp directory created for the acvm + logger.verbose(`Cleaning up BB state`); + await bbConfig.cleanup(); + } + await anvil?.stop(); await watcher.stop(); + + if (directoryToCleanup) { + logger.verbose(`Cleaning up data directory at ${directoryToCleanup}`); + await fs.rm(directoryToCleanup, { recursive: true, force: true }); + } }; return { @@ -660,6 +701,7 @@ export async function createAndSyncProverNode( proverNodePrivateKey: `0x${string}`, aztecNodeConfig: AztecNodeConfig, aztecNode: AztecNode, + dataDirectory: string, ) { // Disable stopping the aztec node as the prover coordination test will kill it otherwise // This is only required when stopping the prover node for testing @@ -670,7 +712,7 @@ export async function createAndSyncProverNode( }; // Creating temp store and archiver for simulated prover node - const archiverConfig = { ...aztecNodeConfig, dataDirectory: undefined }; + const archiverConfig = { ...aztecNodeConfig, dataDirectory }; const archiver = await createArchiver(archiverConfig, new NoopTelemetryClient(), { blockUntilSync: true }); // Prover node config is for simulated proofs diff --git a/yarn-project/kv-store/src/lmdb/store.ts b/yarn-project/kv-store/src/lmdb/store.ts index ad66ec766919..70797b9668ad 100644 --- a/yarn-project/kv-store/src/lmdb/store.ts +++ b/yarn-project/kv-store/src/lmdb/store.ts @@ -1,3 +1,4 @@ +import { randomBytes } from '@aztec/foundation/crypto'; import { createLogger } from '@aztec/foundation/log'; import { promises as fs, mkdirSync } from 'fs'; @@ -64,13 +65,15 @@ export class AztecLmdbStore implements AztecKVStore, AztecAsyncKVStore { ephemeral: boolean = false, log = createLogger('kv-store:lmdb'), ): AztecLmdbStore { - if (path) { - mkdirSync(path, { recursive: true }); + if (!path) { + console.log(`UNDEFINED`); } + const dbPath = path ?? join(tmpdir(), randomBytes(8).toString('hex')); + mkdirSync(dbPath, { recursive: true }); const mapSize = 1024 * mapSizeKb; log.debug(`Opening LMDB database at ${path || 'temporary location'} with map size ${mapSize}`); - const rootDb = open({ path, noSync: ephemeral, mapSize }); - return new AztecLmdbStore(rootDb, ephemeral, path); + const rootDb = open({ path: dbPath, noSync: ephemeral, mapSize }); + return new AztecLmdbStore(rootDb, ephemeral, dbPath); } /** From 2e30e1e1b834928c5d6e3b87b225ad80ffcf04b9 Mon Sep 17 00:00:00 2001 From: PhilWindle Date: Sun, 15 Dec 2024 19:52:29 +0000 Subject: [PATCH 2/6] Cleanup --- yarn-project/kv-store/src/lmdb/store.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/yarn-project/kv-store/src/lmdb/store.ts b/yarn-project/kv-store/src/lmdb/store.ts index 70797b9668ad..9eb32e58d695 100644 --- a/yarn-project/kv-store/src/lmdb/store.ts +++ b/yarn-project/kv-store/src/lmdb/store.ts @@ -65,9 +65,6 @@ export class AztecLmdbStore implements AztecKVStore, AztecAsyncKVStore { ephemeral: boolean = false, log = createLogger('kv-store:lmdb'), ): AztecLmdbStore { - if (!path) { - console.log(`UNDEFINED`); - } const dbPath = path ?? join(tmpdir(), randomBytes(8).toString('hex')); mkdirSync(dbPath, { recursive: true }); const mapSize = 1024 * mapSizeKb; From 0139c42e9065714b0e32124fefbcc53fb2fefea2 Mon Sep 17 00:00:00 2001 From: PhilWindle Date: Sun, 15 Dec 2024 19:59:47 +0000 Subject: [PATCH 3/6] Cleanup --- yarn-project/end-to-end/src/fixtures/utils.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/yarn-project/end-to-end/src/fixtures/utils.ts b/yarn-project/end-to-end/src/fixtures/utils.ts index 407e86745fa0..d7f3f29b46d4 100644 --- a/yarn-project/end-to-end/src/fixtures/utils.ts +++ b/yarn-project/end-to-end/src/fixtures/utils.ts @@ -269,8 +269,6 @@ export type SetupOptions = { startProverNode?: boolean; /** Whether to fund the rewardDistributor */ fundRewardDistributor?: boolean; - /** Data directory for the components under test */ - dataDirectory?: string; } & Partial; /** Context for an end-to-end test as returned by the `setup` function */ From e9c5aed87695650a5450c589eafeffc9045e3d49 Mon Sep 17 00:00:00 2001 From: PhilWindle Date: Mon, 16 Dec 2024 10:11:12 +0000 Subject: [PATCH 4/6] Yran test passes and cleans up --- .../kv-store/src/interfaces/array_test_suite.ts | 4 ++++ .../kv-store/src/interfaces/map_test_suite.ts | 4 ++++ .../kv-store/src/interfaces/set_test_suite.ts | 4 ++++ .../src/interfaces/singleton_test_suite.ts | 4 ++++ .../kv-store/src/interfaces/store_test_suite.ts | 4 ++++ yarn-project/kv-store/src/lmdb/counter.test.ts | 16 ++++++++++++++-- yarn-project/kv-store/src/lmdb/store.ts | 7 +++---- .../kv-store/src/stores/l2_tips_store.test.ts | 4 ++++ 8 files changed, 41 insertions(+), 6 deletions(-) diff --git a/yarn-project/kv-store/src/interfaces/array_test_suite.ts b/yarn-project/kv-store/src/interfaces/array_test_suite.ts index 0affe23305e8..552e7201cf17 100644 --- a/yarn-project/kv-store/src/interfaces/array_test_suite.ts +++ b/yarn-project/kv-store/src/interfaces/array_test_suite.ts @@ -20,6 +20,10 @@ export function describeAztecArray( arr = store.openArray('test'); }); + afterEach(async () => { + await store.delete(); + }); + async function length(sut: AztecAsyncArray | AztecArray = arr) { return isSyncStore(store) && !forceAsync ? (sut as AztecArray).length diff --git a/yarn-project/kv-store/src/interfaces/map_test_suite.ts b/yarn-project/kv-store/src/interfaces/map_test_suite.ts index 3c999f01b2c0..7736315ec6f7 100644 --- a/yarn-project/kv-store/src/interfaces/map_test_suite.ts +++ b/yarn-project/kv-store/src/interfaces/map_test_suite.ts @@ -21,6 +21,10 @@ export function describeAztecMap( map = store.openMultiMap('test'); }); + afterEach(async () => { + await store.delete(); + }); + async function get(key: Key, sut: AztecAsyncMap | AztecMap = map) { return isSyncStore(store) && !forceAsync ? (sut as AztecMultiMap).get(key) diff --git a/yarn-project/kv-store/src/interfaces/set_test_suite.ts b/yarn-project/kv-store/src/interfaces/set_test_suite.ts index 08f2758ebf3c..ad2de503f313 100644 --- a/yarn-project/kv-store/src/interfaces/set_test_suite.ts +++ b/yarn-project/kv-store/src/interfaces/set_test_suite.ts @@ -21,6 +21,10 @@ export function describeAztecSet( set = store.openSet('test'); }); + afterEach(async () => { + await store.delete(); + }); + async function has(key: string) { return isSyncStore(store) && !forceAsync ? (set as AztecSet).has(key) diff --git a/yarn-project/kv-store/src/interfaces/singleton_test_suite.ts b/yarn-project/kv-store/src/interfaces/singleton_test_suite.ts index b3ad148f8d00..6ecaa24522dd 100644 --- a/yarn-project/kv-store/src/interfaces/singleton_test_suite.ts +++ b/yarn-project/kv-store/src/interfaces/singleton_test_suite.ts @@ -18,6 +18,10 @@ export function describeAztecSingleton( singleton = store.openSingleton('test'); }); + afterEach(async () => { + await store.delete(); + }); + async function get() { return isSyncStore(store) && !forceAsync ? (singleton as AztecSingleton).get() diff --git a/yarn-project/kv-store/src/interfaces/store_test_suite.ts b/yarn-project/kv-store/src/interfaces/store_test_suite.ts index 91c2240ecc30..051504889f00 100644 --- a/yarn-project/kv-store/src/interfaces/store_test_suite.ts +++ b/yarn-project/kv-store/src/interfaces/store_test_suite.ts @@ -32,21 +32,25 @@ export function describeAztecStore( expect(await get(forkedStore, forkedSingleton)).to.equal('bar'); await forkedSingleton.delete(); expect(await get(store, singleton)).to.equal('foo'); + await forkedStore.delete(); }; it('forks a persistent store', async () => { const store = await getPersistentStore(); await itForks(store); + await store.delete(); }); it('forks a persistent store with no path', async () => { const store = await getPersistentNoPathStore(); await itForks(store); + await store.delete(); }); it('forks an ephemeral store', async () => { const store = await getEphemeralStore(); await itForks(store); + await store.delete(); }); }); } diff --git a/yarn-project/kv-store/src/lmdb/counter.test.ts b/yarn-project/kv-store/src/lmdb/counter.test.ts index 7e1c8524cf96..12b748fb358a 100644 --- a/yarn-project/kv-store/src/lmdb/counter.test.ts +++ b/yarn-project/kv-store/src/lmdb/counter.test.ts @@ -3,8 +3,11 @@ import { toArray } from '@aztec/foundation/iterable'; import { expect, use } from 'chai'; import chaiAsPromised from 'chai-as-promised'; +import fs from 'fs/promises'; import { type Database, open } from 'lmdb'; import forEach from 'mocha-each'; +import { tmpdir } from 'os'; +import path from 'path'; import { LmdbAztecCounter } from './counter.js'; @@ -12,9 +15,18 @@ use(chaiAsPromised); describe('LmdbAztecCounter', () => { let db: Database; + let dir: string; - beforeEach(() => { - db = open({} as any); + beforeEach(async () => { + dir = path.join(tmpdir(), randomBytes(8).toString('hex')); + await fs.mkdir(dir, { recursive: true }); + db = open({ path: dir } as any); + }); + + afterEach(async () => { + await db.drop(); + await db.close(); + await fs.rm(dir, { recursive: true, force: true }); }); forEach([ diff --git a/yarn-project/kv-store/src/lmdb/store.ts b/yarn-project/kv-store/src/lmdb/store.ts index 9eb32e58d695..4415d0a04030 100644 --- a/yarn-project/kv-store/src/lmdb/store.ts +++ b/yarn-project/kv-store/src/lmdb/store.ts @@ -30,7 +30,7 @@ export class AztecLmdbStore implements AztecKVStore, AztecAsyncKVStore { #multiMapData: Database; #log = createLogger('kv-store:lmdb'); - constructor(rootDb: RootDatabase, public readonly isEphemeral: boolean, private path?: string) { + constructor(rootDb: RootDatabase, public readonly isEphemeral: boolean, private path: string) { this.#rootDb = rootDb; // big bucket to store all the data @@ -78,10 +78,9 @@ export class AztecLmdbStore implements AztecKVStore, AztecAsyncKVStore { * @returns A new AztecLmdbStore. */ async fork() { - const baseDir = this.path ? dirname(this.path) : tmpdir(); + const baseDir = this.path; this.#log.debug(`Forking store with basedir ${baseDir}`); - const forkPath = - (await fs.mkdtemp(join(baseDir, 'aztec-store-fork-'))) + (this.isEphemeral || !this.path ? '/data.mdb' : ''); + const forkPath = await fs.mkdtemp(join(baseDir, 'aztec-store-fork-')); this.#log.verbose(`Forking store to ${forkPath}`); await this.#rootDb.backup(forkPath, false); const forkDb = open(forkPath, { noSync: this.isEphemeral }); diff --git a/yarn-project/kv-store/src/stores/l2_tips_store.test.ts b/yarn-project/kv-store/src/stores/l2_tips_store.test.ts index d9ec9845fc19..32010b339857 100644 --- a/yarn-project/kv-store/src/stores/l2_tips_store.test.ts +++ b/yarn-project/kv-store/src/stores/l2_tips_store.test.ts @@ -17,6 +17,10 @@ describe('L2TipsStore', () => { tipsStore = new L2TipsStore(kvStore, 'test'); }); + afterEach(async () => { + await kvStore.delete(); + }); + const makeBlock = (number: number): L2Block => ({ number, header: { hash: () => new Fr(number) } as BlockHeader } as L2Block); From 4180fbe7795a074fa14dd2125d2d3fe4cf1fd89c Mon Sep 17 00:00:00 2001 From: PhilWindle Date: Mon, 16 Dec 2024 12:06:49 +0000 Subject: [PATCH 5/6] Formatting --- yarn-project/kv-store/src/lmdb/store.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yarn-project/kv-store/src/lmdb/store.ts b/yarn-project/kv-store/src/lmdb/store.ts index 4415d0a04030..f0f453a98ad6 100644 --- a/yarn-project/kv-store/src/lmdb/store.ts +++ b/yarn-project/kv-store/src/lmdb/store.ts @@ -4,7 +4,7 @@ import { createLogger } from '@aztec/foundation/log'; import { promises as fs, mkdirSync } from 'fs'; import { type Database, type RootDatabase, open } from 'lmdb'; import { tmpdir } from 'os'; -import { dirname, join } from 'path'; +import { join } from 'path'; import { type AztecArray, type AztecAsyncArray } from '../interfaces/array.js'; import { type Key } from '../interfaces/common.js'; From 1d62d2a6e1e78a42ab20b6a372867f1543e0b9b5 Mon Sep 17 00:00:00 2001 From: thunkar Date: Tue, 17 Dec 2024 09:04:48 +0000 Subject: [PATCH 6/6] properly close and delete indexeddb --- yarn-project/kv-store/src/indexeddb/store.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/yarn-project/kv-store/src/indexeddb/store.ts b/yarn-project/kv-store/src/indexeddb/store.ts index ad841c0a9ab6..fe72cdf06621 100644 --- a/yarn-project/kv-store/src/indexeddb/store.ts +++ b/yarn-project/kv-store/src/indexeddb/store.ts @@ -1,6 +1,6 @@ import { type Logger } from '@aztec/foundation/log'; -import { type DBSchema, type IDBPDatabase, openDB } from 'idb'; +import { type DBSchema, type IDBPDatabase, deleteDB, openDB } from 'idb'; import { type AztecAsyncArray } from '../interfaces/array.js'; import { type Key } from '../interfaces/common.js'; @@ -183,7 +183,8 @@ export class AztecIndexedDBStore implements AztecAsyncKVStore { /** Deletes this store and removes the database */ delete() { this.#containers.clear(); - return Promise.resolve(this.#rootDB.deleteObjectStore('data')); + this.#rootDB.close(); + return deleteDB(this.#name); } estimateSize(): { mappingSize: number; actualSize: number; numItems: number } {