refactor!: ephemeral arrays#22162
Conversation
| /// Ephemeral arrays are designed for transient communication between PXE (TypeScript) and contracts (Noir) during | ||
| /// simulation, for example, note validation requests or event validation responses. |
There was a problem hiding this comment.
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
benesjan
left a comment
There was a problem hiding this comment.
This is great!
Reviewing it has completely exhausted me though 🥵
| /// Ephemeral arrays are designed for transient communication between PXE (TypeScript) and contracts (Noir) during | ||
| /// simulation, for example, note validation requests or event validation responses. |
There was a problem hiding this comment.
| /// 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 { |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
I see that you went with your approach. Fine with me 👍
| }); | ||
| } | ||
|
|
||
| #[test(should_fail)] |
There was a problem hiding this comment.
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. */ | |||
There was a problem hiding this comment.
| /** 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(); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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()); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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. |
There was a problem hiding this comment.
| // 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) { | |||
There was a problem hiding this comment.
| 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( |
There was a problem hiding this comment.
| 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) -> ()) |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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
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>
|
✅ Successfully backported to backport-to-v4-next-staging #22441. |
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
BEGIN_COMMIT_OVERRIDE refactor!: ephemeral arrays (#22162) END_COMMIT_OVERRIDE
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