Skip to content

chore(release): merge v5-next into v5 for v5.1.0 - #24897

Merged
aminsammara merged 41 commits into
v5from
release-v5.1.0
Jul 22, 2026
Merged

chore(release): merge v5-next into v5 for v5.1.0#24897
aminsammara merged 41 commits into
v5from
release-v5.1.0

Conversation

@aminsammara

@aminsammara aminsammara commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Promotes v5-next onto v5 for the v5.1.0 release.

  • Merges v5-next into v5; the resulting tree is identical to v5-next and .release-please-manifest.json is set to 5.1.0 (the only merge conflict was the manifest — v5 held 5.0.1, resolved to v5-next's 5.1.0).
  • Merge via merge commit (v5 ruleset allows merge only).
  • After this merges, v5 HEAD is tagged v5.1.0.

Does not include #24840 (json-rpc result key), which is still on merge-train/spartan-v5 and not in v5-next.

AztecBot and others added 30 commits July 14, 2026 04:04
Managed to corrupt my DB 🤷
BEGIN_COMMIT_OVERRIDE
fix: handle corruption errors (#24739)
END_COMMIT_OVERRIDE
…l validation (#24694)

## Problem

On a live mainnet node, checkpoint-proposal validation could throw an
uncaught error out of the
gossipsub message handler whenever it ran while the world-state
synchronizer trailed the archiver
(block source) by a block:

```
ERROR world-state:database Call CREATE_FORK failed: Error: Unable to initialize from future
  block: 117860 unfinalizedBlockHeight: 117859. Tree name: NullifierTree
ERROR p2p:libp2p_service Error handling gossipsub message: Error: Unable to initialize from
  future block: 117860 unfinalizedBlockHeight: 117859. Tree name: NullifierTree
```

The archiver already had block N (so the block lookups inside checkpoint
validation succeeded), but
world state's `unfinalizedBlockHeight` was still N-1. When
`validateCheckpointProposal` forked world
state at the parent block before it had been applied, the raw tree error
escaped as an uncaught
ERROR-level gossipsub message. The node lost that checkpoint
validation/attestation for the round and
spammed error logs; it self-healed the next tick, but it should not
happen.

## Root cause

The two world-state fork sites were asymmetric. The block re-execution
path (`reexecuteTransactions`)
syncs world state *before* forking. The checkpoint validation path
(`validateCheckpointProposal`)
forked via `checkpointsBuilder.getFork` *without* a preceding sync. The
checkpoint path does sync the
block source (archiver) earlier, but that does not advance the world
state, so the gap remained.

## Fix

- Move the sync-before-fork invariant into
`FullNodeCheckpointsBuilder.getFork`: it now calls
`worldState.syncImmediate(blockNumber, blockHash)` before
`worldState.fork(blockNumber)`, so the
single fork helper owns the invariant rather than each caller.
`syncImmediate` blocks until world
state reaches the block, or throws a typed error if it genuinely cannot,
instead of leaking the raw
  tree error.
- In `validateCheckpointProposal`, look up the parent block's hash from
the block source and pass it
through so the sync re-syncs on a world-state reorg. Wrap the fork in a
try/catch that maps any
failure to a clean, non-slashable `world_state_not_synced` result, so a
sync/fork failure never
  surfaces as an uncaught ERROR-level gossipsub message.
- As a second, independent guard, check that the forked world state's
archive root matches the
proposal's `lastArchiveRoot` before rebuilding the checkpoint. A
mismatch means world state forked
from a different chain than the proposal was built on, so we fail fast
with a clean, non-slashable
`initial_archive_mismatch` result — mirroring the block-proposal
re-execution check — instead of a
  confusing downstream header/archive mismatch.

Both new reasons record an `unvalidated` outcome and are non-slashable,
matching the other
"we couldn't validate right now" / local-state-divergence reasons.

Other checkpoint-builder fork sites were reviewed and need no change:
the proposer's
`checkpoint_proposal_job` forks at its own already-synced,
coherence-checked world-state tip, and the
block-proposal re-execution path already syncs before forking and
performs the equivalent
archive-root check.

## Tests

- `checkpoint_builder.test.ts`: `getFork` syncs world state to the block
before forking, and
  propagates a sync failure without forking.
- `proposal_handler.test.ts`: forks at the parent block number passing
its block hash; a fork failure
returns `world_state_not_synced`; and a fork whose archive root diverges
from the proposal returns
  `initial_archive_mismatch`.

## Notes

The same bug exists on the v4 line — the v4 site is the same function,
forking via
`this.worldState.fork(parentBlockNumber)` directly without a preceding
`syncImmediate`. This fix
should be ported there as well.

Fixes A-1421
The `docs/examples/bootstrap.sh execute` step is failing CI. The example
runner does an unpinned `yarn add -D typescript tsx`, and
`typescript@latest`
on npm is now `7.0.2` (the native port). Its package layout has no
`lib/_tsc.js`, so Yarn 4's builtin compat patch fails to apply:

```
Error: typescript@patch:...npm%3A7.0.2...: ENOENT: no such file or directory, lstat '.../lib/_tsc.js'
```

Pin the install to `typescript@^5.3.3` — the same line the rest of the
monorepo
uses (resolves to 5.9.3) and matching the existing pin in
`docs/examples/ts/bootstrap.sh`. Verified with Yarn 4.13.0: the unpinned
add
reproduces the `_tsc.js` error, the pinned add applies the compat patch
cleanly.
BEGIN_COMMIT_OVERRIDE
fix(validator): sync world state before forking in checkpoint proposal
validation (#24694)
END_COMMIT_OVERRIDE
Prevents multi-tab concurrency issues in SQLite store creation by
guarding it with weblocks.

Closes F-829
This is a defensive measure in case more than one SQLite page file is
found pointing to the same logical name, which could cause undefined
behavior. This could happen with old, pre-weblock controlled versions of
wallet/pxe (see
#24740), so it's
unlikely to be found in the wild, but it gives us graceful coverage if
that happens.

We detect if a pool contains two valid .opaque files mapped to the same
logical SQLite path, then:

  1. Acquire the pool Web Lock.
2. Copy the entire pool byte-for-byte into:
.aztec-sqlite-quarantine/<timestamp-random>/
  3. Verify the copied directory and file contents.
  4. Write a quarantine.json describing the duplicate mappings.
  5. Delete the original active pool.
  6. Open a new, empty database under the original pool name.
  7. Emit a warning log containing the quarantine location.

The caller receives a successfully opened but empty store, so wallet/PXE
state would need to be recreated or resynchronized. The quarantined
bytes remain available for forensic or manual recovery, although there
is currently no public API or UI for that.

If copying or verification fails, opening fails and the original pool is
not intentionally removed. If the pool merely comes from an old version
but has no duplicate logical mappings, nothing special happens, it opens
normally.

---------

Co-authored-by: Gregorio Juliana <gregojquiros@gmail.com>
#24689)

## Motivation

The `#[note]` macro generated each `PropertySelector` from the field's
position in the struct declaration, but selectors are applied to the
note's packed representation. The two only agree when every field packs
to one `Field`. For a note with a multi-slot field (a `Point`, an array,
a nested struct), every filter on a later field silently constrained an
unrelated packed slot — on the constrained read path, where these checks
are what bind oracle-returned notes to the contract's criteria. A
malicious PXE could satisfy a filter with a note that does not match it.
No shipped contract is affected (all first-party filters target
single-slot fields with single-slot predecessors); the bug was latent in
the library.

## The change

- Selector indices are the field's packed offset: the accumulated sum of
preceding fields' `Packable::N`, mirroring `derive(Packable)`'s layout.
- `PropertySelector<T>` carries the selected field's type.
`select`/`sort` reject fields that pack to more than one `Field` at
compile time (a one-slot criterion cannot express them), and `select`'s
value is typed as the field's type.
- Criterion values are compared as `value.pack()[0]` instead of
`value.to_field()`, matching what the packed note slot actually
contains.
- `properties()` statically asserts the note's packed length equals the
sum of its field packed lengths, so custom `Packable` layouts get a
compile error directing to hand-written selectors instead of wrong ones.
A same-length field reorder is not detectable; that residual gap is
documented.
- Breaking: hand-written `PropertySelector` literals need a type
annotation, and every note field type must implement `Packable`.
Migration notes included. All existing contracts compile to identical
circuits; the PXE already applied indices to the packed layout, so no TS
changes.

## Future changes

The root cause is that two independent authorities describe the same
layout: the note's `Packable` impl owns it, and the macro assumes it.
Every assertion in this PR is a consistency check between the two, and
one divergence (a custom pack that reorders fields at the same total
length) cannot be checked at all and still fails silently. A potential
fix (maybe there is a better one) is for the note macro to own the
layout: `#[note]` derives `Packable` itself and generates the selectors
from the same field list, making divergence impossible by construction,
with an explicit custom-packing opt-out that generates neither and
leaves both to the author. That is a larger breaking change to the macro
surface and is left for a follow-up.

Fixes F-800
BEGIN_COMMIT_OVERRIDE
feat: weblock controlled opfs pool (#24740)
chore: handle legacy duplicate opaque handles (#24743)
fix(aztec-nr)!: compute note property selectors from the packed layout
(#24689)
END_COMMIT_OVERRIDE
Restores the mainnet `SLASH_GRACE_PERIOD_L2_SLOTS` default from `1200`
to `8400` slots in `spartan/environments/network-defaults.yml`.

At the mainnet `AZTEC_SLOT_DURATION` of 72s:
- `1200 × 72s = 86,400s` = **exactly 24 hours** (the value v5 shipped
with)
- `8400 × 72s = 604,800s` = **exactly 7 days** (the intended value)

This value was already raised to 8400 in #21451 ("tune mainnet slasher
penalties and sequencer allocation"), but that commit landed directly on
the v4 release branch and never made it onto `next`. `v5` therefore
inherited the stale 1200 from `next`.

Confirmed by diffing the branches:

| branch | mainnet grace | at 72s slot |
| --- | --- | --- |
| `v4-next` | 8400 | 7 days |
| `next` / `v5-next` / `merge-train/spartan` | 1200 | 24 hours |

- **Only the `networks.mainnet` preset is changed.** The top-level
`slasher` anchor (`0`) and the `devnet` preset (`0`) are unchanged.
- **`testnet` (64 slots) is deliberately left alone.** `v4-next` has
3600 there, but that value traces back to the original `refactor:
centralize network defaults in YAML` commit on the `next` line rather
than to the lost #21451 change — so it is a separate long-standing
divergence, not part of this regression. Worth a follow-up decision, but
out of scope here.
- No generated files are committed:
`yarn-project/slasher/src/generated/` and the CLI network presets are
gitignored and produced at build time from this YAML.
- Operator docs updated to match (8,400 slots / ~7 days). The
`network_versioned_docs/version-v5.0.1` snapshot is intentionally left
untouched, since it documents what v5.0.1 actually shipped.

This changes a **baked-in default for future builds/deployments**. It
does not retroactively alter the grace period on the already-running v5
network — nodes there need `SLASH_GRACE_PERIOD_L2_SLOTS` set explicitly
or a redeploy.

- [x] YAML parses; mainnet resolves to exactly 7.00 days at a 72s slot
- [x] No test or `spartan/environments/*.env` file pins the old `1200`
value

---
*Created by
[claudebox](https://claudebox.work/v2/sessions/849a625af4f2c309/jobs/1)
· group: `slackbot` · [Slack
thread](https://aztecprotocol.slack.com/archives/C0AU8BULZHC/p1784543473010009?thread_ts=1784543473.010009&cid=C0AU8BULZHC)*
…4804) (#24808)

## Summary
Backport of #24804
to `v5-next` via `backport-to-v5-next-staging`.

Restores the mainnet `SLASH_GRACE_PERIOD_L2_SLOTS` default from `1200`
to `8400` slots and updates the operator slashing docs to describe the
7-day grace period.

## Conflict resolution
The automatic cherry-pick conflicted in:
- `spartan/environments/network-defaults.yml`
-
`docs/docs-operate/operators/sequencer-management/slashing-configuration.md`

Resolved by keeping the v5 branch's surrounding slashing/defaults
context and applying only the intended grace-period value and docs
wording from the source PR.

## Tests
- `yq '.networks.mainnet.SLASH_GRACE_PERIOD_L2_SLOTS'
spartan/environments/network-defaults.yml`
- `slots=$(yq '.networks.mainnet.SLASH_GRACE_PERIOD_L2_SLOTS'
spartan/environments/network-defaults.yml) && slot_duration=$(yq
'.networks.mainnet.AZTEC_SLOT_DURATION'
spartan/environments/network-defaults.yml) && test "$slots" = 8400 &&
test "$slot_duration" = 72 && test $((slots * slot_duration)) -eq
604800`
- `rg -n 'SLASH_GRACE_PERIOD_L2_SLOTS[:=] ?1200|First 1,200|First 128
slots|1,200 slots' spartan/environments docs/docs-operate` (no matches)
- `git diff --check origin/backport-to-v5-next-staging...HEAD`

---
*Created by
[claudebox](https://claudebox.work/v2/sessions/195fbf5dfecad0c3/jobs/1)
· group: `slackbot` · [Slack
thread](https://aztecprotocol.slack.com/archives/C0AGN2WT3CP/p1784549517302859?thread_ts=1784549517.302859&cid=C0AGN2WT3CP)*
This pr adds an optional prover-node config,
`PROVER_NODE_PROOF_SUBMISSION_TARGET_ADDRESS`, that lets a prover send
the `submitEpochRootProof` tx to a contract other than the rollup.

Everything else is unchanged: all rollup state is still read from the
real rollup, only the destination of the submit tx is redirected. When
unset, it defaults to the rollup address, so existing provers are
unaffected.

Use case:
A prover that wants to claim prover tips from oxide-styled withdrawal
txs must register itself in the Pool contract as the first prover to
submit a proof of a given length L. To do that, it deploys a wrapper
contract that calls the Pool before (check proven length = L-T) and
after (check proven length = L) calling `submitEpochRootProof` on the
rollup, then points this config at the wrapper.

Closes GK-752
This fixes a scoping bug where an account's ivsk would be used to
produce an ECDH shared secret even if that account was not in scope. The
base wallet class was adjusted to put these in scope when an explicit
sender is selected for tagged messages.
BEGIN_COMMIT_OVERRIDE
refactor(pxe): compute oracle interface hash from wire-structural
mapping labels (#24752)
fix: tagging secrets not being scoped by sender (#24772)
chore: clarify scope of packable impl detection (#24820)
END_COMMIT_OVERRIDE
Replace the secp256r1-specific unique/positive table_index test with one
that sweeps every basic table reachable through any MultiTable. The
LogDeriv relation identifies a table solely by table_index, so this guards
against any generator (not just secp256r1 fixed-base) storing a window or
bit-slice position in place of the builder-assigned index.
Generated by ci-refresh-chonk.

Only the pinned Chonk input hash is committed here; the immediate follow-up CI run is skipped intentionally.

--ci-skip
fix(bb): assign unique secp256r1 lookup table indices
Reduce the mainnet SLASH_INACTIVITY_TARGET_PERCENTAGE default from 0.8
(80%) to 0.7 (70%) in spartan/environments/network-defaults.yml. Only
the networks.mainnet preset is changed; devnet/testnet and the top-level
slasher anchor (all 0.9) are untouched.
aminsammara and others added 11 commits July 21, 2026 12:26
BEGIN_COMMIT_OVERRIDE
fix(docs-examples): pin typescript to 5.x in execute runner (#24736)
chore(spartan): restore 7 day mainnet slash grace period (#24804)
chore(spartan): restore 7 day mainnet slash grace period (backport
#24804) (#24808)
feat: allow custom proof submission target address (#24270)
END_COMMIT_OVERRIDE
…5-next (#24842)

Brings public `v5-next` up to `private/v5-next` (`eb52040bd1`), porting
the barretenberg secp256r1 fixed-base lookup-table soundness fix.

## Net change (6 files, all under `barretenberg/cpp/`)
- Assign a unique `table_index` to each secp256r1 fixed-base lookup
table, and add finalize-time guards that reject duplicate or zero table
indices, with a new circuit-checker test
(`Secp256r1FixedBaseTablesGetUniquePositiveIndices`,
`FinalizationRejectsDuplicateTableIndices`,
`FinalizationRejectsZeroTableIndex`).
- Refresh the pinned Chonk IVC inputs hash to match the circuit change.

## Notes for reviewers
- The commit set also includes the routine `chore: sync public-v5-next
with upstream v5-next` merge nodes that sit between the two tips; those
carry no net file change. The only net delta is the fix + the chonk hash
bump.
- Must merge via a **merge commit** (branch ruleset allows `merge`
only). The public→private sync will reconcile `v5-next` afterward, so
private stays a fast-forward of public.
## Summary

- Documents the trust model of partial note completion on
`PartialUintNote` and `PartialNFTNote`:
- The validity commitment only proves that the contract created the
partial note designating `completer`.
- The storage slot and value/token id are trusted arguments, not bound
by the commitment.
- The completer is not authenticated by the check itself, so contracts
must pass `msg_sender()` as `completer`.
- Adds a WARNING that completion is not single-use: the designated
completer can complete the same partial note any number of times, so
contracts must make every completion independently paid for or
authorized in the completing function (as the token and NFT contracts
already do).
- Fixes doc overclaims that said the validity commitment verifies the
storage slot / state variable.
…#24844)

## Summary

- AuthRegistry, MultiCallEntrypoint and PublicChecks hold no private
state, but used the default `sync_state`, which performs pointless
discovery RPC calls and embeds the HandshakeRegistry address in their
bytecode.
- Adds a shared `do_sync_state_no_op` handler to aztec-nr and wires it
into those three contracts, plus SchnorrInitializerlessAccount, which
previously carried its own local copy of the same no-op.
- The pinned standard-contract artifacts are untouched, so shipped
artifacts and addresses only change at the next intentional re-pin.
A contract with no notes might otherwise panic if e.g. it processed an
offchain message related to one. I also made PXE skip the standard
contracts that have no notes and events, both to avoid such a situation
and because there's no need to do it.
…esting utilities (#24892)

Reimplementation of
#24837 - same
nullifier fix, this time extending `TestEnvironment` so that we can test
the fix works as intended.
BEGIN_COMMIT_OVERRIDE
docs(aztec-nr): document partial note completion trust model (#24816)
refactor(aztec-nr): shared no-op sync handler for stateless contracts
(#24844)
fix: dont panic on note msgs on contracts with no notes (#24852)
docs(noir-contracts): document standard-contract re-pin consequences
(#24890)
fix: change init and single claim nullif to incl owner address, add
testing utilities (#24892)
END_COMMIT_OVERRIDE
@aminsammara
aminsammara requested a review from a team July 21, 2026 23:05
@aminsammara
aminsammara requested review from a team, LeilaWang and nventuro as code owners July 21, 2026 23:05
@aminsammara
aminsammara merged commit 3ffc13a into v5 Jul 22, 2026
14 checks passed
@aminsammara
aminsammara deleted the release-v5.1.0 branch July 22, 2026 09:57
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.