Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/repair-subscription-access-key-index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'mppx': patch
---

Fixed missing subscription access-key address indexes when reusing existing keys.
30 changes: 30 additions & 0 deletions src/tempo/subscription/Store.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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())

Expand Down
20 changes: 12 additions & 8 deletions src/tempo/subscription/Store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,16 @@ export function fromStore(
return (await store.get(recordKey(subscriptionId))) as SubscriptionRecord | null
}

async function ensureAccessKeyAddressIndex(
record: SubscriptionAccessKeyRecord,
): Promise<SubscriptionAccessKeyRecord> {
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
Expand Down Expand Up @@ -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)
Expand All @@ -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) {
Expand Down
Loading