From 311f49032555605a082871e2ce451f47c2c05058 Mon Sep 17 00:00:00 2001 From: EfeDurmaz16 Date: Mon, 20 Jul 2026 17:01:05 +0300 Subject: [PATCH] fix(subscription): repair access-key address index --- .../repair-subscription-access-key-index.md | 5 ++++ src/tempo/subscription/Store.test.ts | 30 +++++++++++++++++++ src/tempo/subscription/Store.ts | 20 ++++++++----- 3 files changed, 47 insertions(+), 8 deletions(-) create mode 100644 .changeset/repair-subscription-access-key-index.md diff --git a/.changeset/repair-subscription-access-key-index.md b/.changeset/repair-subscription-access-key-index.md new file mode 100644 index 00000000..ce6e259c --- /dev/null +++ b/.changeset/repair-subscription-access-key-index.md @@ -0,0 +1,5 @@ +--- +'mppx': patch +--- + +Fixed missing subscription access-key address indexes when reusing existing keys. diff --git a/src/tempo/subscription/Store.test.ts b/src/tempo/subscription/Store.test.ts index d7dd1e26..1a02b023 100644 --- a/src/tempo/subscription/Store.test.ts +++ b/src/tempo/subscription/Store.test.ts @@ -81,6 +81,36 @@ describe('tempo subscription store', () => { expect(await rawStore.get(`record:${subscriptionId}`)).toBeNull() }) + test('repairs a missing access-key address index for an existing record', async () => { + const rawStore = Store.memory() + const store = fromStore(rawStore) + const lookupKey = 'user-1:plan:pro' + const record = await store.getOrCreateAccessKey(lookupKey) + const addressIndexKey = `tempo:subscription:access-key:address:${record.accessKeyAddress}` + + await rawStore.delete(addressIndexKey) + expect(await store.getAccessKeyByAddress(record.accessKeyAddress)).toBeNull() + + expect(await store.getOrCreateAccessKey(lookupKey)).toEqual(record) + expect(await store.getAccessKeyByAddress(record.accessKeyAddress)).toEqual(record) + }) + + test('does not overwrite an existing access-key address index', async () => { + const rawStore = Store.memory() + const store = fromStore(rawStore) + const lookupKey = 'user-1:plan:pro' + const record = await store.getOrCreateAccessKey(lookupKey) + const indexedRecord = await store.getOrCreateAccessKey('user-2:plan:pro') + + await rawStore.put( + `tempo:subscription:access-key:address:${record.accessKeyAddress}`, + indexedRecord, + ) + + expect(await store.getOrCreateAccessKey(lookupKey)).toEqual(record) + expect(await store.getAccessKeyByAddress(record.accessKeyAddress)).toEqual(indexedRecord) + }) + test('rejects a replayed activation challenge', async () => { const store = fromStore(Store.memory()) diff --git a/src/tempo/subscription/Store.ts b/src/tempo/subscription/Store.ts index 8caba96e..d6172523 100644 --- a/src/tempo/subscription/Store.ts +++ b/src/tempo/subscription/Store.ts @@ -120,6 +120,16 @@ export function fromStore( return (await store.get(recordKey(subscriptionId))) as SubscriptionRecord | null } + async function ensureAccessKeyAddressIndex( + record: SubscriptionAccessKeyRecord, + ): Promise { + await store.update(accessKeyAddressKey(record.accessKeyAddress), (current) => { + if (current) return { op: 'noop', result: undefined } + return { op: 'set', value: record, result: undefined } + }) + return record + } + async function clearRenewalState(subscriptionId: string, periodIndex: number, attempt: string) { await store.update(recordKey(subscriptionId), (current) => { const subscription = current as SubscriptionRecord | null @@ -259,7 +269,7 @@ export function fromStore( async getOrCreateAccessKey(key) { const existing = (await store.get(accessKeyKey(key))) as SubscriptionAccessKeyRecord | null - if (existing) return existing + if (existing) return ensureAccessKeyAddressIndex(existing) const privateKey = Secp256k1.randomPrivateKey() const account = TempoAccount.fromSecp256k1(privateKey) @@ -278,13 +288,7 @@ export function fromStore( return { op: 'set', value: candidate, result: candidate } }, ) - .then(async (record) => { - await store.update(accessKeyAddressKey(record.accessKeyAddress), (current) => { - if (current) return { op: 'noop', result: undefined } - return { op: 'set', value: record, result: undefined } - }) - return record - }) + .then(ensureAccessKeyAddressIndex) }, async put(record) {