Skip to content

feat: merge-train/fairies#21353

Merged
nventuro merged 26 commits intonextfrom
merge-train/fairies
Mar 13, 2026
Merged

feat: merge-train/fairies#21353
nventuro merged 26 commits intonextfrom
merge-train/fairies

Conversation

@AztecBot
Copy link
Copy Markdown
Collaborator

@AztecBot AztecBot commented Mar 11, 2026

BEGIN_COMMIT_OVERRIDE
fix: skip oracle version check for pinned protocol contracts (#21349)
fix: not reusing tags of partially reverted txs (#20817)
feat: move storage_slot from partial commitment to completion hash (#21351)
feat: offchain reception (#20893)
fix: handle workspace members in needsRecompile crate collection (#21284)
fix(aztec-nr): return Option from decode functions and fix event commitment capacity (#21264)
fix: handle bad note lengths on compute_note_hash_and_nullifier (#21271)
fix: address review feedback from PRs #21284 and #21237 (#21369)
fix: claim contract & improve nullif docs (#21234)
feat!: auto-enqueue public init nullifier for contracts with public functions (#20775)
fix: search for all note nonces instead of just the one for the note index (#21438)
fix: set anvilSlotsInAnEpoch in e2e_offchain_payment to prevent finalization race (#21452)
fix: complete legacy oracle mappings for all pinned contracts (#21404)
fix: correct inverted constrained encryption check in message delivery (#21399)
feat!: improve L2ToL1MessageWitness API (#21231)
fix: update example_swap to use new L2ToL1MembershipWitness API (#21466)
END_COMMIT_OVERRIDE

@benesjan benesjan requested a review from nventuro as a code owner March 11, 2026 11:07
mverzilli and others added 4 commits March 11, 2026 12:30
Adds the ability to receive offchain messages to Aztec.nr contracts. 

It works by using capsules to implement a persistent offchain message
inbox, and making sure that inbox is processed whenever `sync_state`
runs.

Apps deliver an offchain message to the inbox through a new utility
function `offchain_receive`, generated by the Aztec macro.

An `offchain::sync_inbox` function, takes the responsibility to let
`sync_state` know which messages should be processed like so:

1. Limits processed messages to those whose originating TX are available
at PXE's anchor block. To this end it uses a new
`resolve_message_contexts` oracle to determine which messages have
corresponding TXs known by PXE.
2. It is resilient to reorgs. Messages are re-processed for until
expiration time to make sure their effects don't get lost after re-orgs.
After expiration, they are safe to be discarded.

The feature can be exercised by invoking a function that emits offchain
messages on the sender side, saving the resulting offchain effects, and
then calling `offchain_receive` on the recipient side with them.

```typescript
    // Alice sends the private transfer which emits offchain messages.
    const { receipt, offchainMessages } = await contract.methods
      .transfer_offchain(paymentAmount, bob)
      .send({ from: alice });
    expect(offchainMessages.length).toBeGreaterThan(0);

    const messageForBob = offchainMessages.find(msg => msg.recipient.equals(bob));
    expect(messageForBob).toBeTruthy();

    await contract.methods
      .offchain_receive([
        {
          ciphertext: messageForBob!.payload,
          recipient: bob,
          tx_hash: receipt.txHash.hash,
          expiration_timestamp: messageForBob!.expirationTimestamp,
        },
      ])
      .simulate({ from: bob });
```

Known issues (can be addressed in subsequent PRs):

1. The new oracle, together with the long window offchain messages get
replayed for, means we should really cache `getTxEffects` node calls to
avoid performance regressions.
2. The current approach of PXE to caching contract sync calls, means
that with the current implementation we need to wait for one block
before a new message delivered to the inbox gets processed. We will
solve this by adding a new oracle to invalidate the contract sync cache.
3. Scopes aren't handled properly, which might need bigger refactors
that exceed offchain

Closes F-323
Closes F-327
Closes F-325
Copy link
Copy Markdown
Collaborator

@ludamad ludamad left a comment

Choose a reason for hiding this comment

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

🤖 Auto-approved

@AztecBot AztecBot added this pull request to the merge queue Mar 11, 2026
@AztecBot
Copy link
Copy Markdown
Collaborator Author

🤖 Auto-merge enabled after 4 hours of inactivity. This PR will be merged automatically once all checks pass.

@github-merge-queue github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Mar 11, 2026
AztecBot and others added 8 commits March 11, 2026 18:46
This fixes the `Claims` contract (which is a bit odd anyway) by using
`SingleUseClaim` instead of dangerously pushing a raw nullifier into
state. It's another example of `SingleUseClaim` being a bit awkward to
use - we may want to revisit that a bit.

I took the opportunity to review and improve the docs sorrounding
nullifiers, mostly noting how dangerous it is to use these functions
directly. I moved the docs on what a nullifier even is to the
`nullifier` mod, which seems like a better home for an explanation of
the concept.

---------

Co-authored-by: Claude Opus 4.6 <[email protected]>
@nchamo nchamo requested a review from LeilaWang as a code owner March 12, 2026 11:43
AztecBot and others added 6 commits March 12, 2026 15:04
…index (#21438)

We now search in all indices instead of just the one for the
corresponding note hash.
…ization race (#21452)

## Summary
- Sets `anvilSlotsInAnEpoch: 32` in `e2e_offchain_payment` test setup,
matching what `epochs_l1_reorgs` already does.

## Problem
PR #21156 added `--slots-in-an-epoch 1` as the default for anvil, making
`finalized = latest - 2`. PR #20893 added `e2e_offchain_payment` which
simulates L1 reorgs. When both landed on `merge-train/fairies`, the
reorg test fails deterministically because finalization races past the
rollback target block.

## Fix
Use `anvilSlotsInAnEpoch: 32` (matching Ethereum mainnet) so the
finalized block stays far enough behind latest to allow rollbacks in the
test.

ClaudeBox log: https://claudebox.work/s/c5ac5d52da86e23a?run=4
## Summary
- Expands legacy oracle aliases from 5 to 30 entries covering ALL
oracles used by ALL pinned protocol contracts (AuthRegistry,
ContractClassRegistry, ContractInstanceRegistry, FeeJuice,
MultiCallEntrypoint, PublicChecks, SponsoredFPC)
- Adds adapter functions for #21209 signature changes
(`privateNotifyEnqueuedPublicFunctionCall` and
`privateNotifySetPublicTeardownFunctionCall` 4-param →
`validatePublicCalldata` 1-param)
- Adds legacy mappings for #21209 renames
(`privateNotifySetMinRevertibleSideEffectCounter` →
`notifyRevertiblePhaseStart`, `privateIsSideEffectCounterRevertible` →
`inRevertiblePhase`)
- Replaces `isActualProtocolContract` (only checked 3 contracts) with
`isProtocolContract` (checks all) for oracle version check skip

## Test plan
- PXE tests pass (39 suites, 424 tests)
- Build compiles cleanly
- Lint passes
- Verified all old-style oracle names in committed artifacts are covered
by legacy mappings

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.6 <[email protected]>
#21399)

## Summary
- Fixed inverted boolean comparison in `message_delivery.nr` line 208
that caused `ONCHAIN_CONSTRAINED` to run encryption unconstrained and
vice versa.
- The check was comparing against `ONCHAIN_UNCONSTRAINED` instead of
`ONCHAIN_CONSTRAINED`, flipping the condition passed to
`remove_constraints_if`.

## Backport
This is a backport fix targeting `v4-next` via `merge-train/fairies`.

## Test plan
- [ ] Verify constrained encryption is actually constrained when using
`ONCHAIN_CONSTRAINED` delivery mode
- [ ] Verify unconstrained encryption when using `ONCHAIN_UNCONSTRAINED`
delivery mode

cc @nventuro for review

ClaudeBox log: https://claudebox.work/s/c8cf1c507575d776?run=3
The old API had two issues:
- it required knowledge of the epoch for the tx in which the message was
included, which is hard to acquire
- it did not support duplicate messages (i.e. same hash) in the epoch at
all - it only found the first one

This changes things so that users no longer need to know the epoch, and
instead query with `(message, txHash)`. If more than one message is
found in the tx matching `message`, we throw and request an additional
optional param `messageIndexInTx`. If this is passed, then we assert
that the message at that index indeed matches `message`.

Most users should not need to pass the index, but it is there in case it
is needed. It is expected that apps would know the messages they're
interested in, and can look at indices from the tx effects.

Doing this requires fetching the epoch as well as checkpoint, block and
tx indices from the node. There was no API for checkpoint data, so I
exposed `getCheckpointsDataForEpoch` so that we can find the
checkpoint's index in the epoch. I also added the epoch number to the tx
receipt.

---

I used Claude heavily here as I don't really know my way around the node
and archiver code, but I think the result makes sense.

Closes #20874

---------

Co-authored-by: Claude Opus 4.6 <[email protected]>
Co-authored-by: AztecBot <[email protected]>
Co-authored-by: Santiago Palladino <[email protected]>
@nventuro nventuro requested a review from a team as a code owner March 12, 2026 23:07
@nventuro nventuro enabled auto-merge March 12, 2026 23:09
@nventuro nventuro added the claudebox Owned by claudebox. it can push to this PR. label Mar 12, 2026
AztecBot and others added 4 commits March 12, 2026 23:11
## Summary

PR #21231 changed the `computeL2ToL1MembershipWitness` API signature
from `(node, epoch, message)` to `(node, message, txHash)`, but the
`example_swap` docs example (added in PR #20233) was written against the
old API. This caused the merge-train CI to fail with `TypeError: Cannot
read properties of undefined (reading 'siblingPath')` because the
function received wrong arguments and returned `undefined`.

## Changes

- Removed `RollupContract` import and usage (no longer needed to
manually compute epoch)
- Updated both `computeL2ToL1MembershipWitness` calls to use new
signature: `(node, message, txHash)`
- Get epoch numbers from the returned witness (`witness.epochNumber`)
instead of computing manually

ClaudeBox log: https://claudebox.work/s/5eb93c1235ed3384?run=2

Co-authored-by: esau <[email protected]>
@nventuro nventuro added this pull request to the merge queue Mar 13, 2026
Merged via the queue into next with commit 9fbd9ef Mar 13, 2026
22 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci-no-squash claudebox Owned by claudebox. it can push to this PR.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants