Skip to content

fix(wallets): make WalletDB.deleteAccount atomic - #25219

Open
vezenovm wants to merge 6 commits into
merge-train/fairiesfrom
mv/f-548-wallet-db-atomic-delete-account
Open

fix(wallets): make WalletDB.deleteAccount atomic#25219
vezenovm wants to merge 6 commits into
merge-train/fairiesfrom
mv/f-548-wallet-db-atomic-delete-account

Conversation

@vezenovm

@vezenovm vezenovm commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Follow-up to #25216, fixing the same defect in WalletDB.deleteAccount: four parallel account entry deletes plus a separate alias scan and delete, with no transaction. A failure partway through could leave any partial mix, e.g. the account entries gone with a stale alias left behind.

The whole body now runs inside store.transactionAsync(), so the deletes commit all-or-nothing.

The red/green test reuses the crashingStore helper from #25216, generalized to inject failures on delete as well as set. It fails the alias delete, which runs after the four account entry deletes, then asserts the account is fully intact. Pre-fix, the account entries were deleted while the stale alias survived (retrieveAccount threw "does not exist").

deleteAccount now carries a jsdoc note that deletion is local to this store and leaves PXE state unaffected.

Same defect as storeAccount (audit finding 12 vs 5): four parallel account
entry deletes plus a separate alias cleanup, with no transaction. A failure
partway through leaves the account entries gone but the alias behind, or any
other partial mix. Wrap the whole body in store.transactionAsync() so the
deletes commit all-or-nothing; the alias scan reads through the pending write
transaction so its semantics are unchanged.

Extends the crashingStore test helper to also inject failures on delete, and
pins rollback: a failed alias delete must leave the account fully intact.

Also documents that deleteAccount does not deregister the account from the
PXE, per the audit's recommendation.
@vezenovm vezenovm added the ci-draft Run CI on draft PRs. label Aug 13, 2026
@vezenovm vezenovm changed the title fix: make WalletDB.deleteAccount atomic fix(wallets): make WalletDB.deleteAccount atomic Aug 13, 2026
Base automatically changed from mv/f-548-wallet-db-atomic-store-account to merge-train/fairies August 13, 2026 18:00
Comment thread yarn-project/wallets/src/embedded/wallet_db.test.ts Outdated
@vezenovm
vezenovm marked this pull request as ready for review August 13, 2026 18:00
The PXE exposes no account removal API, so telling callers to deregister
separately prescribed an impossible action. State the true contract instead:
deletion is local to this store and PXE state is unaffected.
…:AztecProtocol/aztec-packages into mv/f-548-wallet-db-atomic-delete-account
@vezenovm vezenovm removed the ci-draft Run CI on draft PRs. label Aug 13, 2026

@mverzilli mverzilli left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved but note that test coverage only is assured for LMDB. I'd consider adding something to AztecKit's browser suite if we want to make sure this will behave well on browsers.

@nchamo nchamo left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just some comments

}

/**
* Deletes an account's stored data and its alias atomically. Deletion is local to this store;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not introduced here, but since the doc now promises we delete "its alias": #readAccountAliases is keyed by address, so if an account ever got a second alias we'd only delete one of them and the other would keep pointing at a deleted address. Is one-alias-per-account an invariant somewhere? If not, then maybe we could fix it or add a TODO? Just checking


it('deletes no account data when a write fails midway through deleteAccount', async () => {
const store = await openTmpStore('wallet-db-atomicity-test');
const db = new WalletDB(store, () => {});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tiny thing: this db shadows the one from the outer describe. Could we name it seedDb or similar? Otherwise if the const ever gets dropped we'd seed the beforeEach store instead and the failure would point somewhere confusing.

this.accounts.delete(accountKey('signingKey', address)),
]);
// Clean up alias if one exists
const aliasesByAddress = await this.#readAccountAliases();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Small thing: this scan is now inside the transaction, so we hold the write lock while iterating every alias to find one. Could we just resolve the alias before opening the transaction? Not sure how much it matters in practice

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants