Skip to content

feat: merge-train/spartan#21784

Merged
ludamad merged 62 commits into
nextfrom
merge-train/spartan
Mar 20, 2026
Merged

feat: merge-train/spartan#21784
ludamad merged 62 commits into
nextfrom
merge-train/spartan

Conversation

@AztecBot

@AztecBot AztecBot commented Mar 19, 2026

Copy link
Copy Markdown
Collaborator

BEGIN_COMMIT_OVERRIDE
feat(p2p): add tx validator for contract instance deployment addresses (#21771)
fix: always deploy IRM for testnet (#21755)
fix: avoid mutating caller's array via splice in snapshot sync (A-718) (#21759)
chore: update network logs skill (#21785)
feat(archiver): validate contract instance addresses before storing (#21787)
fix: ensure no division by 0 (#21786)
feat: support private fork releases via ci-release (#21778)
fix: restrict scenario deployments to only nightly (#21798)
fix(stdlib): zero-pad bufferFromFields when declared length exceeds payload (#21802)
test(protocol-contracts): verify max-size bytecode fits in contract class log (#21818)
fix: wire BOT_DA_GAS_LIMIT through helm/terraform for staging-public (#21809)
fix: remove jest-mock-extended from worker processes + fix parallelize_strict silent failures (#21821)
fix(archiver): throw on duplicate contract class or instance additions (#21799)
chore: remove broadcasted function events (#21805)
fix: sync dateProvider from anvil stdout on every mined block (#21829)
fix(sequencer): use wall-clock time instead of L1 block timestamp for slot estimation (#21769)
fix: use correct EthCheatCodes method name in epochs_missed_l1_slot test (#21848)
feat(p2p): add tx validator for contract class id verification (#21788)
feat: publisher funding (#21631)
feat: batch chonk verifier TS integration (#21823)
fix(sequencer): remove l1 block timestamp check (#21853)
fix: use local IVC inputs for batch_verifier bench test (#21857)
fix(p2p): centralize gossipsub penalization and fix inconsistencies (#21863)
chore: publish GitHub releases to AztecProtocol/barretenberg (#21775)
END_COMMIT_OVERRIDE

deffrian and others added 17 commits March 16, 2026 11:15
Use chunk + asyncPool to replace splice-based batching in
BrokerCircuitProverFacade. Both getAllCompletedJobs and job retrieval
now use non-mutating patterns with bounded concurrency.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Validates that contract instance deployment logs contain correct addresses
by recomputing the address from the event fields and rejecting mismatches.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
#21771)

## Motivation

When a contract instance is deployed, a `ContractInstancePublishedEvent`
is emitted as a private log containing the contract address and all
fields needed to recompute it. Currently the archiver blindly trusts
these addresses. A malicious or buggy client could submit a tx with an
incorrect address, poisoning the archiver's contract data.

## Approach

Added a new stateless tx validator that extracts
`ContractInstancePublishedEvent` logs from a tx's private logs,
recomputes the contract address via
`computeContractAddressFromInstance`, and rejects the tx if the claimed
address doesn't match. The validator is wired into all tx entry points
(gossip, JSON-RPC, req/resp, and block proposals).

## Changes

- **stdlib**: Added `TX_ERROR_INCORRECT_CONTRACT_ADDRESS` and
`TX_ERROR_MALFORMED_CONTRACT_INSTANCE_LOG` error constants
- **p2p**: New `ContractInstanceTxValidator` that validates contract
instance deployment addresses in private logs
- **p2p**: Wired the validator into all three entry-point factory
functions (gossip stage 1, req/resp + block proposals, JSON-RPC)
- **p2p (tests)**: Unit tests for the new validator (correct address,
wrong address, malformed log, no instance logs) and updated factory
tests
PhilWindle and others added 10 commits March 19, 2026 11:05
#21759)

## Summary
- Replace `splice(0, N)` with index-based `slice(i, i+N)` in
`BrokerCircuitProverFacade` to avoid mutating the caller's `ids` array
during snapshot sync batching.

Fixes A-718

## Test plan
- Build passes
- The fix is mechanical: replacing mutating `splice(0, N)` with
non-mutating `slice(i, i+N)` in a for-loop

🤖 Generated with [Claude Code](https://claude.com/claude-code)
Adds a defense-in-depth check in the archiver's data store updater that
recomputes contract instance addresses from their fields before storing
them, filtering out any instances with mismatched addresses.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…21787)

## Motivation

The archiver blindly stores contract instance data extracted from
private logs without verifying that the claimed address matches the
instance fields. While the p2p tx validator (#21771) catches this at the
network layer, blocks received via req/resp or proposals skip that
check. This adds a second line of defense at the storage layer.

## Approach

Before storing contract instances, the archiver now recomputes the
address from the instance fields via
`computeContractAddressFromInstance` and filters out any with mismatched
addresses. The check is skipped during delete operations since we need
to remove instances regardless.

## Changes

- **archiver**: Validate contract instance addresses in
`updateDeployedContractInstances` before storing, filtering out
mismatches with a warning log
- **stdlib (tests)**: Add elapsed timing to
`computeContractAddressFromInstance` test (~1.3ms per call)
Replace silent overwrites with explicit errors when adding a contract
class or instance that already exists in the store. This catches
unexpected double-adds that could lead to data loss on rollback.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When REF_NAME has a 'private' semver prerelease (e.g.
v5.0.0-private.20260318), ci-release fetches the matching tag from
aztec-packages-private, creates a worktree, and runs the release from
there. Cache uploads are suppressed to avoid leaking private source
artifacts.

Also locks down ci3-external.yml to use github.token instead of
AZTEC_BOT_GITHUB_TOKEN.
@ludamad ludamad enabled auto-merge March 20, 2026 01:33
AztecBot and others added 11 commits March 20, 2026 02:00
Adds TypeScript integration for the batch chonk verifier C++ service:

- BatchChonkVerifier: TS orchestrator managing bb subprocess, FIFO pipe,
  and proof lifecycle (peer path with batching)
- FifoFrameReader: length-delimited frame reader for named pipes
- TestCircuitVerifier used for both peer and RPC in fake-proof mode
- BBCircuitVerifier + QueuedIVCVerifier for RPC path (real proofs)
- Server splits proofVerifier into peerProofVerifier + rpcProofVerifier

Config changes:
- numConcurrentIVCVerifiers -> bbRpcVerifyConcurrency + bbPeerVerifyBatchSize
- bbIVCConcurrency -> bbBatchVerifyThreads
- QueuedIVCVerifier takes (verifier, concurrency) instead of (config, verifier)

Integration tests and queue robustness tests via bb.js bindings.
…proposals

When the dateProvider wall clock is ahead of the latest mined L1 block,
getNextL1SlotTimestamp can return a timestamp in a future L2 slot. The
canProposeAt simulation passes (with time override) but the actual tx
lands in an L1 block still in the previous slot, causing silent rejection.

Floor the computed timestamp with latestBlock.timestamp + slotDuration
to ensure we never target a slot beyond what the next L1 block can reach.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
#21799)

## Motivation

The contract instance store used `.set()` which silently overwrote
existing entries, while the contract class store used
`.setIfNotExists()` which silently ignored duplicates. Neither behavior
catches the unexpected case of a double-add, which could lead to data
loss on rollback — if an instance is added at block N and again at block
M, rolling back block M would delete the instance entirely, invalidating
the first add. The protocol prevents this via deployer nullifiers, but
the store should enforce it as defense-in-depth.

## Approach

Both `addContractInstance` and `addContractClass` now check for existing
entries and throw if the key already exists. This surfaces any
unexpected double-adds as errors rather than silently corrupting state.

## Changes

- **archiver (contract_instance_store)**: `addContractInstance` checks
`hasAsync` before writing; throws with a descriptive error on duplicate
- **archiver (contract_class_store)**: `addContractClass` replaces
`setIfNotExists` with explicit `hasAsync` check and throw on duplicate
- **archiver (tests)**: Updated "add twice" tests to expect throws
instead of silent success
## Motivation

The `PrivateFunctionBroadcastedEvent` and
`UtilityFunctionBroadcastedEvent` were removed from the
ContractClassRegistry Noir contract, making all supporting TypeScript
infrastructure dead code. This cleans it up.

## Approach

Removed the event types, membership proof creation and validation
functions, the archiver data flow that processed these events
(`storeBroadcastedIndividualFunctions`), the store methods that
persisted them (`addFunctions`), and the
`privateFunctions`/`utilityFunctions` fields from `ContractClassPublic`.

Bumps the `ARCHIVER_DB_VERSION` to account for the schema change in the
store.

## Changes

- **stdlib**: Removed `ExecutablePrivateFunctionWithMembershipProof`,
`UtilityFunctionWithMembershipProof`, and related types from
`ContractClassPublic`. Deleted membership proof files and their tests.
- **protocol-contracts**: Deleted `PrivateFunctionBroadcastedEvent` and
`UtilityFunctionBroadcastedEvent` classes, their tests, snapshots, and
hex fixtures.
- **archiver**: Removed `storeBroadcastedIndividualFunctions` from the
data store updater, `addFunctions` from the contract class store, and
related serialization helpers and tests.
- **txe, end-to-end**: Removed references to the deleted fields.
Anvil logs block timestamps to stdout on each mined block. Parse these
and update the TestDateProvider so it stays in lockstep with the L1
chain, eliminating drift between wall clock and anvil chain time.

Also rename cheatCodes.timestamp() to lastBlockTimestamp() to clarify
it returns the latest block's discrete timestamp, not the current time.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Anvil logs block timestamps to stdout on each mined block. Parse these
and update the TestDateProvider so it stays in lockstep with the L1
chain, eliminating drift between wall clock and anvil chain time.

Also rename cheatCodes.timestamp() to lastBlockTimestamp() to clarify it
returns the latest block's discrete timestamp, not the current time.
… slot estimation (#21769)

## Motivation

When an Ethereum slot is missed (no block produced), the next L1 block
lands 24+ seconds after the previous one instead of 12. Computing the
next block timestamp as `getBlock().timestamp + ethereumSlotDuration`
produces wrong results, causing incorrect L2 slot, committee, and
proposer calculations. Additionally, the archiver's sync check would
block the sequencer from building when L1 blocks were delayed, even if
the previous checkpoint had already been synced.

Fixes #14766

## Approach

Replace all `getBlock().timestamp + ethereumSlotDuration` patterns with
a new `getNextL1SlotTimestamp(dateProvider.nowInSeconds(), constants)`
helper that rounds wall-clock time up to the next L1 slot boundary. This
is correct regardless of missed slots. Additionally,
`getSyncedL2SlotNumber` now uses the latest synced checkpoint slot as a
second signal (alongside L1 block timestamps) to determine sync
progress, so the sequencer can start building even when L1 blocks are
delayed.

## Changes

- **stdlib**: Added `getNextL1SlotTimestamp` helper in epoch-helpers
that computes the next L1 slot boundary from wall-clock time
- **ethereum**: Renamed `canProposeAtNextEthBlock` to `canProposeAt` on
`RollupContract`, now accepts a precomputed timestamp instead of
computing it internally
- **sequencer-client**: Injected `DateProvider` into
`GlobalVariableBuilder` (now receives it as first arg + public client as
second arg instead of creating its own). Publisher and global variable
builder use `getNextL1SlotTimestamp` instead of `getBlock().timestamp`
- **epoch-cache**: Updated `getEpochAndSlotInNextL1Slot` to use
`getNextL1SlotTimestamp`
- **archiver**: `getSyncedL2SlotNumber` now returns the max of
L1-timestamp-based sync and checkpoint-based sync, handling missed L1
blocks via the inbox LAG mechanism
- **end-to-end (tests)**: Added `epochs_missed_l1_slot` e2e test that
pauses L1 mining after a checkpoint and verifies the sequencer still
builds during the pause
…est (#21848)

## Summary
Fix TypeScript compilation error in the new
`epochs_missed_l1_slot.test.ts` introduced by #21769.

The test calls `eth.timestamp()` but the method on `EthCheatCodes` is
actually `lastBlockTimestamp()`. This caused `yarn tsgo -b
--emitDeclarationOnly` to fail with:
```
error TS2339: Property 'timestamp' does not exist on type 'EthCheatCodes'.
```

## Fix
Changed `eth.timestamp()` → `eth.lastBlockTimestamp()` on line 104 of
the test file.

ClaudeBox log: https://claudebox.work/s/e62ab0794047aa8c?run=1
## Motivation

Contract class registration events contain a class ID alongside the
fields needed to recompute it (artifactHash, privateFunctionsRoot,
packed bytecode). This adds a validation to avoid a malicious tx from
registering a class with a mismatched ID if it found a bug in the
registry contract, poisoning the archiver's contract data.

## Approach

Made `toContractClassPublic()` a simple synchronous conversion with no
validation (symmetric with how `toContractInstance()` works for contract
instances). Validation is done explicitly at each call site that needs
it: the `DataTxValidator` and the archiver's
`updatePublishedContractClasses`.

## Changes

- **protocol-contracts**: `toContractClassPublic()` is now sync and
returns `ContractClassPublic` without validation or bytecode commitment;
`toContractClassPublicWithBytecodeCommitment()` adds the commitment but
also does not validate
- **stdlib**: Added `TX_ERROR_INCORRECT_CONTRACT_CLASS_ID` and
`TX_ERROR_MALFORMED_CONTRACT_CLASS_LOG` error constants; added elapsed
timing test for `computeContractClassId` with real artifacts
- **p2p**: Extended `DataTxValidator` with explicit contract class ID
verification (recomputes class ID from event fields and compares)
- **p2p (tests)**: Added contract class ID validation tests to
`data_validator.test.ts`; updated factory tests
- **archiver**: `updatePublishedContractClasses` explicitly validates
class IDs and collects bytecode commitments in a single pass;
`addContractClasses` now takes `ContractClassPublicWithCommitment[]`
instead of separate arrays
- **simulator**: Simplified `addContractClassesFromEvents` and callers
from async to sync since `toContractClassPublic()` is no longer async

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
PhilWindle and others added 9 commits March 20, 2026 15:24
Was needed due to clock drift between sequencer and anvil, but PR #21829
should now remove the need for that.
Ref: A-16

- Add automatic publisher funding: when a publisher's ETH balance drops
below a configurable threshold, a dedicated funding account tops it up
- Funding account is configured via the keystore's fundingAccount field
and controlled by PUBLISHER_FUNDING_THRESHOLD / PUBLISHER_FUNDING_AMOUNT
env vars
…ough terraform

- BB_CHONK_VERIFY_BATCH_CONCURRENCY default 0 (auto) → 6 to leave cores
  for the rest of the node on recommended 8-core machines
- Wire BB_CHONK_VERIFY_MAX_BATCH and BB_CHONK_VERIFY_BATCH_CONCURRENCY
  through terraform variables.tf and main.tf to all 5 node types
Adds `BatchChonkVerifier`, the TS integration for the C++ batch chonk
verifier service. Proofs arriving over P2P are batched so a single IPA
SRS MSM covers the entire batch, with bisection to isolate bad proofs on
failure.

The node now runs two separate verification paths:
- **Peer**: `BatchChonkVerifier` — persistent bb subprocess, FIFO pipe,
batched IPA
- **RPC**: `BBCircuitVerifier` wrapped in `QueuedIVCVerifier` —
file-based, concurrency-limited, with OTel metrics
- **Test mode**: `TestCircuitVerifier` for both (always valid)

```
BB_CHONK_VERIFY_MAX_BATCH=16        # peer: proofs per batch (see note below)
BB_CHONK_VERIFY_BATCH_CONCURRENCY=0 # peer: worker threads (0=auto)
BB_NUM_IVC_VERIFIERS=8              # rpc: max concurrent verifications
BB_IVC_CONCURRENCY=1                # rpc: threads per verification
```

> Default batch size of 16 chosen because at 4 cores, a full batch of 16
e2e app proofs verifies in ~245ms — roughly the latency budget for P2P
gossip validation in a busy block. Single-proof latency is ~80ms at 4
cores.

**[Interactive review
page](https://aztecprotocol.github.io/dev-pages/batch-chonk-verifier.html)**
with real benchmark data, work-stealing simulation, and bisection
animation.

New files: `BatchChonkVerifier`, `FifoFrameReader`, integration tests,
queue robustness tests, bench (wired into `bootstrap.sh`).

`QueuedIVCVerifier` constructor simplified from `(config, verifier)` to
`(verifier, concurrency)`.
Was needed due to clock drift between sequencer and anvil, but PR #21829
should now remove the need for that.
## Summary
The `batch_verifier.bench.test.ts` benchmark (added in #21823) was
downloading pinned IVC inputs from S3 at test runtime, but bench tests
run in Docker containers with `--net=none` (no network), causing all 7
tests to fail.

Instead of adding network access, this uses the pre-generated
`example-app-ivc-inputs-out` folder that's already built by
`end-to-end/bootstrap.sh build_bench` — the same pattern used by the IVC
flow benchmarks in `barretenberg/cpp/scripts/ci_benchmark_ivc_flows.sh`.

## Changes
- `batch_verifier.bench.test.ts`: Replace S3 download with local
`../end-to-end/example-app-ivc-inputs-out` path
- `yarn-project/bootstrap.sh`: Keep original bench command (no `NET=1`
needed)

## CI Log
http://ci.aztec-labs.com/fafcdc0ea9a5d52b
…21863)

## Summary

Gossipsub message validation had double-penalization paths: inner
validation functions called `penalizePeer` directly, and the outer
`validateReceivedMessage` wrapper could penalize again on errors.
Attestation cap exceeded was also inconsistently handled (ignored
instead of rejected like proposals).

## Approach

Centralized all gossipsub penalization into `validateReceivedMessage` by
adding a `severity` field to the `Reject` variant of
`ReceivedMessageValidationResult`. Inner functions now return severity
instead of calling `penalizePeer` directly. Added `tryDeserialize`
helper for graceful deserialization failure handling.

## Changes

- **p2p (libp2p_service)**: Centralized penalization in
`validateReceivedMessage`, removed direct `penalizePeer` calls from
`handleGossipedTx`, `validateAndStoreBlockProposal`,
`validateAndStoreCheckpointProposal`, and
`validateAndStoreCheckpointAttestation`. Changed attestation cap
exceeded from `Ignore` to `Reject` with `HighToleranceError`.

Fixes A-705

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
## Summary
- Move automatic GitHub release creation from
`AztecProtocol/aztec-packages` to `AztecProtocol/barretenberg` — bb
artifacts are the only reason we need programmatic releases
- Update all bb artifact download URLs (`barretenberg-rs build.rs`, rust
bootstrap) to point to `AztecProtocol/barretenberg` releases
- `bbup` tries `AztecProtocol/barretenberg` first, falls back to
`aztec-packages` indefinitely
- Users can still create `aztec-packages` releases manually via the
GitHub "Create a release" button when needed

---------

Co-authored-by: ludamad <adam.domurad@gmail.com>
@ludamad ludamad added this pull request to the merge queue Mar 20, 2026
Merged via the queue into next with commit d4316a7 Mar 20, 2026
23 checks passed
@ludamad ludamad deleted the merge-train/spartan branch March 20, 2026 22:34
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.

8 participants