Skip to content

feat: cache PublicImmutable reads in the fact store - #25568

Merged
nventuro merged 2 commits into
nico/f-909-make-fact-store-support-global-factsfrom
nico/f-908-implement-a-block-hash-based-cache-for-pub-immut-with-a
Oct 2, 2026
Merged

nventuro merged 2 commits into
nico/f-909-make-fact-store-support-global-factsfrom
nico/f-908-implement-a-block-hash-based-cache-for-pub-immut-with-a

Conversation

@nventuro

@nventuro nventuro commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

This adds a fact-store based cache to PublicImmutable so that we don't need to do a node roundtrip every time we access the state variable. The fact-store handles reorgs automatically.

There's some sublte details in when and how the init nullifiers are checked depending on anchor blocks, but those are best explained by the code.

@nventuro
nventuro added this pull request to stack #25569 October 1, 2026 19:12
@nventuro
nventuro requested a review from nchamo October 1, 2026 19:13

@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.

🔥

let cached_len = payload.len();
let packed_len = <T as Packable>::N;
assert(
cached_len == packed_len,

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.

I'm not very familiar with upgrades, but would it be possible for the contract to be upgraded and the new version to have a PublicImmutable with a different type at this slot? If so, should we treat the mismatch as a cache miss and replace the stale fact instead of panicking?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This is just overly defensive code to avoid any weird errors - I don't expect these panics from ever happening. I considered the cache clear that you allude, but it should be impossible for the current code to produce such a scenario. We may want to fail loudly in that case - the contract woudl be bricked, yes, but a wallet would be able to sort it out by clearning the fact manually.

Not ideal if it happens, but if we really think it's impossible then maybe better not to hide it at this early stage where we have such a close relationship with the ecosystem.

Private and utility reads of a PublicImmutable now record the value as a
retractable fact at the anchor block, in a public-scope collection per storage
slot, and later reads return it without a node roundtrip. A value cached at a
block after the anchor is only used once the initialization nullifier is found
at the anchor.

WithHash exposes the hint check on its own as
verify_historical_public_storage_hint, so the private read can pass the cached
value as the hint.
Utility and private reads of a cached value are now tested to not call the
public storage oracle. The test for reading at an anchor block earlier than the
one the value was cached at is renamed to say so.
@nventuro
nventuro force-pushed the nico/f-908-implement-a-block-hash-based-cache-for-pub-immut-with-a branch from 6345e3a to e27da24 Compare October 2, 2026 13:53
@nventuro
nventuro merged commit 30f2da7 into merge-train/fairies-v5 Oct 2, 2026
12 checks passed
@nventuro
nventuro deleted the nico/f-908-implement-a-block-hash-based-cache-for-pub-immut-with-a branch October 2, 2026 17:16
nventuro added a commit to aztec-labs-eng/aztec-node that referenced this pull request Oct 8, 2026
* feat(pxe): add simulateAndProveTx to simulate and prove in a single operation (AztecProtocol/aztec-packages#25534)

Adds `PXE.simulateAndProveTx`, which simulates a transaction, hands the
result to a `prepareProve` callback that returns the request to prove,
and then proves it, all as a single PXE operation. No private state sync
runs in between, so the simulation and the witness generation see the
same notes and tagging indices.

`simulateTx` and `proveTx` now share their bodies with the new method
through private helpers. There are no tests yet: nothing runs
transactions from PXE directly, and doing so needs wallet support whose
shape is not settled.

* feat!: support public fact scopes (AztecProtocol/aztec-packages#25563)

Fact collections used to always belong to an account, because facts
were only accessed during private state sync, where the account being
synced is known. Collections now belong to a `FactScope`, which is
either an account or the new public scope. A collection in the public
scope can be accessed whichever accounts are in scope, but still only by
the contract that created it.

The `aztec::facts` functions and the four fact oracles take a
`FactScope` instead of an `AztecAddress`. Public-scope collections use
the same database layout as account ones, so no schema migration is
needed.

Unlike upstream, the fact oracles change in place instead of gaining
`V2` names with legacy remappings, so the oracle interface major version
moves 32 -> 33.

* feat: cache PublicImmutable reads in the fact store (AztecProtocol/aztec-packages#25568)

Private and utility reads of a `PublicImmutable` now cache the value in
the fact store, in the public scope, so that later reads need no node
roundtrip. The fact store retracts the cached value if the block it was
read at is reorged out.

A value cached from a block after the current anchor is only used once
the state variable is confirmed to be initialized at the anchor.

* chore: regenerate the standard contracts pinned build for oracle version 33

The pinned standard contracts were compiled against Aztec.nr at oracle
interface version 32, which PXE now rejects, so the pinned build,
canonical addresses, and class ids are regenerated against the new
oracle interface. All four contracts move to new addresses and class
ids.

* docs: drop the standard contract re-pin from the unreleased migration notes

The re-pinned standard contracts have not been deployed, so nobody has
seen the intermediate addresses and there is nothing to migrate from.
Remove the re-pin note and the standard contract addresses from the v3
contract address domain separator note.
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.

2 participants