Skip to content

refactor!: ephemeral arrays#22162

Merged
mverzilli merged 29 commits intomerge-train/fairiesfrom
martin/volatile-arrays
Apr 9, 2026
Merged

refactor!: ephemeral arrays#22162
mverzilli merged 29 commits intomerge-train/fairiesfrom
martin/volatile-arrays

Conversation

@mverzilli
Copy link
Copy Markdown
Contributor

@mverzilli mverzilli commented Mar 30, 2026

Implements a new type of array especially designed to use in oracle interfaces between Aztec.nr and PXE. The memory space of of these arrays is isolated by contract call frame and lives in memory. This makes them faster to work with, but it also removes the need for a lot of boilerplate to instantiate them, use them as params, and ultimately dispose of them.

Closes F-136

@mverzilli mverzilli marked this pull request as ready for review March 31, 2026 13:08
@mverzilli mverzilli requested a review from nventuro as a code owner March 31, 2026 13:08
@mverzilli mverzilli changed the base branch from next to merge-train/fairies March 31, 2026 13:08
@mverzilli mverzilli removed the request for review from nventuro March 31, 2026 13:17
Comment on lines +15 to +16
/// Ephemeral arrays are designed for transient communication between PXE (TypeScript) and contracts (Noir) during
/// simulation, for example, note validation requests or event validation responses.
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.

I think they are useful for more than that, but I want to grow a bit more confidence on the API and oracle design before encouraging people to use it happily

@mverzilli mverzilli requested review from Thunkar and nventuro April 8, 2026 18:05
@mverzilli mverzilli requested a review from benesjan April 8, 2026 18:05
Copy link
Copy Markdown
Contributor

@benesjan benesjan left a comment

Choose a reason for hiding this comment

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

This is great!

Reviewing it has completely exhausted me though 🥵

Comment on lines +15 to +16
/// Ephemeral arrays are designed for transient communication between PXE (TypeScript) and contracts (Noir) during
/// simulation, for example, note validation requests or event validation responses.
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.

Suggested change
/// Ephemeral arrays are designed for transient communication between PXE (TypeScript) and contracts (Noir) during
/// simulation, for example, note validation requests or event validation responses.
/// Ephemeral arrays are designed for passing data between PXE (TypeScript) and contracts (Noir) during simulation,
/// for example, note validation requests or event validation responses.

Same as above: "Transient" is overloaded here. In Aztec it already means "transient notes" (notes created and nullified in the same tx), so reusing it for ephemeral arrays risks suggesting a connection that doesn't exist. Worse, it's redundant — "ephemeral" already conveys the lifetime, and the sentence goes on to say "during simulation."

I'd drop it.

}

/// Initializes an empty ephemeral array at the given base slot, clearing any pre-existing data.
pub unconstrained fn initialize(base_slot: Field) -> Self {
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.

This seems unsafe as silently clearing pre-existing data hides bugs. I would instead just assert here that there is no pre-existing data. If there is a callsite where this behavior was desirable then I would just wipe the array before calling initialize here.

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.

Mmm, I see your point but at the same time it's a bit annoying to have to combine at+clear everywhere you just know you're working with disposable data. How about I nuke this one and make clear return Self? That way we can use clear to instantiate and the name makes it clear that you might be wiping stuff

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 think I would just rename this to clear_and_init if this is the target use case of this function. Or do you think it's too long and ugly?

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 see that you went with your approach. Fine with me 👍

});
}

#[test(should_fail)]
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.

Why don't we test error messages here and below? This risks the test passing when it fails for some other unrelated reason (e.g. like TXE not running or smt.)

@@ -0,0 +1,107 @@
import { Fr } from '@aztec/foundation/curves/bn254';

/** In-memory array service for transient data during a single contract call frame. */
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.

Suggested change
/** In-memory array service for transient data during a single contract call frame. */
/** In-memory store for ephemeral arrays scoped to a single contract call frame. */

Dropping "transient"

LOG_RETRIEVAL_RESPONSES_ARRAY_BASE_SLOT,
scope,
)
log_retrieval_requests.clear();
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.

This is also only defensive clearing in case get_pending_partial_notes_completion_logs was called multiple times, right?

Would put here a comment if yes.

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.

correct and good idea on adding a comment


/// Capsule array slot used by [`sync_inbox`] to pass tx hash resolution requests to PXE.
/// Ephemeral array slot used by [`sync_inbox`] to pass tx hash resolution requests to PXE.
global OFFCHAIN_CONTEXT_REQUESTS_SLOT: Field = sha256_to_field("AZTEC_NR::OFFCHAIN_CONTEXT_REQUESTS_SLOT".as_bytes());
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.

Unrelated but here we don't use the "base slot" terminology. So if you think we want to keep base slot would rename it here (I think I would just drop base). Can be for followup.

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.

I'll drop the base slot thing jargon, you're right it is unneeded

setContractSyncCacheInvalid(contractAddress: AztecAddress, scopes: AztecAddress[]): void;
emitOffchainEffect(data: Fr[]): Promise<void>;

// Ephemeral array methods — in-memory per-call-frame arrays for transient data.
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.

Suggested change
// Ephemeral array methods — in-memory per-call-frame arrays for transient data.
// Ephemeral array methods

Once again - to not overload transient

@@ -494,30 +496,41 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
}

public async getPendingTaggedLogs(pendingTaggedLogArrayBaseSlot: Fr, scope: AztecAddress) {
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.

Suggested change
public async getPendingTaggedLogs(pendingTaggedLogArrayBaseSlot: Fr, scope: AztecAddress) {
// Deprecated, only kept for backwards compatibility until Alpha v5 rolls out.
public async getPendingTaggedLogs(pendingTaggedLogArrayBaseSlot: Fr, scope: AztecAddress) {

Useful to have this well-tracked

return this.ephemeralArrayService.newArray(maybeLogRetrievalResponses.map(LogRetrievalResponse.toSerializedOption));
}

public async getMessageContextsByTxHash(
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.

Suggested change
public async getMessageContextsByTxHash(
// Deprecated, only kept for backwards compatibility until Alpha v5 rolls out.
public async getMessageContextsByTxHash(

Co-authored-by: Jan Beneš <janbenes1234@gmail.com>
/// callback.
///
/// It is **not** safe to push new elements from inside the callback.
pub unconstrained fn for_each<Env>(self, f: unconstrained fn[Env](u32, T) -> ())
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.

IDK if I'm a fan of for_each being used for mutation. Much like JS, I think it would be better to have dedicated methods (filter, map etc) but maybe Noir is not expressive enough

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.

Agree. This is a bit of a cargo cult of the CapsuleArray interface, I was kind of assuming "Noir is not expressive enough" is why we ended up with this

@mverzilli mverzilli requested a review from benesjan April 9, 2026 09:36
Copy link
Copy Markdown
Contributor

@benesjan benesjan left a comment

Choose a reason for hiding this comment

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

LGTM!

@mverzilli mverzilli enabled auto-merge (squash) April 9, 2026 14:00
@mverzilli mverzilli merged commit d8fbc34 into merge-train/fairies Apr 9, 2026
25 checks passed
@mverzilli mverzilli deleted the martin/volatile-arrays branch April 9, 2026 14:07
AztecBot pushed a commit that referenced this pull request Apr 9, 2026
Implements a new type of array especially designed to use in oracle
interfaces between Aztec.nr and PXE. The memory space of of these arrays
is isolated by contract call frame and lives in memory. This makes them
faster to work with, but it also removes the need for a lot of
boilerplate to instantiate them, use them as params, and ultimately
dispose of them.

Closes F-136

---------

Co-authored-by: Jan Beneš <janbenes1234@gmail.com>
@AztecBot
Copy link
Copy Markdown
Collaborator

AztecBot commented Apr 9, 2026

✅ Successfully backported to backport-to-v4-next-staging #22441.

Thunkar added a commit that referenced this pull request Apr 9, 2026
BEGIN_COMMIT_OVERRIDE
cherry-pick: fix(pxe): support custom PrivateKernelProver and unify
EmbeddedWalletOptions (#22348)
fix: update testnet compatibility test and bust cache on pinned artifact
changes (#22429)
fix(pxe): support custom PrivateKernelProver and unify
EmbeddedWalletOptions (backport #22348) (#22391)
refactor!: ephemeral arrays (#22162)
END_COMMIT_OVERRIDE
github-merge-queue bot pushed a commit that referenced this pull request Apr 9, 2026
BEGIN_COMMIT_OVERRIDE
refactor!: ephemeral arrays (#22162)
END_COMMIT_OVERRIDE
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants