Skip to content

chore: port v5-next to next#24825

Open
AztecBot wants to merge 784 commits into
nextfrom
port-v5-next-to-next
Open

chore: port v5-next to next#24825
AztecBot wants to merge 784 commits into
nextfrom
port-v5-next-to-next

Conversation

@AztecBot

@AztecBot AztecBot commented Jul 21, 2026

Copy link
Copy Markdown
Collaborator

BEGIN_COMMIT_OVERRIDE
chore: update Noir to v1.0.0-beta.22 (v5-next) (#23870)
refactor(pxe)!: derive oracle interfaces from registry and introduce aztec_misc_ prefix (#23840)
fix(ci): fail loudly on yarn.lock drift after build (#23882)
chore: regenerate yarn.lock for noir beta.22 (#23877)
feat!: proofless tx lookup in getTxByHash and getTxsByHash (#23827)
chore: update Noir to v1.0.0-beta.22 (v5-next, redo of #23870) (#23886)
chore: re-sync public-eligible files unintentionally diverged on v5-next (#219)
chore: pin build (#23869)
feat: merge-train/spartan-v5 (#23885)
feat(ci): private release to internal Artifact Registry (docker + npm) with nightly tags (#206)
refactor(pxe): append only NoteStore and PrivateEventStore (#23785)
feat(aztec-nr): include delivery mode in handshake discovery (#23873)
refactor(txe): move orchestration logic out of rpc_translator (#23904)
refactor(aztec-nr)!: move messages::message_delivery to messages::delivery (#23875)
fix(ci): run contract-snapshots tests in CI (#23895)
fix: check epoch job overlap (#23481)
fix: init bb sync before node rpc (#23864)
refactor(sequencer): split timing into consensus and proposer timetables (#23809)
chore: Accumulated backports to v5-next (#23879)
refactor(pxe): hash oracle registry instead of Oracle class (#23907)
chore(sequencer): gate checkSync behind the cheap proposer check (#23810)
test(cli-wallet): wait for checkpointed in serial sandbox flow tests (#23916)
feat: merge-train/spartan-v5 (#23910)
fix(docs): update webapp-tutorial to current wallets/PXE API + fix vite polyfill resolution (#23908)
fix: guard formatViemError structuredClone against non-cloneable errors (A-818) (#23919)
fix: validate contract artifact schema in CLI deploy/call path (A-836) (#23921)
feat: TransientArray (#23576)
refactor(pxe): replace Oracle class with Proxy-based buildACIRCallback (#23915)
test: stabilize validator_nuke_and_suppression post-recovery assertion (#23852)
fix(e2e): poll for high-value inclusion in n_tps bench (#23858)
fix(e2e): always init bb sync in wallet worker (#23855)
feat: merge-train/spartan-v5 (#23927)
fix: interrupt checkpoint job on sequencer stop (#23930)
chore: add tmp folder in yarn-project (#23932)
test(ci): skip non-deterministic expand::test_avm_test_contract snapshot (#23935)
feat: merge-train/fairies-v5 (#23881)
chore: cherry-pick private-port-next label (#23939)
fix(p2p): cap inbound tx DA gas limit by max tx blob size (A-1162) (#23933)
fix: ingest L1 checkpoints up to the blob block capacity (A-1156) (#23934)
fix: sync proposed checkpoint on HA peers (A-1165) (#23940)
feat(pxe): add constrained-secret search discipline to syncTaggedPrivateLogs (#23942)
chore: Accumulated backports to v5-next (#23931)
chore: reward boost config per AZIP-5 (#23943)
feat: merge-train/spartan-v5 (#23936)
feat: merge-train/fairies-v5 (#23948)
fix(pxe): prevent contract sync deadlock on nested syncs (#23951)
feat(noir): optionally install nargo from the matching official release (#23949)
docs(stdlib): clarify checkpoint capacity ceiling is the provable max (#23952)
feat(ci3): diversify build-instance spot via EC2 create-fleet (v5-next) (#23966)
feat: merge-train/fairies-v5 (#23961)
test: always capture local network logs for compose tests (#23912)
fix: pin getAttesters reads to a single L1 block (A-819) (#23920)
refactor(prover-node): CheckpointStore + SessionManager redesign (#23552)
feat: merge-train/spartan-v5 (#23965)
feat: initializerless schnorr account contract (#23962)
fix(p2p): stop checkpoint-replay storm when pruning to an uncheckpointed block (#23967)
refactor(sequencer)!: always enforce timetable with concrete block duration (#23821)
fix(e2e): drop removed enforceTimeTable option from optimistic proving test (#23976)
feat: merge-train/fairies-v5 (#23974)
feat: persist peer bans for a configurable duration (A-1157) (#23922)
fix(ci3): scope build-instance name by repo to stop cross-repo reaping (v5-next) (#23988)
fix(ci3): scope build-instance name by repo to stop cross-repo reaping (#308)
chore: improve noir contract test tooling (#23946)
fix: interrupt publisher send-at-slot sleep on sequencer stop (#23990)
refactor!: rename node JSON-RPC to aztec_* prefixes (#23909)
feat(aztec-nr): extend OnchainDelivery builder for secret origin (#23865)
fix(p2p): drive tx protection release from synced blocks instead of wall clock (#23978)
feat(aztec-nr): wire handshake secret discovery into contract sync (#23938)
fix(ci): run noir-projects nargo fmt check in CI (#24003)
chore: Accumulated backports to v5-next (#23994)
feat: merge-train/fairies-v5 (#23992)
chore(ci): public nightlies skip scenario tests and the next tag (#23984)
chore: public nightlies skip scenario tests and the next tag (backport #23984) (#24009)
chore: Backport to v5 next staging (#24010)
fix(p2p)!: resolve checkpoint tips from stored ids (#23968)
fix: deflake HA full e2e suite by switching to in-proc interval-mining anvil (#23979)
test: fix point_compression is_greater flag for regenerated BLS12-381 generator (#328)
fix(gas)!: client fallback limits track network per-block budget (A-1154) (#23947)
feat: network-wide consensus config with validation and override protection (A-1168) (#23977)
test(e2e): pick bad slots upfront and warp to them in proposer invalidates multiple checkpoints (#24017)
feat: use initializerless accounts (#23973)
chore(ci): scenario tests pick docker registry by repo on v5-next (#24022)
docs(CLAUDE.md): discourage unprompted subagents and dynamic workflows (#24027)
fix: remove type assertion (#24023)
feat: richer EphemeralArray and TransientArray APIs (#23982)
fix(bot): check L1-to-L2 message readiness against PXE sync tip (#24004)
fix: Merge Conflicts (#24014)
feat: merge-train/spartan-v5 (#23975)
fix: merge train conflicts (#24047)
refactor(stdlib)!: thin chain-checkpointed event, collapse sync (#24007)
refactor!: remove proposedCheckpoint tip (#24008)
fix(sequencer): wait for previous L1 block before publishing (#24037)
fix: restore v5-next merge ancestry on fairies merge train (re-open #24047) (#24049)
fix: client flows benchmarks (#24055)
test(e2e_fees): bridge fee juice from a dedicated L1 account (#24054)
fix(aztec-node): pipelining-aware slot and fee simulation in simulatePublicCalls (#24031)
feat: merge-train/fairies-v5 (#24020)
fix(ci3): GITHUB_REPOSITORY unbound when running ci.sh locally (v5-next) (#24060)
refactor: move registerContractFunctionSignatures to the node debug API (#24066)
fix(noir-protocol-circuits): fail when pinned VKs do not match (#364)
chore: reject non-canonical x coordinate (native affine_element) (#24029)
fix: resolve v5-next → merge-train/spartan-v5 conflict (#24072)
chore: merge v5-next into merge-train/spartan-v5 (raw, conflict markers) (#24071)
feat: add alerts for internal networks (#24090)
test(ci): mark e2e_epochs/epochs_mbps_redistribution as flaky (#24098)
fix: isolate cross-chain L1 writes from publisher nonce (#24104)
fix(ci3): cache DNS on build instances on v5-next (#24107)
test(world-state): make delayed-close fork queue-cleanup wait deterministic (#24106)
chore: Accumulated backports to v5-next (#24095)
chore: remove obsolete no_predicates wrappers (#7729) (#24093)
fix(kv-store): lazy-load skipped browser benchmark deps (#24108)
fix(ci): name the merge-train branch in Slack notifications (#24102)
feat!: change for_each iteration order in CapsuleArray, EphemeralArray, TransientArray (#24021)
chore: Accumulated backports to v5-next (#24118)
feat(p2p): slash proposers exceeding max blocks per checkpoint (A-1166) (#24041)
fix(stdlib): fix race conditions in L2BlockStream (#24042)
chore: resolve public-v5-next→v5-next merge (regen pinned VKs) (#391)
chore: merge public-v5-next into v5-next (#388)
test(prover-node): make checkpoint store pruning test deterministic (#24130)
feat: merge-train/fairies-v5 (#24117)
fix(simulator): make circuit recorder concurrency-safe via AsyncLocalStorage (#24112)
refactor(pxe): restore satisfies-typed oracle registries (#24132)
feat: merge-train/spartan-v5 (#24053)
feat: merge-train/fairies-v5 (#24134)
fix: rename contract class getters to reflect origina vs current (#24140)
fix: content-addressed merkle tree / LMDB store correctness fixes (#211)
feat: merge-train/fairies-v5 (#24144)
fix(test): reliably find target proposer in sentinel_status_slash (A-1217) (#24143)
fix(test): wait for full gossip mesh before committee produces (A-1219) (#24149)
fix: init bb.js sync singleton before subsystems start in createAndSync (#24147)
feat(prover-node): capture checkpoint-level proving metrics (#24051)
fix: allow prover mode autodetection (#24151)
chore: add create-issue skill for filing Linear issues (#24082)
fix: stabilize scenario invalidation timing (#24128)
fix: set default inbox lag to 2 (#24127)
test(spartan): wait for proposed instead of checkpointed in performTransfers (#24123)
fix(validator): make block-number guard reorg-aware (A-1218) (#24141)
chore: Accumulated backports to v5-next (#24153)
refactor: Booth-slice element MSM (straus_msm + batch_mul), shared with Constantine recoder (#23691)
fix(pxe): repoint broken debugging docs link in PXE error messages (#24145)
fix(test): pin AZTEC_INBOX_LAG=1 in sandbox compose envs (#24162)
chore(sequencer): downgrade insufficient-txs block log to verbose (#24164)
feat: merge-train/fairies-v5 (#24152)
fix(ci): import initializerless account in CLI acceptance test (#24175)
fix(test): use in-memory file store for TxFileStore tests (A-1211) (#24167)
fix(test): handle default inbox lag of 2, remove temporary inboxLag=1 pins (A-1250) (#24170)
fix(world-state): clean up queues for destroyed forks (#24178)
feat(bb)!: Hard-code shift size in merge verifier (#409)
feat: merge-train/fairies-v5 (#24176)
feat: merge-train/spartan-v5 (#24148)
fix(ethereum): skip unfunded publishers in selection (#24180)
test(e2e): deflake epochs_mbps_redistribution (#24182)
test(l1): assert verifier/distributor/booster/slasher references in V5 validate() (#24187)
feat(l1): add V5 upgrade payload and deploy script (#23752)
feat(prover-node)!: wire prover JSON-RPC API to the admin endpoint (#24189)
feat: merge-train/spartan-v5 (#24181)
fix(p2p): re-seed discovery from persisted peer ENRs after restart (#24169)
docs(e2e): annotate e2e tests with setup/category notes (#24191)
feat(txe): auto-generate oracle serialization roundtrip tests (#24138)
feat: reject out-of-range checkpoint header fields at propose (A-1254) (#24199)
feat: merge-train/fairies-v5 (#24200)
feat: protect epoch-proof fees against an unsound verifier (#24190)
feat(aztec-nr)!: add msg_sender to the utility context (#24062)
fix(cli): transpile pre-existing artifacts on aztec compile (#24188)
feat: merge-train/fairies-v5 (#24206)
chore: merge v5-next into merge-train/spartan-v5 (raw, conflict markers) (#24221)
feat: make node.getContract take an optional reference block (#24207)
fix(archiver): index zero-field logs under empty tag instead of throwing (A-1253) (#24212)
feat(aztec-nr): wire constrained message delivery (#23866)
fix(prover-node): report awaiting-root and publishing-proof phases in EpochSession (A-1212) (#24216)
fix(p2p): bound declared contract-class bytecode length before allocating (A-1258) (#24213)
fix(p2p): frame gossipsub msgId and restrict allowedTopics (A-1256) (#24214)
chore: merge v5-next into merge-train/spartan-v5 (#24226)
fix(pxe): respect slot boundaries in oracle (de)serialization (#24211)
fix(p2p): guard ENR address parsing against malformed TCP fields (A-1255) (#24215)
feat: allow registration of raw shared secrets (#23708)
feat(e2e): constrained delivery and discovery with two PXEs (#24228)
feat: merge-train/spartan-v5 (#24196)
refactor(stdlib)!: rename unsafe AztecAddress constructors to *Unsafe (#24230)
fix(world-state): verify archive root in sync_block to reject divergent state (#24229)
feat(aztec-node): force sentinel for validators + offense collection (#24202)
fix(archiver): treat re-publish of preloaded protocol contracts as idempotent (#24227)
test(e2e): wait for L1 txs to be mined in token bridge tutorial test (#24065)
docs(v5-next): backport doc corrections from #24005 and #23830 (#24242)
docs(v5-next): backport counter tutorial alignment from #23517 (#24243)
fix(txe): correct AVM oracle registry types for call and success_copy (#24203)
fix(spartan): fund sponsored FPC in block-capacity bench env (#24248)
feat(aztec-nr): make handshake tagging secrets mode agnostic (#24241)
feat: merge-train/spartan-v5 (#24256)
docs: add network-deployed-version skill (#24238)
docs: require self-contained code comments (#24239)
fix(archiver): validate checkpoint attestations from calldata before fetching blobs (A-1252) (#24247)
fix(sequencer): prune in failed-sync fallback so the chain can recover (#24253)
fix: port reusable network-teardown action to v5-next (#24252)
fix(telemetry): raise span queue size and make telemetry shutdown idempotent (#24121)
feat: merge-train/spartan-v5 (#24264)
test(validator-client): deflake integration test with frozen clock (#24259)
chore: Accumulated backports to v5-next (#24267)
fix(ci3): cache DNS on build instances to dodge link-local PPS throttling (#24105)
chore: Accumulated backports to v5-next (#24277)
refactor(pxe): derive oracle wire mappings from plain structs (#24244)
test: retry composed cheat code timestamp race (#24279)
chore: merge v5-next into merge-train/fairies-v5 (#24296)
chore: merge v5-next into merge-train/fairies-v5 (#24299)
refactor(aztec_sublib): drop unused standard addresses and authwit (#24273)
chore(sqlite3mc-wasm): bump SQLite3MultipleCiphers to 2.3.5 (#24293)
refactor(aztec-nr)!: return structs from call & keys oracles (#24284)
refactor(aztec-node): split server.ts into factory + focused modules (#24283)
docs(v5-next): backport v5 testnet connection details + evergreen doc fixes (#24246)
refactor(testing): route warpL2Time cheat codes through automine debug RPC (#24303)
feat(node): auto-shutdown node on incompatible canonical rollup upgrade (#24269)
feat(pxe)!: unify sender/secret registration into TaggingSecretSource (#24280)
feat(pxe): resolve tagging secret strategy via wallet hook (#24040)
test: deflake e2e_offchain_payment reorg reprocessing race (#24309)
feat: FactStore (#23989)
feat(e2e): track per-test setup/hook timings (#24281)
chore(tooling): add monitor-pr skill (#24274)
test: deflake epochs_optimistic_proving reorg-during-proving gate (#24308)
fix(kernel): bind tx_request_salt across the private kernels (#477)
fix(pxe): use v5 AztecAddress factories in FactStore (#24322)
refactor: message context service -> tx resolver service (#24131)
feat: merge-train/spartan-v5 (#24272)
fix(aztec-node): import inspectBlockParameter in server.ts (#24323)
refactor(e2e): consolidate the multi-node and single-node test categories (#24201)
fix(e2e): keep timing_env testEnvironment through yarn prepare (#24328)
feat: add TestEnvironment::send_l1_to_l2_message (#24314)
feat: aztec.nr side of fact store (#24139)
refactor(noir-contracts): drop unneeded constrained delivery (#24336)
refactor: offchain reception on entity store (#24142)
test(e2e): speed up setup by automining L1 contract deployment (#24313)
test(e2e): increase slashing proposer search attempts (#24339)
test(sequencer-client): fix checkpoint proposal job timetable setup (#24346)
test(e2e): consolidate more tests into multi-node and single-node categories (#24310)
test(e2e): 10x faster deploy L1 contracts via on-demand mining under anvil automine (#24316)
fix: exclude tmp from yarn-project clean-lite (#24342)
fix(sequencer): gate waitForMinTxs on age-eligible pending tx count (#24315)
fix: support pagination in getPublicEvents (#24324)
test(noir-contracts): TestToken with unconstrained delivery and repoint e2e tests (#24337)
feat: merge-train/fairies-v5 (#24223)
fix(ci): retry forge solc fetch on transient DNS failures (#24358)
chore: copy constants and verifier out of private for v5 payload (#24297)
fix(aztec-nr): Prevent handshake nullifier griefing and nullifier sequence tests (#24258)
chore: merge v5-next into merge-train/spartan-v5 (#24361)
chore: Accumulated backports to v5-next (#24360)
test(e2e): bound fee settings L1 base fee spike (#24355)
feat(aztec-nr): derive message tag from a resolved secret source (#24312)
test(sequencer-client): freeze clock with ManualDateProvider to deflake checkpoint build-offset assertion (#24363)
test(e2e): deflake optimistic-proving by anchoring epoch and asserting on checkpoint header slot (#24364)
test(e2e): drop advancePastGenesis now that genesis uses a real timestamp (#24362)
feat(keys)!: add derivation for message-signing and fallback keys (#24348)
test(e2e): start debug_trace single-node test at genesis block 0 (#24376)
test(e2e): anchor multi_proof on a fresh epoch instead of hardcoding epoch 0 (#24372)
feat: make sqlite the path of least resistance backend option for PXE and wallet (#24179)
test(e2e): consolidate the automine test category (#24343)
test(e2e): deflake sync_after_reorg by waiting on the rollup's actual prune predicate (#24379)
feat: merge-train/spartan-v5 (#24331)
fix(ethereum): broadcast L1 deploy txs one at a time on anvil to dodge automine race (#24378)
test(ci): classify e2e_token_bridge_tutorial L1-tx confirmation timeout as the known flake (#24385)
feat: merge-train/fairies-v5 (#24365)
feat: merge-train/spartan-v5 (#24384)
chore(aztec-nr): add constrained delivery index nullifier test (#24366)
refactor(e2e): relocate no-node straggler tests (#24344)
test(e2e): pin gas_estimation public-payment txs to one block to deflake fee comparison (#24382)
feat(archiver): event-trigger L2BlockStream sync from archiver updates (#24317)
fix(release-image): stamp stdlib/package.json with release version (#23393)
fix(key-store): protect all KeyStore methods with transactionAsync (#24406)
fix(release-image): stamp stdlib/package.json with release version (#24405)
feat(pxe): add resolveCustomRequest hook for caller-defined requests (#24409)
test(e2e): speed up individual e2e tests (#24345)
chore(e2e): warm blob KZGs in parallel during setup (#24383)
chore: add perf as a valid PR title prefix (#24412)
refactor(pxe): unify the app-silo primitive for tagging and encryption (#24402)
feat!: close down access to context member fields (#24417)
feat(pxe): default unconstrained delivery to a handshake (#24387)
test(e2e): adopt shared wait helpers (#24404)
test(e2e): run prover client.test.ts in CI (#24399)
fix(ethereum): mine empty L1 blocks without touching the mempool (#24414)
test(e2e): deflake empty block proving test (#24411)
test(e2e): allocate HA node p2p ports above the ephemeral range to deflake e2e_ha_full (#24418)
fix(sequencer): use evmMine in automine auto-settle to avoid dropping test L1 txs (#24421)
test(e2e): instrument common spans for wall-clock tracking (#24407)
test(e2e): remove redundant reqresp_no_handshake e2e test (#24424)
feat!: allow passing and retrieving privacy keys in pxe/wallet (#24416)
feat: merge-train/spartan-v5 (#24391)
fix(bench): use address-derived tagging secrets for the client-flows bench; widen the PXE unfinalized tagging window (#24429)
test(e2e): skip token bridge deploy and pre-deploy cross-chain L1 contracts (#24408)
feat(aztec-nr): let contracts choose the message tag derivation (#24432)
feat: merge-train/fairies-v5 (#24388)
feat(pxe): origin block number timestamp log oracle in log retrieval (#24398)
feat: merge-train/spartan-v5 (#24438)
fix(sequencer): recover fee-estimation caches from transient L1 read failures (#24426)
test(e2e): fix epoch-2 tip race in proof_fails after-epoch-end test (#24441)
feat: annotate fact origin block state (#24289)
feat: merge-train/fairies-v5 (#24443)
feat(avm): lock-free sharded TraceContainer (port #24300 to v5-next) (#24442)
feat: use fact origin block state in offchain reception (#24292)
fix(avm): avoid data race on shared interaction selector writes (port #24456 to v5-next) (#24457)
feat: merge-train/fairies-v5 (#24455)
feat!: remove fallback and signing keys from pxe (#24451)
fix(aztec-nr): support empty notes and events (#24464)
docs: improve upgrades warning (#24462)
test(e2e): resolve remaining wait-code REFACTOR markers (#24428)
perf(e2e): parallelize setup & p2p work and lighten throwaway txs (#24448)
fix(e2e): give token_bridge_tutorial its own L1 account to avoid sequencer nonce race (#24386)
feat!: make contract classes dynamic (#24282)
feat(bench): Updated tx inclusion benchmarking (#24266)
fix(txe): align aztec_avm_returndataSize registry type with Noir u32 (#24480)
fix(wallet-sdk): use derived contract address in registerContract (#24482)
docs: document how to make nested utility calls (#24478)
feat(aztec-nr): add interactive handshake to the handshake registry (#24473)
feat: merge-train/fairies-v5 (#24470)
fix: improve valkeys password handling (#24459)
feat: allow separate valkeys passwords. (#24460)
fix(pxe): return no shared secrets when scope keys are not held (#24487)
feat: speed up valkeys generation (#24467)
feat: merge-train/fairies-v5 (#24505)
feat(ci): merge-train/spartan-v6 (v5-next port) (#24517)
chore: Accumulated backports to v5-next (#24497)
feat: store discovered handshakes as facts (#24483)
feat: merge-train/fairies-v5 (#24519)
feat: merge-train/spartan-v5 (#24454)
fix(sequencer): do not run fallback actions within build window (#24504)
fix(proposal-handler): ensure we default to pushing blocks to archiver (#24506)
fix(p2p): bump global reqresp limits (#24507)
fix(p2p): reject proposal when expected proposer is undefined (#24509)
fix(archiver): tolerate re-included already-stored checkpoints (#24513)
fix(p2p): canonicalize yParity attestation signatures before L1 bundle (#24515)
test(e2e): sync anvil clock before sequencer start (#24474)
test(e2e): consolidate automine token suites (#24489)
test(e2e): consolidate automine contracts and effects suites (#24490)
test(e2e): consolidate cross-chain bridge and messaging suites (#24491)
test(e2e): merge single-node fee, proving, and sequencer config suites (#24492)
test(e2e): name shared timing presets and collapse proof-submission constants (#24494)
test(e2e): unify slashing timing profiles and merge duplicate suites (#24495)
test(e2e): dedupe multi-node block-production and governance test setups (#24498)
test(e2e): extract p2p gossip scenario skeleton and add category README (#24500)
test(e2e): split node-killing HA test into its own composed suite file (#24503)
feat(sequencer): make sequencer pausable (#24475)
feat: merge-train/spartan-v5 (#24524)
fix(sqlite3mc-wasm): restore bundler-visible wasm resolution (#24529)
perf(e2e): warp proven-checkpoint waits, tighten in-process polling, and instrument setup spans (#24452)
fix: reject aztecSlotDuration not a multiple of ethereumSlotDuration (#24481)
ci: apply private-port-next label to any v-next merge-train base (v5-next) (#24538)
refactor(aztec-nr): rename get_handshakes to non_interactive variant (#24511)
fix(pxe): UNFINALIZED_TAGGING_INDEXES_WINDOW_LEN should be MAX_PRIVATE_LOGS_PER_TX + headroom, not exactly MAX_PRIVATE_LOGS_PER_TX (#24437)
refactor(aztec-nr): keep handshake secrets internal to TagSecretSource (#24508)
fix(config): fail loudly on invalid or fractional numeric config (#24536)
fix(sequencer): invalidate malicious yParity attestation checkpoints (A-1401) (#24537)
test(txe): extend oracle roundtrip coverage to more scalar oracles (#24550)
refactor!: reimplement partial notes on FactStore (#24369)
fix(ethereum): deflake monitor timeout tests in l1_tx_utils (#24545)
fix(ci): retry docker compose up on transient registry pull failures (#24546)
fix(e2e): resolve flaky proposer-not-found in equivocation_offenses duplicate_proposal test (#24557)
fix(e2e): resolve flaky checkpoint TypeError in multi_validator_node test (#24543)
feat!: stop deriving signing key from privacy keys (#24439)
fix(ethereum): prevent unhandled L1 rejections from tx monitor loops during shutdown (v5 line, partial) (#24551)
fix(sequencer): prevent unhandled rejections from in-flight L1 requests during shutdown (v5 line) (#24560)
feat: merge-train/fairies-v5 (#24530)
feat: merge-train/spartan-v5 (#24532)
fix(node): fork-aware getWorldState that fails closed on sync errors (#24563)
fix(validator): prevent double-sign on stuck-duty cleanup race and handle pg pool errors (A-1313, A-1314) (#24556)
fix(p2p): bound reqresp response buffering before size check (#24553)
fix: preload txe with protocol contracts (#24574)
fix(p2p): reject reqresp requests larger than a single muxer frame (#24554)
fix(p2p): process one request per reqresp stream to enforce rate limit (#24552)
fix(validator): fail closed on signing-protection persistence gaps (A-1315, A-1317, A-1318) (#24565)
feat(p2p): retain finalized txs a configurable margin behind finality (#24329)
test(e2e): onchain message delivery harness (#24373)
feat(pxe): recipient-side interactive handshake registration (#24514)
feat(aztec-nr): sender-side support for interactive handshakes (#24522)
feat: merge-train/spartan-v5 (#24576)
test(txe): struct support in oracle serialization roundtrip tests (#24588)
chore(prover-client): demote empty addTxs log from warn to verbose (#24593)
refactor(aztec-nr): drop unused HandshakeNote fields (#24595)
docs: threat model for node (#24465)
feat: merge-train/spartan-v5 (#24596)
feat: merge-train/fairies-v5 (#24580)
docs(e2e): update READMEs for the consolidated suite layout (#24518)
test(e2e): instrument and diagnose bot suite setup cost (#24534)
perf(e2e): warp dead waits in multi-node recovery and proving tests (#24566)
perf(e2e): shrink e2e slot times (#24570)
perf(e2e): seed BananaFPC fee juice at genesis instead of bridging (#24564)
perf(bot): parallelize independent bot factory setup steps (#24581)
feat(world-state): support prefilled nullifiers in genesis state (#24567)
perf(e2e): seed standard contracts at genesis (#24568)
fix: tweak depositToAztec gas config (#24607)
feat(prover): revive cancelled proving jobs from a persisted aborted state (#24578)
chore: add writing-e2e-tests skill (#24597)
perf(e2e): overlap and batch setup txs in e2e harnesses (#24569)
feat!: make inbox secrets be multiple fields (#24599)
feat(e2e): interactive handshake e2e (#24590)
docs: revert threat model for node (#24465) (#24610)
docs: document initializerless accounts (v5-next backport of #24512) (#24598)
docs: add video lessons page with short explainer videos (v5-next backport of #24555) (#24601)
feat: merge-train/fairies-v5 (#24609)
fix(txe): align tagging strategy oracle with PXE (#24561)
feat: merge-train/fairies-v5 (#24619)
fix(sol): use pinned compiler for bb sol build (#24620)
fix(aztec.js): give waitForNode a bounded default timeout (#24627)
refactor: cache Aztec node reads per execution (#24630)
feat(pxe)!: Add AppTaggingSecret kinds to keys in tagging stores (#24604)
feat: add batch is block in archive oracle (#24634)
feat: merge-train/spartan-v5 (#24606)
feat: merge-train/fairies-v5 (#24632)
feat: getTxEffects oracle (#24636)
fix(prover-node): rebuild pruned checkpoint provers and recover the epoch (#24436)
docs: add offchain message delivery guide (backport of #24583 to v5-next) (#24613)
feat: preserve stores on schema version or rollup address change (#24631)
feat: assert non revertible phase when setting fee payer (#24479)
feat: merge-train/spartan-v5 (#24641)
fix: release OPFS handles before sqlite worker close/delete ack (#24647)
feat: merge-train/fairies-v5 (#24638)
fix(prover-node): do not abort in-flight proving jobs on a clean shutdown (#24579)
fix: prevent access to secrets not in scope (#24616)
fix: prevent reception of messages too far into the future (#24645)
fix(aztec-nr): prevent recipient forging a colliding handshake (#24403)
feat: merge-train/spartan-v5 (#24652)
feat!: forbid external note validation checks (#24644)
feat(txe): add option to authorize all utility call targets (#24662)
docs: fee readme improvements (#24666)
feat(cli): support funding accounts in validator-keys new/set-funding-account (#24476)
feat(cli): support funding accounts in validator-keys new/set-funding-account (#24476)
feat: merge-train/fairies-v5 (#24654)
fix(aztec-nr): reject infinity ephemeral key in message encryption (#24665)
feat: merge-train/spartan-v5 (#24669)
chore: Accumulated backports to v5-next (#24670)
feat: merge-train/fairies-v5 (#24673)
test: fix proof_boundary startup race (#24671)
feat: merge-train/spartan-v5 (#24677)
feat: exported in-process testing network (#24629)
chore: move prover namespace to regular RPC server (#24680)
chore: revert "chore: move prover namespace to regular RPC server" (#24683)
fix(pxe): widen tracked sender tagging ranges with onchain discovery evidence (#24655)
feat: merge-train/fairies-v5 (#24682)
fix(aztec-nr): tolerate malformed partial-note completion logs (#24668)
feat: merge-train/fairies-v5 (#24702)
chore: merge v5 into v5-next (#24700)
fix: handle corruption errors (#24739)
feat: merge-train/fairies-v5 (#24744)
fix(validator): sync world state before forking in checkpoint proposal validation (#24694)
feat: merge-train/spartan-v5 (#24746)
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)
feat: merge-train/fairies-v5 (#24769)
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)
feat: merge-train/fairies-v5 (#24817)
END_COMMIT_OVERRIDE

spalladino and others added 30 commits July 1, 2026 12:21
Wait for checkpoint 1 before raising minTxsPerBlock so epoch 0 has
something to prove.
…flake e2e_ha_full (#24418)

## Summary

Deflakes `e2e_ha_full.parallel.test.ts`, which FLAKED in CI ([run
6d29cd3fcf374a68](http://ci.aztec-labs.com/6d29cd3fcf374a68)) reporting
`should not be affected by process.env.TZ changes` as the failing test.

## Root cause

- The reported test name is a red herring. The failure happened in
`beforeAll` while creating the second HA node (HA-1): `CodeError:
Transport (@libp2p/tcp) could not listen on any available address`, code
`ERR_NO_VALID_ADDRESSES`. jest attributed the `beforeAll` throw to the
first test in the suite (the timezone test, a pure Postgres test that
never ran) and skipped the other 7.
- The suite assigned **fixed** p2p ports `(config.p2pPort ?? 40400) + i
+ 1`, i.e. 40400 (bootstrap), 40401-40405 (HA nodes). These sit inside
the Linux default ephemeral port range (`32768-60999`, per
`/proc/sys/net/ipv4/ip_local_port_range`).
- The OS draws ephemeral ports from that range for the in-process prover
node (created by `setup()` with `p2pPort: 0`) and for outbound TCP
connections. When an ephemeral socket already held one of 40401-40405 at
bind time, that HA node's libp2p TCP `listen()` failed with
`EADDRINUSE`. libp2p runs the listen attempt under `Promise.allSettled`
and, finding no fulfilled result, throws the aggregate
`ERR_NO_VALID_ADDRESSES` without surfacing the underlying `EADDRINUSE` —
which is why no `EADDRINUSE` string appears in the logs.
- Confirmed against the passing retry (`061f6753f56b00fd`): identical
fixed ports, but there HA-0..HA-4 bound 40401-40405 cleanly. The only
divergence is HA-1 failing to bind 40402 in the failed run.

## Fix

- Allocate every node's p2p port via `get-port` from **61000-65535**,
above the ephemeral range, so neither in-process ephemeral sockets nor
concurrent CI jobs can be holding a node's port when libp2p binds it.
Applied to the bootstrap node (via `setup()` opts) and to each HA node.
- Set `p2pBroadcastPort` alongside `p2pPort`: discv5 defaults the
broadcast port by mutating the config object in place, and that mutated
value would otherwise leak from the bootstrap config into the HA node
configs (which are built by spreading `config`), making them advertise
the wrong port in their ENR.

## Verification

- TypeScript build of `@aztec/end-to-end` passes; prettier clean.
- A local repro of the flake is not feasible: it is a probabilistic
ephemeral-port collision that cannot be deterministically forced, and
the test requires the full docker-compose HA stack (Postgres +
Web3Signer). The fix is grounded in both the logs (HA-1 failing to bind
fixed port 40402 with `ERR_NO_VALID_ADDRESSES`) and source (fixed ports
inside the ephemeral range; `p2pPort: 0` prover node; discv5 binding UDP
on the same port) — moving the ports above the ephemeral range removes
the collision source entirely.
… test L1 txs (#24421)

The local-network `AutomineSequencer` auto-settle loop hangs a test's
direct L1 tx (~180s viem `WaitForTransactionReceiptTimeoutError`),
surfacing as the `e2e_token_bridge_tutorial` flake.

## Context

After `markAsProven`, `runProve()` called `mineEmptyBlock()` purely to
advance the L1 block hash so the archiver re-reads the proven tip.
`mineEmptyBlock` drops and re-adds the anvil mempool non-atomically
(snapshot → `anvil_dropAllTransactions` → mine → re-add snapshotted
txs). A direct test L1 tx submitted in the snapshot→drop window is
dropped and never re-added; with the sequencer otherwise idle it is
never mined, so the caller hangs until viem times out.

## Approach

Replace `mineEmptyBlock()` with `evmMine()` at that call site.
`evm_mine` advances the block hash and includes whatever is pending,
with no mempool drop and no automine toggle — so there is no race
window. The call site only needs a new block hash, not an empty block.

`mineEmptyBlock()` itself is unchanged: its other callers invoke it
single-threaded with no concurrent external tx and some rely on the
block being empty, and making it race-safe across independent RPC
clients is out of scope.
## Motivation

A-1178 (#24281) measures each e2e test's wall-clock as four coarse
buckets (`setupFnMs`, `beforeHooksMs`, `bodyMs`, `afterHooksMs`). That
tells us a test is slow but not *where* it loses time. Most e2e tests
spend their wall-clock in the same handful of operations — standing up
the environment, waiting for a tx to be mined/checkpointed/proven,
waiting for a committee to form or an offense to be detected,
client-side proving of seed txs, and warp scans hunting for a proposer
slot.

This decomposes that time into **named spans** so a single aggregate
query over a full CI run answers *"across the suite, how much total
wall-clock goes into proving / spinning up nodes / waiting for
checkpoints?"* — a ranked list of where to invest in speedups. It
extends A-1178 and builds on the consolidation (#24201/#24310) and
helpers (#24404) work that turned each repeated operation into one
shared definition, so wrapping it once instruments every test with no
per-test edits.

This is **instrumentation / data-gathering only** — no test behavior or
timing changes.

## Approach

- A generic `testSpan(name, fn)` / `testSpanSync(name, fn)` wrapper
(`fixtures/timing.ts`) records `{ owner, name, start, end }` into the
shared collector installed by the timing environment. When
`TEST_TIMING_FILE` is unset there is no collector, so `testSpan()` calls
`fn()` directly — exactly zero-cost, with no clock reads.
- At flush, the timing environment groups spans by owner then tag and
computes four numbers per `(owner, tag)`:
- `count` — multiplicity is itself a signal (waited for a checkpoint 14×
points at a loop to batch).
  - `totalMs` — naive sum (correct for serial repeats).
- `busyMs` — duration of the **union** of the spans' intervals;
concurrency-correct, so a `Promise.all` of 12 concurrent 3s spans reads
~3s instead of ~36s. The `busyMs ≪ totalMs` signature flags work that is
run serially where it could be parallel.
- `maxMs` — longest single occurrence, to catch one pathological wait
hiding in a cheap average.
- The aggregates attach as an additive `spans` map on each `type:"test"`
/ `type:"suite"` JSONL line. `setupFnMs` / `teardownFnMs` are kept for
back-compat, now derived from the `setup:env:<mode>` / `teardown:env`
tags.
- Tags follow a stable `category:label` taxonomy (`setup:`, `wait:`,
`tx:`, `warp:`, `wallet:`, `deploy:`, `other:`), tagged **by concept,
not by function** — e.g. every checkpoint waiter maps to
`wait:checkpoint` — so a label is a forever aggregation key regardless
of which helper a test happened to call.
- **One clock.** All spans use `performance.now()`. I verified Node's
`perf_hooks` clock is process-wide: a `performance.now()` taken inside a
fresh `vm` context (the jest sandbox realm) shares the same monotonic
origin as the host realm (delta ~0.002ms; a fresh
`perf_hooks.performance` inside a separate context anchors to the same
`timeOrigin`). Interval-merging for `busyMs` across the sandbox and host
realms is therefore valid, so **`busyMs` is included** (no fallback to
`totalMs`-only needed).
- **Background loop.** `startMempoolFeeder` runs interleaved with
arbitrary tests; without care its prove/send spans would smear onto
whichever test was current when each round fired. Its production is run
inside an `AsyncLocalStorage`-scoped owner override
(`other:mempool-feeder`), which pins every span in its async call tree
to a fixed owner — isolated from concurrent test-body spans. That owner
matches no test/suite record, so the feeder's spans are cleanly excluded
from the per-test view.

## Changes by phase

- **Phase 0 — collector + `testSpan()` + flush aggregation.** New
`fixtures/timing.ts` (`testSpan`/`testSpanSync`/`withTestSpanOwner`).
`timing_env.mjs` collector generalized from `fnSpans` to a `spans`
array; `finalizeAndFlush` groups by owner/tag, merges intervals for
`busyMs`, attaches the `spans` map, and derives the back-compat fields.
- **Phase 1 — shared waits + `setup:node`.** Wrapped the waiters in
`fixtures/wait_helpers.ts` (`wait:proposed` / `wait:proven`,
`wait:checkpoint` / `wait:proven-checkpoint`, `wait:tx-mined`,
`wait:l2-to-l1-witness`, `wait:pending-tx`, `wait:sequencer-state`) and
the wait methods on `SingleNodeTestContext` / `MultiNodeTestContext`
(`wait:epoch`, `wait:slot`, `wait:proof-window`, `wait:node-sync`,
`wait:proof-submitted`, `wait:offense`, the multi-node
`wait:checkpoint`/`wait:proven-checkpoint`/`wait:block` convergence
waiters). Wrapped `createNode`/`createProverNode` with `setup:node`.
- **Phase 2 — decompose `setup:env`.** Cracked the opaque setup in
`fixtures/setup.ts` into `setup:env:anvil`, `:l1-deploy`,
`:sequencer-start`, `:prover-node`, `:pxe`, and `wallet:create`. The
top-level `setup:env` is tagged with the prover mode (`setup:env:none` /
`:fake` / `:real`) so the three factories are comparable.
- **Phase 3 — tx leaf wraps.** `proveInteraction` → `tx:prove`,
`ProvenTx.send` → `tx:send`; everything else
(`proveTxs`/`proveAndSendTxs`/the submit helpers) aggregates through
those leaves. `startMempoolFeeder` handled as above.
- **Phase 4 — slashing/governance waiters.** Wrapped
`awaitCommitteeExists` (`wait:committee`), `awaitOffenseDetected`
(`wait:offense`), `awaitCommitteeKicked` (`wait:committee-kicked`),
`awaitProposalExecution` (`wait:slash-execution`), and the
`findUpcomingProposerSlot` / `advanceToEpochBeforeProposer` /
`findSlotsWithProposers` scans (`warp:find-proposer`).
- **Phase 5 — reporting.** The JSONL schema now carries the per-line
`spans` map, and `TEST_TIMING_SPANS=1` emits one `type:"span"` line per
occurrence (owner, name, ms) for deep dives. The repo now also ships a
`track-e2e-times` skill (`yarn-project/.claude/skills/track-e2e-times/`)
covering how to **collect and aggregate** the span timings locally: run
the suite with `TEST_TIMING_FILE` set, find the per-worker JSONL, and
print per-test sums plus the ranked span leaderboard (via the bundled
`row.sh`). Only the step of publishing aggregate numbers to a running
tracking log remains a separate out-of-band workflow. The leaderboard
rollup is a small additive jq over the new `spans` maps:

  ```
jq -rs '[ .[] | select(.type=="test" or .type=="suite") | (.spans // {})
| to_entries[] ]
    | group_by(.key)
    | map({ tag: .[0].key, count: (map(.value.count) | add),
busyMs: (map(.value.busyMs) | add), maxMs: (map(.value.maxMs) | max) })
| sort_by(-.busyMs) | .[] | "\(.busyMs)\t\(.count)\t\(.maxMs)\t\(.tag)"'
  ```

The existing `row.sh` is unaffected — it filters `type=="test"` and
reads the unchanged `setupFnMs`/`bodyMs`/etc., ignoring the new `spans`
key and `type:"span"` lines.

## Notes

- `waitForEpoch`/`waitForSlot` (added to `@aztec/ethereum`'s
`rollup_cheat_codes.ts` by #24404) are deliberately **not** instrumented
in-package: that package should not depend on the e2e timing collector,
and the `wait:epoch`/`wait:slot` concepts they cover are already
captured at the e2e-context wrapper layer (`waitUntilEpochStarts`, the
slot waiters). They have no e2e callers yet; a future call site picks
them up via its context wrapper.
- Spans do not partition `bodyMs` — a parent span includes its children,
so `sum(spans) ≠ bodyMs`. Tagging is at the leaf wait/setup/tx level
where additivity holds; the few intentional nests (e.g.
`wait:committee-kicked` contains `wait:slash-execution`) carry distinct
tags.

Verified: `yarn build`, `yarn lint end-to-end`, `yarn format --check
end-to-end` all pass. The flush aggregation (busyMs interval merge,
back-compat derivation, feeder-owner exclusion, opt-in raw lines) is
covered by unit tests in `end-to-end/src/shared/timing_env.test.ts`,
which exercise the extracted `aggregateSpans` / `foldSpansInto` pure
functions directly.

Fixes A-1179



## Example timing output

Each e2e test run in CI emits one JSONL record per test and per suite,
carrying a `spans` map keyed by `category:label` tag, where each value
is `{count, totalMs, busyMs, maxMs}` (`busyMs` is the de-overlapped
union duration, so concurrent occurrences of a span are counted once).
An illustrative `type:"test"` line from CI run 28469687883 (commit
de3635e):

```json
{
  "suite": "optimistic.parallel",
  "type": "test",
  "name": "single-node/proving/optimistic happy path proves multiple epochs via checkpoint-driven flow",
  "status": "passed",
  "setupFnMs": 4707,
  "beforeHooksMs": 4717,
  "bodyMs": 327324,
  "teardownFnMs": 615,
  "afterHooksMs": 624,
  "totalMs": 332668,
  "startedAt": "2026-06-30T19:20:58.279Z",
  "spans": {
    "setup:env:anvil": {
      "count": 1,
      "totalMs": 82,
      "busyMs": 82,
      "maxMs": 82
    },
    "setup:env:l1-deploy": {
      "count": 1,
      "totalMs": 470,
      "busyMs": 470,
      "maxMs": 470
    },
    "setup:env:sequencer-start": {
      "count": 1,
      "totalMs": 3141,
      "busyMs": 3141,
      "maxMs": 3141
    },
    "setup:env:prover-node": {
      "count": 1,
      "totalMs": 139,
      "busyMs": 139,
      "maxMs": 139
    },
    "setup:env:pxe": {
      "count": 1,
      "totalMs": 391,
      "busyMs": 391,
      "maxMs": 391
    },
    "wallet:create": {
      "count": 1,
      "totalMs": 79,
      "busyMs": 79,
      "maxMs": 79
    },
    "setup:env:fake": {
      "count": 1,
      "totalMs": 4707,
      "busyMs": 4707,
      "maxMs": 4707
    },
    "wait:epoch": {
      "count": 5,
      "totalMs": 180210,
      "busyMs": 180210,
      "maxMs": 36067
    },
    "tx:prove": {
      "count": 4,
      "totalMs": 2123,
      "busyMs": 2123,
      "maxMs": 621
    },
    "tx:send": {
      "count": 4,
      "totalMs": 144305,
      "busyMs": 144305,
      "maxMs": 36124
    },
    "wait:proven-checkpoint": {
      "count": 4,
      "totalMs": 0,
      "busyMs": 0,
      "maxMs": 0
    },
    "wait:node-sync": {
      "count": 4,
      "totalMs": 401,
      "busyMs": 401,
      "maxMs": 101
    },
    "teardown:env": {
      "count": 1,
      "totalMs": 615,
      "busyMs": 615,
      "maxMs": 615
    }
  },
  "commit": "de3635e74eb89071b52939d1583d072ba2c3852c",
  "branch": "spl/a1179-track-common-spans",
  "runId": "28469687883"
}
```
Removes the redundant `reqresp_no_handshake` e2e test.
This lets wallets specify the private keys of the different privacy keys
directly, instead of them all being derived from a single master secret.
The wallets get equivalent functionality.

There's also a new getter that returns these keys, only in PXE. Wallets
can use this e.g. export accounts and import in a separate device. Apps
do not get access to these keys.

Finally I got rid of some old utilities that were unused
(`account_with_single_key`).

---

Some more work will follow this (e.g. not actually storing the fallback
keys, etc.), this is the initial step in the modernization of this store
and interface.
BEGIN_COMMIT_OVERRIDE
refactor(e2e): relocate no-node straggler tests (#24344)
test(e2e): pin gas_estimation public-payment txs to one block to deflake
fee comparison (#24382)
feat(archiver): event-trigger L2BlockStream sync from archiver updates
(#24317)
test(e2e): speed up individual e2e tests (#24345)
chore(e2e): warm blob KZGs in parallel during setup (#24383)
chore: add perf as a valid PR title prefix (#24412)
test(e2e): adopt shared wait helpers (#24404)
test(e2e): run prover client.test.ts in CI (#24399)
fix(ethereum): mine empty L1 blocks without touching the mempool
(#24414)
test(e2e): deflake empty block proving test (#24411)
test(e2e): allocate HA node p2p ports above the ephemeral range to
deflake e2e_ha_full (#24418)
fix(sequencer): use evmMine in automine auto-settle to avoid dropping
test L1 txs (#24421)
test(e2e): instrument common spans for wall-clock tracking (#24407)
test(e2e): remove redundant reqresp_no_handshake e2e test (#24424)
END_COMMIT_OVERRIDE
…bench; widen the PXE unfinalized tagging window (#24429)

## Summary

- widen the PXE `UNFINALIZED_TAGGING_INDEXES_WINDOW_LEN` to
`MAX_PRIVATE_LOGS_PER_TX` (aligning the window with the per-tx
private-log cap, which is the real bound the store is gated on)
- wire a `resolveTaggingSecretStrategy` hook on the client-flows bench
user wallet that returns `address-derived` for unconstrained delivery,
restoring the pre-#24387 tagging secret path for these benches

## Investigation

The merge-train PR #24388 was removed from the merge queue on 2026-07-01
at 13:05 UTC after CI3 failed in `bench all` on merge-group SHA
`84262a7ee42e1c391c00b55b377910ddb4308230`, in run `28515530242`. The
concrete failures:

- `client_flows/transfers`: expected 7 execution steps but got 10 for
the sponsored-FPC transfer case, matching 2 extra app executions plus
one extra kernel batch after #24387 (`feat(pxe): default unconstrained
delivery to a handshake`).
- `bench_build_block`: timed out after repeated `Highest used index 21
is further than window length from the highest finalized index 0.
Tagging window length 20 is configured too low` errors.

Both failures share a root cause: #24387 defaulted unconstrained
delivery to a non-interactive handshake for external recipients, and the
benches don't pre-establish a chain, so every timed transfer inflates a
cold-chain handshake bootstrap into the measured numbers.

## Why the hook, not just wider fudges

The bench's subject is steady-state app cost. Baking `+2 app executions
per cold handshake` into `expectedExecutionSteps` measures a
first-time-only cost every run, and re-tunes with every future
handshake-bootstrap change. Instead, the user wallet now wires a
`resolveTaggingSecretStrategy` hook that returns `{ type:
'address-derived' }` for unconstrained delivery — the same tagging
secret path this bench got before #24387. The hook only affects the
client-flows bench's user wallet, not production defaults.

Constrained delivery is unaffected: the Noir circuit rejects
`address-derived` there, so the hook falls back to
`non-interactive-handshake` for the constrained branch.

## Why the window bump stands independently

The previous doc-comment on `UNFINALIZED_TAGGING_INDEXES_WINDOW_LEN` was
self-contradictory: the header text said the value had to be larger than
`MAX_PRIVATE_LOGS_PER_TX` "because there could be more than
MAX_PRIVATE_LOGS_PER_TX indexes consumed in case the logs are squashed,"
but the constant was set to `20` (well below `MAX_PRIVATE_LOGS_PER_TX =
64`). The real limit is one full private-tx's worth of unfinalized tag
indexes per chain; aligning the constant with that bound makes the
semantic clearer.

Refs #24388, #24387.

## Testing

- Diff verified: `client_flows/transfers.test.ts` now runs with the
pre-#24387 `expectedExecutionSteps(1 + apps + 1 + recursions)` counts.
- Local unit tests / `bench all` will re-run under CI3 on this branch's
push.
…tracts (#24408)

Stacked on top of #24345. Continues the e2e speedup work, targeting two
levers that cut wasted setup work in the single-node cross-chain suites.

## 1. Skip the token bridge deploy for arbitrary-message cross-chain
tests (A-1185)

`l1_to_l2.parallel` and `l2_to_l1.parallel` only pass arbitrary L1↔L2
messages from their own `TestContract`; they never touch the L2 token,
bridge, or portal — they use the cross-chain harness solely for
`ethAccount` and the L1 client/contract handles. They were nonetheless
paying for the full token+portal+bridge setup (an L1 ERC20 deploy, an L1
portal deploy, two L2 contract deploys, and their init txs) on every
run.

`CrossChainMessagingTest` now takes a `deployTokenBridge` flag (default
`true`). When `false`, it sets up the L1 handles + `ethAccount` and
skips the token bridge entirely. The two suites pass `false` and read
the L1 bits directly off the test object. No coverage lost — neither
suite asserts anything on the token bridge.

## 2. Deploy cross-chain L1 contracts before the node starts, under
automine

When a test needs extra L1 contracts (e.g. the cross-chain token portal
+ ERC20), deploying them after the node is running means paying the L1
block interval per deploy and racing the live sequencer/archiver.
`setup` already mines its own L1 contract deployment under anvil
automine before the node starts; this adds a `deployL1Contracts` hook to
`SetupOptions` that runs in that same window, so a test's L1 deploys
mine instantly and finish before any node exists. The resolved value is
exposed on the context as `l1DeployResult`.

The token-bridge path of `CrossChainMessagingTest` now uses this hook to
deploy the ERC20 + token portal pre-node; `CrossChainTestHarness`
accepts the pre-deployed portal address and skips its own portal deploy
(backward compatible — existing callers that pass nothing still deploy
it). This benefits `token_bridge_private`, `token_bridge_public`, and
`token_bridge_failure_cases`.

## Notes

- The `deployL1Contracts` hook is general-purpose; any suite that
deploys L1 contracts in setup can move them into the automine window.
- A second codex pass over the deferred-speedup catalogue surfaced
further low-risk items (fold the warp into the shared multi-node
`waitForProvenCheckpoint`; audited automine swaps for a few fee tests)
that are left for follow-ups.
BEGIN_COMMIT_OVERRIDE
chore(aztec-nr): add constrained delivery index nullifier test (#24366)
fix(key-store): protect all KeyStore methods with transactionAsync
(#24406)
feat(pxe): add resolveCustomRequest hook for caller-defined requests
(#24409)
refactor(pxe): unify the app-silo primitive for tagging and encryption
(#24402)
feat!: close down access to context member fields (#24417)
feat(pxe): default unconstrained delivery to a handshake (#24387)
feat!: allow passing and retrieving privacy keys in pxe/wallet (#24416)
fix(bench): use address-derived tagging secrets for the client-flows
bench; widen the PXE unfinalized tagging window (#24429)
feat(aztec-nr): let contracts choose the message tag derivation (#24432)
END_COMMIT_OVERRIDE
BEGIN_COMMIT_OVERRIDE
test(e2e): skip token bridge deploy and pre-deploy cross-chain L1
contracts (#24408)
END_COMMIT_OVERRIDE
…failures (#24426)

## Problem

In the docs-examples CI job (`docs/examples/bootstrap.sh execute`),
`example_swap` and `recursive_verification` exhausted all 5 retry
attempts on the same signature during `.send()` fee estimation:

```
Error: An unknown error occurred while executing the contract function "getTimestampForSlot".
  args: (54)   Details: L1 RPC request failed   code: -32702
```

Stack: `sendTx -> completeFeeOptions -> getMinFees ->
aztecNode.getPredictedMinFees / getCurrentMinFees` (over JSON-RPC to the
node). The failing reads are node-side, in
`FeeProviderImpl.computeCurrentMinFees` (`getTimestampForSlot` /
`getManaMinFeeAt`) and `FeePredictor.fetchState`.

Failing run: http://ci.aztec-labs.com/66ef70e6044f13bd (branch
`merge-train/spartan-v5`, commit `0e1827b945`).

## Root cause

`FeeProviderImpl.getCurrentMinFees` and `FeePredictor.getState` cache a
promise keyed on the L1 block number, refreshing only when the block
advances. When the cached computation rejects (a transient `L1 RPC
request failed` blip against the single 1-CPU anvil the docs-examples
job runs), the rejected promise is replayed on every subsequent call at
the same L1 block.

`getCurrentMinFees` was worse: it chained the next computation via
`currentMinFees.then(() => computeCurrentMinFees())`. Once
`currentMinFees` is rejected, `.then(onFulfilled)` propagates the
rejection and never invokes `computeCurrentMinFees` again — so the cache
stays poisoned permanently, regardless of L1 block advancement, until
the node restarts.

Because fee estimation fails before any L1 tx is submitted, no new L1
block is mined by that path, so a single blip permanently wedges
`.send()` on the node. The CI log confirms this: after one transient
failure on `getManaMinFeeAt`, `getTimestampForSlot(54)` failed
identically across all 5 retries of both examples — even though the node
kept mining L1 blocks (148-152) for the examples' own contract deploys
in the same second. This rules out a live node/anvil stall and pinpoints
the poisoned promise cache.

## Fix

- `getCurrentMinFees`: chain off the previous promise's settlement (via
a swallowing `.catch`) rather than its fulfillment, so a prior rejection
no longer short-circuits the new computation; and reset the cached L1
block number when the computation rejects so the next call recomputes at
the same block.
- `getState` (FeePredictor): reset the cached L1 block number on
rejection for the same reason.

Both are idempotent read paths, so recomputing on the next call is safe.

## Testing

- Added unit tests to `fee_provider.test.ts` and `fee_predictor.test.ts`
that fail (red) against the current code and pass (green) with the fix:
a transient rejection followed by a call at the same L1 block must
recompute rather than replay the cached rejection.
- Red/green verified locally: both new tests fail on the unpatched
source and the full `sequencer-client` fee suites pass (13 tests) with
the fix.
- A deterministic docs-examples repro was not feasible (it depends on a
transient L1 RPC blip under CI contention), so the tests target the
poisoning mechanism directly.
…24441)

Fixes a genuine timing flake in
`single-node/proving/proof_fails.parallel.test.ts` › `does not allow
submitting proof after epoch end`, observed in CI run `99ed463db48b9cf2`
on the v5 line (`Expected: 2, Received: 0` at the final epoch
assertion).

This test ran into a situation where the proposer `prune`d the unproven
chain without `propose`ing a new checkpoint, so the check for "has the
last checkpoint been mined in epoch 2" failed, since there was _no
checkpoint at all_ in the entire chain.

This fixes it so we wait until there's actually a checkpoint mined. In
parallel, I'm looking into _why_ the proposer decided to prune without
proposing.
Augments facts exposed from PXE to Aztec.nr with origin block state
(pending | proven | finalized), capped to the anchor block to avoid
"future leaking"

Closes F-715
BEGIN_COMMIT_OVERRIDE
feat(pxe): origin block number timestamp log oracle in log retrieval
(#24398)
END_COMMIT_OVERRIDE
…24442)

Ports #24300 (`feat(avm): lock-free sharded TraceContainer`, commit
`8880be5894`) to `v5-next`.

This is the real fix for the intermittent AVM check-circuit hang that
#24413 currently works around with a per-call timeout + dispose/retry.
Root cause is now fully established from a symbolized core dump of a
wedged `bb-avm` (see Evidence).

## Symptom

`bb-avm` intermittently wedges mid-`checkAvmCircuit` (e.g.
`avm_check_circuit_token.test.ts`): every thread sits idle-blocked in a
futex, 0% CPU, for the whole timeout. Jest's 60s per-test timeout fires
(its `afterAll` then prints the metrics breakdown — which is why the
breakdown appears "after 60s"), and the leaked wedged child keeps the
process alive until the 600s CI timeout (exit 124). It is **not** the
bb/bb.js UDS transport — socket queues are empty and no transport defect
reproduces it.

## Root cause: recursive read-lock deadlock on a per-column
`std::shared_mutex`

`TraceContainer` stores each column behind its own `std::shared_mutex`
(libc++ — writer-preferring, non-recursive). Readers (`get`,
`visit_column`) take `shared_lock`; writers (`set`, `get_column_rows`,
`reserve_column`) take `unique_lock`. During tracegen,
`AvmTraceGenHelper::fill_trace_interactions` (`tracegen_helper.cpp:464`)
dispatches interaction builders via `bb::parallel_for`, so builders run
**concurrently** on the thread pool.

Precise sequence that deadlocks (all confirmed from the core):

1. Builder **A** (`MultiPermutationBuilder::process`) calls
`TraceContainer::visit_column(colX, visitor)`, which takes a
**`shared_lock` on colX** and — *while still holding it* — invokes the
caller-supplied visitor.
2. That visitor (`set_destination_selector`'s lambda) re-enters
`TraceContainer::get(colX)`, requesting a **second, re-entrant
`shared_lock` on the same column**.
3. Concurrently, Builder **B**
(`LookupIntoDynamicTableSequential::process`) calls
`TraceContainer::get_column_rows(colX)` → **`unique_lock`**. libc++
immediately sets `__write_entered_` and waits for the reader count to
reach zero.
4. libc++'s `shared_mutex` is **writer-preferring**: once
`__write_entered_` is set, *new* `lock_shared` acquisitions block.
Builder A's re-entrant `get(colX)` is a new reader, so it blocks.
5. But Builder A still holds its **first** `shared_lock` (from
`visit_column`) and cannot release it — it is blocked inside the
visitor. The reader count therefore never reaches zero, so Builder B's
writer wait never completes, so Builder A's re-entrant read never
unblocks. **Deadlock.**
6. A and B are two of the `fill_trace_interactions` `parallel_for`
worker threads. They never finish their iteration, so the pool's
`complete_` counter stalls (observed `complete_ = 398`, `num_iterations_
= 400`). The pool dispatcher (`main`) waits on `complete_condition_` for
`complete_ == num_iterations_` forever → check-circuit never returns.

## How / when it was introduced

This is a **latent** deadlock in the vm2 AVM tracegen concurrency design
— the combination of (a) per-column `std::shared_mutex`, (b) parallel
builder execution, and (c) `visit_column` invoking a re-entrant visitor.
It is not a recent regression; that design has been in place for a long
time (merge-train squashing obscures the exact originating PR). It only
fires under a specific runtime interleaving — two particular builders
contending on the same column with the re-entrant read racing a writer —
which is why it is rare and load-sensitive.

## Why `next` is fixed but `v5-next` is not

`merge-base(next, v5-next)` is `b82384aa36` (`feat:
merge-train/spartan`, 2026-06-04). #24300 (`8880be5894`) landed on
`next` on **2026-06-25**, i.e. *after* the two lines diverged, so the
`v5-next` / `merge-train/spartan-v5` line never received it. #24300 was
written as a **performance/scalability rework** (remove per-column lock
contention), so it fixed this deadlock **inadvertently** — the commit
message and PR describe it as a lock-free optimization, not a hang fix.
`next` has been quietly immune since; `v5-next` still carries the
deadlock.

## Why the lock-free change resolves it

#24300 replaces the per-column `{ shared_mutex; hash_map }` with a
lock-free sharded design: a fixed array of
`std::atomic<ColumnInterval*>` shards, installed via CAS, each holding a
dense fixed-size row array. `get`, `set`, `visit_column`,
`get_column_rows` take **no lock at all** — concurrent readers and
writers touch atomics and distinct cells. With no lock, a re-entrant
read from a visitor cannot block behind a writer, so the deadlock is
**structurally impossible**, not merely less likely.

## Evidence

Reproduced on `v5-next` (wedged in ~24s under 32-way load); with this
port, 12+ minutes clean. A `gcore` of a wedged `bb-avm`, symbolized
against an unstripped rebuild, shows: `main` in
`fill_trace_interactions` → `parallel_for` → `complete_condition_.wait`;
13 pool workers idle; **2** pool workers blocked on the **same** column
`shared_mutex` — one in `get_column_rows → __shared_mutex_base::lock()`
(writer, on `__gate2_`), one in `visit_column → (visitor) → get →
__shared_mutex_base::lock_shared()` (re-entrant reader, on `__gate1_`) —
matching `complete_ = 398/400` exactly.

## Notes

- Cherry-pick applied cleanly; only `trace_container.{cpp,hpp}` change.
- With this in, #24413's timeout workaround becomes belt-and-suspenders
rather than the sole guard.

---

**Footnote — smells worth reworking (out of scope here):**

1. **Holding a lock across a caller-supplied callback.**
`TraceContainer::visit_column(col, visitor)` invokes an arbitrary
visitor while inside the column's critical section, and visitors do
re-enter the container. That is a re-entrancy/deadlock trap independent
of the locking primitive; the lock-free rewrite dodges it, but the *API
shape* still invites it. Consider snapshotting (collect under the
region, release, then call the visitor) or explicitly
documenting/forbidding re-entrancy.
2. **`std::shared_mutex` is writer-preferring and non-recursive.** Any
path that re-acquires it for read — directly or via a callback — while
already holding it is a latent deadlock. Fine-grained per-column
`shared_mutex` + callback re-entrancy was a landmine waiting for the
right load.
3. **Separate, still-live hazard in `bb::parallel_for_mutex_pool` (not
this bug).** Its nested-call guard was changed from a process-wide
`std::atomic_bool` to a `thread_local bool` (commit `a6526dd`, PR
#17538, incidental to the aztec_process migration). It now only
serializes nested `parallel_for` on the *dispatcher* thread; a
`parallel_for` invoked from a task running on a *worker* thread is
unguarded and will spin up a nested pool. The original global-atomic
guard protected all threads. Worth restoring cross-thread protection (or
explicitly deciding to support nested `parallel_for`).
Tightens up the offchain reception state machine by using block finality
to terminate a processed reception

Closes F-715
The interactions tracegen phase runs every lookup/permutation job via parallel_for (fill_trace_interactions). Lookups that share a destination-selector column can write the same (selector, row) cell from multiple threads, so the previous guarded read-modify-write (get(...) != 1 then set(...)) on that shared cell is a data race (undefined behavior).

Add an opt-in use_atomic_limbs flag to TraceContainer::set that stores the field's four limbs with relaxed atomic writes (4 plain movq on x86-64, no lock -- unlike a whole-field std::atomic_ref<FF>, whose 32 bytes exceed the lock-free width). Use it for the shared selector write and drop the guard read: the write is now an unconditional idempotent atomic store of 1. Because every writer stores the same value, per-limb atomicity is sufficient (no torn value is possible), and the hot non-atomic set() path is unchanged.
…24456 to v5-next) (#24457)

Ports #24456 to `v5-next`.

## What was wrong

The interactions tracegen phase runs every lookup/permutation job
concurrently: `AvmTraceGenHelper::fill_trace_interactions` concatenates
all builders' jobs and dispatches them with `parallel_for`. Lookups
whose fine-grained destination selector is a *shared* column
(`DST_SELECTOR != outer_dst_selector`) can resolve to the same `dst_row`
from different jobs, so multiple threads write the same `(selector,
row)` cell at once.

The previous code did a guarded read-modify-write on that shared cell:

```cpp
if (DST_SELECTOR != outer_dst_selector && trace.get(DST_SELECTOR, dst_row) != 1) {
    trace.set(DST_SELECTOR, dst_row, 1);
}
```

Both the `get` and the non-atomic 32-byte `set` race against the other
threads' writes to the same cell — a data race, i.e. undefined behavior.
In practice it produced the right value (every writer stores `1`), but
it is still UB.

## The fix

Add an opt-in `use_atomic_limbs` flag to `TraceContainer::set`. When
set, the field's four 64-bit limbs are written with **relaxed atomic
stores** — four plain `movq` on x86-64, no lock and no libatomic call.
(A whole-field `std::atomic_ref<FF>` is *not* an option on the hot path:
at 32 bytes it exceeds the hardware lock-free width and falls back to a
locked libatomic call.)

The shared selector write now uses it and drops the guard read:

```cpp
if (DST_SELECTOR != outer_dst_selector) {
    trace.set(DST_SELECTOR, dst_row, 1, /*use_atomic_limbs=*/true);
}
```

The write is an unconditional, idempotent atomic store of `1`. Because
every concurrent writer stores the *same* value, per-limb atomicity is
sufficient — no torn value is possible — so this is data-race-free
without the cost of whole-field atomicity. The default (non-atomic)
`set` hot path is untouched and keeps its sparse-column "zero = absent"
fast path.

## Performance (this PR vs baseline)

Mega bulk AVM tx, full proving, `HARDWARE_CONCURRENCY=16`. Tracegen
stage timings, median of runs (baseline n=3, this PR n=6):

| Stage | baseline | this PR |
|---|---|---|
| tracegen traces | 1,536 ms | 1,537 ms |
| tracegen interactions | 350 ms | 329 ms |
| tracegen all | 1,917 ms | 1,936 ms |

No measurable cost — the safety fix is free. The per-limb atomic only
fires on the shared selector writes in the interactions stage; the
traces stage is byte-for-byte the same hot path as baseline.
BEGIN_COMMIT_OVERRIDE
feat: use fact origin block state in offchain reception (#24292)
END_COMMIT_OVERRIDE
nventuro and others added 22 commits July 14, 2026 22:11
BEGIN_COMMIT_OVERRIDE
feat: exported in-process testing network (#24629)
fix(pxe): widen tracked sender tagging ranges with onchain discovery
evidence (#24655)
END_COMMIT_OVERRIDE
# Conflicts:
#	noir-projects/aztec-nr/aztec/src/standard_addresses.nr
#	noir-projects/noir-contracts/pinned-standard-contracts.tar.gz
#	yarn-project/standard-contracts/src/standard_contract_data.ts
#	yarn-project/txe/esbuild/plugins/size_guard.mjs
Resolves the 4 conflicts from merging origin/v5 into v5-next:

- yarn-project/txe/esbuild/plugins/size_guard.mjs: keep the higher
  totalLimitMiB = 15 (v5), since merging v5's larger bundle content in
  needs the bigger cap; merged both bump-log lines chronologically.

- Generated standard-contract artifacts (kept at the v5-next baseline,
  REGEN REQUIRED before merge):
    - noir-projects/aztec-nr/aztec/src/standard_addresses.nr
    - yarn-project/standard-contracts/src/standard_contract_data.ts
    - noir-projects/noir-contracts/pinned-standard-contracts.tar.gz
  These are generated by 'bootstrap.sh pin-standard-build' + 'yarn
  workspace @aztec/standard-contracts run generate'. The merged tree
  combines v5-next's standard-contract source changes (handshake
  forgery-protection) with v5's aztec-nr + protocol-constants changes,
  so the deterministic addresses match NEITHER committed side and must
  be re-pinned/regenerated from the merged tree. Left at v5-next's
  values as a consistent placeholder; bootstrap drift-check will flag
  this until regenerated.
## Problem

Partial-note discovery has four crash points on the completion path,
each reachable by a malicious sender and each firing before the pending
note advances — so every subsequent sync re-hits it and permanently
freezes note sync for that contract:

- a matched completion log yields no note (`panic`),
- a pending note resolves to more than one completion log (`assert`),
- the delivered private half plus the log's public content exceed the
packed-note capacity (`BoundedVec` overflow in the append), or
- the completion log payload is empty, so reading the storage slot is
out of bounds.

The first three are reachable on the canonical token (mismatched content
over the unconstrained delivery channel, completing the same partial
note twice which the token does not prevent, and an over-length
delivered private half); the last needs an attacker-controlled contract
emitting a tag-only log.

## Fix

Make each non-fatal: warn and advance the FSM rather than panicking, so
one bad message cannot break sync. A completion log that cannot yield a
note (empty, over-length, or matching none) is skipped; more than one
completion log completes with the first.

Fixes F-798
Merges public `v5` into public `v5-next` so ongoing development can
continue on `v5-next` while `v5` stays the release line. Requested in
#engineering.

`v5` was ~887 commits ahead of the merge base and `v5-next` ~31, and the
merge is **not** clean — 4 conflicts. Structured as three commits for
review:

### Commit 1 — raw merge (conflict markers committed)
`Merge remote-tracking branch 'origin/v5'` — the merge commit with the
conflict markers left in place, so you can see exactly what git couldn't
auto-resolve:

- `yarn-project/txe/esbuild/plugins/size_guard.mjs`
- `noir-projects/aztec-nr/aztec/src/standard_addresses.nr` *(generated)*
- `yarn-project/standard-contracts/src/standard_contract_data.ts`
*(generated)*
- `noir-projects/noir-contracts/pinned-standard-contracts.tar.gz`
*(generated, binary)*

### Commit 2 — hand resolution
- **`size_guard.mjs`** — resolved by hand. Kept the higher
`totalLimitMiB = 15` (from `v5`): merging `v5`'s larger bundle content
into `v5-next` needs the bigger cap. Merged both bump-log lines
chronologically.
- The 3 generated standard-contract artifacts were left at the `v5-next`
baseline as a placeholder (correctly flagged by CI: `BBApiException:
verification key has wrong size: expected 5216, got 4576` — the old
pinned VKs don't match `v5`'s bb).

### Commit 3 — regenerated standard-contract artifacts
Ran the real regeneration on the merged tree:
`noir-projects/noir-contracts/bootstrap.sh pin-standard-build` + `yarn
workspace @aztec/standard-contracts run generate`, iterated to a
fixpoint (3 rounds — the standard contracts embed each other's address
stamps via aztec-nr, so re-pinning shifts addresses until they
stabilize). All four standard-contract addresses changed, as expected:
the merged tree combines **v5-next's** handshake forgery-protection
changes with **v5's** `aztec-nr` + protocol-constants + bb (VK format)
changes, so the addresses match neither committed side.

Verified locally on the merged tree: full `noir-contracts` build and
full `yarn-project` build (the CI job that failed) both pass, including
the standard-contracts drift check.

Everything else auto-merged (including `constants.gen.ts` /
`constants.nr` and the handshake contract/aztec-nr source). Opened as a
draft for review per the usual conflict-PR flow.

---
*Created by
[claudebox](https://claudebox.work/v2/sessions/97f178a58b3b68fb) ·
group: `slackbot`*
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
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
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
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.