Skip to content

feat(store): cache account witnesses for tracked accounts - #2476

Open
sofiazcoaga wants to merge 5 commits into
0xMiden:nextfrom
sofiazcoaga:feat/cache-account-witnesses
Open

feat(store): cache account witnesses for tracked accounts#2476
sofiazcoaga wants to merge 5 commits into
0xMiden:nextfrom
sofiazcoaga:feat/cache-account-witnesses

Conversation

@sofiazcoaga

@sofiazcoaga sofiazcoaga commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Problem

Every FPI transaction fetches the foreign account's AccountWitness from the node. N transactions between two syncs cost N GetAccount requests, even against the same account at the same block.

Changes

  1. Client::track_account_witness registers an account whose account witness should be kept fresh. New account_witnesses table, one row per account.
  2. Every sync refreshes them at the new sync height, up to four requests in flight. Each witness is validated against the synced block header's account root before it is stored.
  3. The sync reuses the witnesses it already validated while fetching public account state, so a registered account that changed in that window costs one request, not two.
  4. Building a transaction, a registered foreign account resolves from the store instead of the network: a private one takes the cached witness, a public one the client tracks builds its whole AccountInputs locally. Both fall back to the existing request when there is nothing usable.

The cost moves from once per transaction to once per sync.

Closes #2443

@sofiazcoaga
sofiazcoaga marked this pull request as draft September 1, 2026 13:34
@sofiazcoaga

Copy link
Copy Markdown
Contributor Author

@igamigo the issue mentions "we should also keep account witnesses for watched accounts, which we already request anyway", but I deliberately did not add watched accounts to account witness fetching automatically. Watched accounts are only refetched during sync when they have been modified, so for an unmodified one there is no request to reuse. Automatically tracking their account witnesses would mean an RPC call for every watched account on every sync, so a client watching N accounts for observability would pay N extra requests per sync. I believe this should be a conscious decision. Let me know your thoughts on this.

@sofiazcoaga
sofiazcoaga marked this pull request as ready for review September 1, 2026 21:35
@sofiazcoaga
sofiazcoaga requested review from TomasArrachea, igamigo and juan518munoz and removed request for igamigo and juan518munoz September 3, 2026 13:46

@juan518munoz juan518munoz left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Looks good! Only left a comment about a possible optimization (I might have overlooked something that makes it senseless)

// Cache MMR so pruning can reuse in-memory MMR.
self.cache_partial_mmr(partial_mmr).await?;

self.refresh_account_witnesses().await;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I wonder if we can cache the witnesses inside state_sync.sync_state and drop
refresh_account_witnesses entirely.

Doing this it means we lose the guarantee that every registered account has a fresh witness after a sync, since only the changed accounts get their witnesses updated. But we can cover the rest lazily in fetch_public_account_inputs, which already falls back to get_account there and caches the returned AccountCode, it just discards the witness it fetched. We can stamp the witness at the block the proof came back at (the _block_num we currently drop), and any further tx against the same account at the same reference block hits the cache.

This way witness cache works under these conditions:

  • changed in the last sync: free hit from the overall sync.
  • unchanged, first tx at this tip: one request (which we already pay today) then cached.
  • unchanged, later txs at the same tip: cache because of above.

One caveat is that fetch_public_account_inputs does not validate the witness it receives
today, which is fine while it dies with the transaction, so persisting it would need a
validate_account_witness call first.

Another upside to this is that we could address the issue's "keep account witnesses for watched accounts" at no extra cost.

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.

Cache account witnesses

2 participants