From b5d22bf41378346f311802dd9f134ebb1f245265 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Wed, 8 Jul 2026 16:45:35 -0400 Subject: [PATCH 1/2] test(pxe): share schema-mismatch wipe helper across db-compatibility tests --- .../pxe_db_compatibility.test.ts | 124 ++++++++++-------- 1 file changed, 67 insertions(+), 57 deletions(-) diff --git a/yarn-project/pxe/src/storage/backwards_compatibility_tests/pxe_db_compatibility.test.ts b/yarn-project/pxe/src/storage/backwards_compatibility_tests/pxe_db_compatibility.test.ts index 55db5435c26c..657761dde507 100644 --- a/yarn-project/pxe/src/storage/backwards_compatibility_tests/pxe_db_compatibility.test.ts +++ b/yarn-project/pxe/src/storage/backwards_compatibility_tests/pxe_db_compatibility.test.ts @@ -1,6 +1,7 @@ import { Fr } from '@aztec/foundation/curves/bn254'; import { EthAddress } from '@aztec/foundation/eth-address'; import { KeyStore } from '@aztec/key-store'; +import type { AztecAsyncKVStore } from '@aztec/kv-store'; import { createStore, openTmpStore } from '@aztec/kv-store/lmdb-v2'; import { AztecAddress } from '@aztec/stdlib/aztec-address'; import { GENESIS_BLOCK_HEADER_HASH } from '@aztec/stdlib/block'; @@ -144,6 +145,51 @@ async function collectOpenedStores() { } } +/** + * Drives the schema-mismatch wipe path shared by every "wipes rows written under schema vN" test: seeds rows into a + * `pxe_data` store opened at `oldSchemaVersion`, then reopens the same on-disk directory at the current + * `PXE_DATA_SCHEMA_VERSION`. The version mismatch makes DatabaseVersionManager wipe the database on open, so + * `assertWiped` runs against a freshly-cleared store and can prove the legacy rows are gone. Owns the throwaway + * data-directory lifecycle and the open/close ceremony so each test only spells out its own legacy write and + * post-wipe assertion. + * + * @param oldSchemaVersion - schema version the legacy rows are written under (a `PRE_..._PXE_SCHEMA_VERSION`) + * @param tmpDirPrefix - prefix for the throwaway data directory, e.g. `'pxe-schema-reset-'` + * @param seedLegacyRows - writes rows into the store opened at `oldSchemaVersion` + * @param assertWiped - asserts against the store reopened at the current schema version + */ +async function expectStoreWipedOnUpgradeFrom( + oldSchemaVersion: number, + tmpDirPrefix: string, + seedLegacyRows: (oldStore: AztecAsyncKVStore) => Promise, + assertWiped: (currentStore: AztecAsyncKVStore) => Promise, +) { + const dataDirectory = await mkdtemp(join(tmpdir(), tmpDirPrefix)); + const config = { + dataDirectory, + dataStoreMapSizeKb: 1024, + rollupAddress: EthAddress.ZERO, + }; + + try { + const oldStore = await createStore('pxe_data', oldSchemaVersion, config); + try { + await seedLegacyRows(oldStore); + } finally { + await oldStore.close(); + } + + const currentStore = await createStore('pxe_data', PXE_DATA_SCHEMA_VERSION, config); + try { + await assertWiped(currentStore); + } finally { + await currentStore.close(); + } + } finally { + await rm(dataDirectory, { recursive: true, force: true, maxRetries: 3 }); + } +} + /** * Backwards-compatibility test suite for PXE storage. The intent is to detect any change to the bytes PXE writes to * disk that would render existing on-device data unreadable after a version bump. Each schema test (in @@ -163,42 +209,23 @@ async function collectOpenedStores() { describe('PXE storage compatibility test suite', () => { it('wipes key-store rows written before the message-signing and fallback secret keys were removed', async () => { const account = AztecAddress.fromStringUnsafe('0x0b3683ee9df3ed6ed7027145bd6093f783b0bb4d8354501d906db7bb8cb58ea3'); - const dataDirectory = await mkdtemp(join(tmpdir(), 'pxe-schema-reset-')); - const config = { - dataDirectory, - dataStoreMapSizeKb: 1024, - rollupAddress: EthAddress.ZERO, - }; - - try { - const oldStore = await createStore( - 'pxe_data', - PRE_MESSAGE_AND_FALLBACK_SECRET_KEY_REMOVAL_PXE_SCHEMA_VERSION, - config, - ); - try { - await oldStore + await expectStoreWipedOnUpgradeFrom( + PRE_MESSAGE_AND_FALLBACK_SECRET_KEY_REMOVAL_PXE_SCHEMA_VERSION, + 'pxe-schema-reset-', + oldStore => + oldStore .openMap('key_store') .set( `${account.toString()}-ivsk_m`, Buffer.from('1fb01c42d1aaa2662041b899c77cb19e08192193acc5a94405f1b43c974eba7a', 'hex'), - ); - } finally { - await oldStore.close(); - } - - const currentStore = await createStore('pxe_data', PXE_DATA_SCHEMA_VERSION, config); - try { + ), + async currentStore => { const keyStore = new KeyStore(currentStore); - // Opening a below-current-version DB triggers DatabaseVersionManager to wipe it, so the account written under - // the old schema is gone and the new code never reads its now-incompatible rows. + // The wipe dropped the account written under the old schema, so the new code never reads its + // now-incompatible rows. await expect(keyStore.hasAccount(account)).resolves.toBe(false); - } finally { - await currentStore.close(); - } - } finally { - await rm(dataDirectory, { recursive: true, force: true, maxRetries: 3 }); - } + }, + ); }); it('wipes tagging-store rows written under the legacy two-part AppTaggingSecret key format', async () => { @@ -206,34 +233,17 @@ describe('PXE storage compatibility test suite', () => { // current toString() can only emit the three-part `::` form. These are the exact // secret/app values the RecipientTaggingStore schema fixture uses, i.e. a real pre-migration key. const legacyKey = `${new Fr(2n).toString()}:${AztecAddress.fromBigIntUnsafe(3n).toString()}`; - const dataDirectory = await mkdtemp(join(tmpdir(), 'pxe-schema-tagging-reset-')); - const config = { - dataDirectory, - dataStoreMapSizeKb: 1024, - rollupAddress: EthAddress.ZERO, - }; - - try { - const oldStore = await createStore('pxe_data', PRE_KIND_PREFIXED_TAGGING_KEY_PXE_SCHEMA_VERSION, config); - try { - await oldStore.openMap('highest_aged_index').set(legacyKey, 13); - } finally { - await oldStore.close(); - } - - const currentStore = await createStore('pxe_data', PXE_DATA_SCHEMA_VERSION, config); - try { - // Opening a mismatched-version DB triggers DatabaseVersionManager to wipe it, so the legacy-format key is - // discarded before the new parser (which rejects two-part keys) could ever enumerate it. Assert on the raw - // map: a high-level getter would miss the key regardless of the wipe, because the new three-part - // toString() never reconstructs the legacy key, so it would false-pass. + await expectStoreWipedOnUpgradeFrom( + PRE_KIND_PREFIXED_TAGGING_KEY_PXE_SCHEMA_VERSION, + 'pxe-schema-tagging-reset-', + oldStore => oldStore.openMap('highest_aged_index').set(legacyKey, 13), + async currentStore => { + // The wipe discarded the legacy-format key before the new parser (which rejects two-part keys) could ever + // enumerate it. Assert on the raw map: a high-level getter would miss the key regardless of the wipe, + // because the new three-part toString() never reconstructs the legacy key, so it would false-pass. await expect(currentStore.openMap('highest_aged_index').sizeAsync()).resolves.toBe(0); - } finally { - await currentStore.close(); - } - } finally { - await rm(dataDirectory, { recursive: true, force: true, maxRetries: 3 }); - } + }, + ); }); it('opens the expected set of stores', async () => { From 7223f8f2a4952e5b2a5477fa47f8f2e0aad8ca35 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Wed, 8 Jul 2026 17:36:35 -0400 Subject: [PATCH 2/2] test(pxe): drop redundant tempdir param and trim comments on wipe helper --- .../pxe_db_compatibility.test.ts | 29 +++++-------------- 1 file changed, 7 insertions(+), 22 deletions(-) diff --git a/yarn-project/pxe/src/storage/backwards_compatibility_tests/pxe_db_compatibility.test.ts b/yarn-project/pxe/src/storage/backwards_compatibility_tests/pxe_db_compatibility.test.ts index 657761dde507..e40e0209e4a1 100644 --- a/yarn-project/pxe/src/storage/backwards_compatibility_tests/pxe_db_compatibility.test.ts +++ b/yarn-project/pxe/src/storage/backwards_compatibility_tests/pxe_db_compatibility.test.ts @@ -146,25 +146,16 @@ async function collectOpenedStores() { } /** - * Drives the schema-mismatch wipe path shared by every "wipes rows written under schema vN" test: seeds rows into a - * `pxe_data` store opened at `oldSchemaVersion`, then reopens the same on-disk directory at the current + * Seeds rows into a `pxe_data` store opened at `oldSchemaVersion`, then reopens it at the current * `PXE_DATA_SCHEMA_VERSION`. The version mismatch makes DatabaseVersionManager wipe the database on open, so - * `assertWiped` runs against a freshly-cleared store and can prove the legacy rows are gone. Owns the throwaway - * data-directory lifecycle and the open/close ceremony so each test only spells out its own legacy write and - * post-wipe assertion. - * - * @param oldSchemaVersion - schema version the legacy rows are written under (a `PRE_..._PXE_SCHEMA_VERSION`) - * @param tmpDirPrefix - prefix for the throwaway data directory, e.g. `'pxe-schema-reset-'` - * @param seedLegacyRows - writes rows into the store opened at `oldSchemaVersion` - * @param assertWiped - asserts against the store reopened at the current schema version + * `assertWiped` runs against a cleared store and can prove the legacy rows are gone. */ async function expectStoreWipedOnUpgradeFrom( oldSchemaVersion: number, - tmpDirPrefix: string, seedLegacyRows: (oldStore: AztecAsyncKVStore) => Promise, assertWiped: (currentStore: AztecAsyncKVStore) => Promise, ) { - const dataDirectory = await mkdtemp(join(tmpdir(), tmpDirPrefix)); + const dataDirectory = await mkdtemp(join(tmpdir(), 'pxe-schema-wipe-')); const config = { dataDirectory, dataStoreMapSizeKb: 1024, @@ -211,7 +202,6 @@ describe('PXE storage compatibility test suite', () => { const account = AztecAddress.fromStringUnsafe('0x0b3683ee9df3ed6ed7027145bd6093f783b0bb4d8354501d906db7bb8cb58ea3'); await expectStoreWipedOnUpgradeFrom( PRE_MESSAGE_AND_FALLBACK_SECRET_KEY_REMOVAL_PXE_SCHEMA_VERSION, - 'pxe-schema-reset-', oldStore => oldStore .openMap('key_store') @@ -221,26 +211,21 @@ describe('PXE storage compatibility test suite', () => { ), async currentStore => { const keyStore = new KeyStore(currentStore); - // The wipe dropped the account written under the old schema, so the new code never reads its - // now-incompatible rows. await expect(keyStore.hasAccount(account)).resolves.toBe(false); }, ); }); it('wipes tagging-store rows written under the legacy two-part AppTaggingSecret key format', async () => { - // The legacy unconstrained toString() emitted a two-part `:` key. Build it by hand, since the - // current toString() can only emit the three-part `::` form. These are the exact - // secret/app values the RecipientTaggingStore schema fixture uses, i.e. a real pre-migration key. + // The current toString() only emits the three-part `::` form, so build the legacy two-part + // `:` key by hand. const legacyKey = `${new Fr(2n).toString()}:${AztecAddress.fromBigIntUnsafe(3n).toString()}`; await expectStoreWipedOnUpgradeFrom( PRE_KIND_PREFIXED_TAGGING_KEY_PXE_SCHEMA_VERSION, - 'pxe-schema-tagging-reset-', oldStore => oldStore.openMap('highest_aged_index').set(legacyKey, 13), async currentStore => { - // The wipe discarded the legacy-format key before the new parser (which rejects two-part keys) could ever - // enumerate it. Assert on the raw map: a high-level getter would miss the key regardless of the wipe, - // because the new three-part toString() never reconstructs the legacy key, so it would false-pass. + // Assert on the raw map, not a high-level getter: the new three-part toString() never reconstructs the + // legacy two-part key, so a getter would report it absent (false-pass) whether or not the wipe happened. await expect(currentStore.openMap('highest_aged_index').sizeAsync()).resolves.toBe(0); }, );