Skip to content

feat: merge-train/spartan#20899

Merged
AztecBot merged 64 commits intonextfrom
merge-train/spartan
Mar 3, 2026
Merged

feat: merge-train/spartan#20899
AztecBot merged 64 commits intonextfrom
merge-train/spartan

Conversation

@AztecBot
Copy link
Collaborator

@AztecBot AztecBot commented Feb 26, 2026

BEGIN_COMMIT_OVERRIDE
fix: track last seen nonce in case of stale fallback L1 RPC node (#20855)
feat: Validate num txs in block proposals (#20850)
fix(archiver): enforce checkpoint boundary on rollbackTo (#20908)
fix: tps zero metrics (#20656)
fix: handle scientific notation in bigintConfigHelper (#20929)
feat(aztec): node enters standby mode on genesis root mismatch (#20938)
fix: logging of class instances (#20807)
feat(slasher): make slash grace period relative to rollup upgrade time (#20942)
chore: add script to find PRs to backport (#20956)
chore: remove unused prover-node dep (#20955)
fix: increase minFeePadding in e2e_bot bridge resume tests and harden GasFees.mul() (#20962)
feat(sequencer): (A-526) rotate publishers when send fails (#20888)
chore: (A-554) bump reth version 1.6.0 -> 1.11.1 for eth devnet (#20889)
chore: metric on how many epochs validator has been on committee (#20967)
fix: set wallet minFeePadding in BotFactory constructor (#20992)
chore: deflake epoch invalidate block test (#21001)
chore(sequencer): e2e tests for invalid signature recovery in checkpoint attestations (#20971)
chore: deflake duplicate proposals and attestations (#20990)
chore: deflake epochs mbps test (#21003)
feat: reenable function selectors in txPublicSetupAllowList (#20909)
fix: limit offenses when voting in tally slashing mode by slashMaxPayloadSize (#20683)
fix(spartan): wire SEQ_L1_PUBLISHING_TIME_ALLOWANCE_IN_SLOT env var (#21017)
END_COMMIT_OVERRIDE

AztecBot and others added 11 commits February 26, 2026 15:14
This PR introduces additional block/checkpoint proposal validation to
ensure `SEQ_MAX_TX_PER_BLOCK` is not breached. Also adds additional
tests for existing validation criteria.

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
## Summary
- Reject `rollbackTo` calls targeting a block that is not the last block
of its checkpoint, since rollback operates at checkpoint granularity.
The error message indicates the nearest valid checkpoint boundaries so
the caller can retry with a correct target.
- Fix proven checkpoint number to roll back to the target checkpoint
instead of resetting to zero.

Fixes A-552

## Test plan
- Unit tests added in `archiver-store.test.ts` covering:
  - Rejection of non-boundary blocks with correct error message
- Successful rollback to checkpoint boundary with sync point
verification
  - Proven checkpoint rollback when target is before proven block
  - Proven checkpoint preservation when target is after proven block
- e2e test `e2e_epochs/manual_rollback` passes

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

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Ref: A-493
- Split `peerCount` metric into total connections and healthy
connections
- Fixed memory leak in peer manager: `peerConnectedAt` map entries are
now deleted after recording connection duration
  - Fixed mined-delay tracking for `TxPoolV2`
  - Added attestation pool instrumentation
  - Renamed metrics/groups
  - Other zero metric fixes
Copy link
Collaborator

@ludamad ludamad left a comment

Choose a reason for hiding this comment

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

🤖 Auto-approved

@AztecBot AztecBot added this pull request to the merge queue Feb 26, 2026
@AztecBot
Copy link
Collaborator Author

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

@github-merge-queue github-merge-queue bot removed this pull request from the merge queue due to failed status checks Feb 26, 2026
AztecBot and others added 11 commits February 27, 2026 02:01
BigInt() doesn't accept scientific notation strings like "1e+23" or
"2E+23". This caused a crash when starting a node with --network
testnet, as config values like 200000e18 get converted to "2e+23" by
the JSON pipeline.

Parse scientific notation losslessly using bigint arithmetic instead
of going through float64, preserving exact values.
## Summary

- Fixes `bigintConfigHelper` to handle scientific notation strings (e.g.
`1e+23`, `2E+23`) that `BigInt()` can't parse directly
- Uses lossless bigint arithmetic to parse scientific notation instead
of going through float64
- Fixes crash when starting a node with `--network testnet` where config
values like `200000e18` get converted to `2e+23` by JSON

## Context

The bug surfaces because `network-defaults.yml` values like `200000e18`
pass through `yq -o json | jq`, which converts them to float64
scientific notation (`2E+23`). At runtime, `BigInt("2e+23")` throws
`SyntaxError: Cannot convert 2e+23 to a BigInt`.

This fix makes the parsing layer resilient to scientific notation,
regardless of how the values arrive.

## Test plan

- Added unit tests covering: plain integers, scientific notation
(`1e+23`, `2E+23`, `1e23`, `5e18`), decimal mantissa (`1.5e10`), empty
string default, and error on non-integer results (`1e-3`)
- All 10 test cases verified passing

[ClaudeBox log](http://ci.aztec-labs.com/e312c3dcef56fc86-1)
Closes A-547

Forward-port of #20937.

## Summary

- Before calling `getL1Config()`, the node now compares its local
genesis archive root against the canonical rollup's via `archiveAt(0)`.
- If the roots don't match (e.g. during L1 contract upgrades where the
old rollup ABI is incompatible), the node enters **standby mode**
instead of crashing.
- In standby mode, a lightweight HTTP server is started for K8s liveness
probes, and the node polls every 10 minutes until a compatible rollup
becomes canonical.
- When `rollupVersion` is not explicitly configured (`undefined`), it
now falls back to `'canonical'` instead of passing `0` to the registry.

### Tested against testnet (incompatible rollup):

```
Genesis archive root: 0x2727683df35594b1f073a681532520056ca8a775398c8b5a94574c67ef1ce6de
Genesis root mismatch: expected 0x2727683df35594b1f073a681532520056ca8a775398c8b5a94574c67ef1ce6de, got 0x0ae6138310f7b877b6c68856451eddec0873fb63f9033931cedcdc46815729e1 from rollup at 0x66a41cb55f9a1e38a45a2ac8685f12a61fbfab77. Entering standby mode. Will poll every 10s for a compatible rollup...
Standby status server listening on port 8080
Still waiting. Rollup at 0x66a41cb55f9a1e38a45a2ac8685f12a61fbfab77 has genesis root 0x0ae6138310f7b877b6c68856451eddec0873fb63f9033931cedcdc46815729e1.
```

```
$ curl http://localhost:8080/status
OK
```

## Test plan

- Run `aztec start --node --archiver --network testnet` against an
incompatible rollup: verify standby mode with health check responding OK
- Run against a compatible network: verify normal startup without
entering standby
- Ctrl+C during standby: verify clean exit
#20942)

## Summary

Closes A-596

- Anchors `SLASH_GRACE_PERIOD_L2_SLOTS` to the `CanonicalRollupUpdated`
event emitted by the Registry when the rollup becomes canonical, instead
of comparing against genesis slot 0
- Adds `getCanonicalRollupRegistrationTimestamp()` to `RegistryContract`
to query the registration event timestamp
- Computes and caches the canonical rollup registration L2 slot in the
slasher factory, threading it through to the `SlashOffensesCollector`
where the grace period comparison now uses `offenseSlot < registeredSlot
+ gracePeriodSlots`

## Motivation

The rollup can be deployed weeks/months before becoming canonical
(registered via `addRollup()`). By upgrade time, the current slot is far
past any "slot from genesis" value, making the grace period ineffective.
Now operators just set a duration (e.g. 3600 slots = 3 days) and the
system anchors it to the actual upgrade time automatically.

## Test plan

- Unit tests updated and passing for `SlashOffensesCollector`,
`EmpireSlasherClient`, and `TallySlasherClient`
- Grace period test verifies offenses within `registeredSlot +
gracePeriod` are skipped and offenses after are not
@AztecBot AztecBot enabled auto-merge February 27, 2026 22:03
@AztecBot
Copy link
Collaborator Author

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

@AztecBot AztecBot added this pull request to the merge queue Mar 2, 2026
@github-merge-queue github-merge-queue bot removed this pull request from the merge queue due to failed status checks Mar 2, 2026
AztecBot and others added 18 commits March 2, 2026 11:06
## Summary
- Sets `wallet.setMinFeePadding(config.minFeePadding)` in the
`BotFactory` constructor so that all transactions during bot setup
(token deployment, minting) use the configured fee padding instead of
the wallet's default (0.5).
- Previously, only account deployment explicitly used
`config.minFeePadding` (line 226 of factory.ts), while token deploy and
minting relied on the wallet default, causing insufficient fee headroom
when gas prices escalate during rapid block building in tests.

## Root cause
The merge-train/spartan PR (#20899) was dequeued because
`e2e_bot.test.ts` ("does not reuse prior bridge claims if recipient
address changes") failed with:
```
maxFeesPerGas.feePerL2Gas must be greater than or equal to gasFees.feePerL2Gas,
but got maxFeesPerGas.feePerL2Gas=698400000 and gasFees.feePerL2Gas=932700000
```
The test config had `minFeePadding: 99` (100x multiplier), but this was
only applied during account deployment, not during subsequent token
deployment/minting which used the wallet's default 1.5x multiplier.

## Test plan
- [x] `make yarn-project` builds successfully
- [ ] CI e2e_bot test passes with the fix

[ClaudeBox log](http://ci.aztec-labs.com/e9fdbc1ddac8e593-1)
## Summary

- Fix flaky "proposer invalidates previous block with shuffled
attestations" e2e test that fails with
`ValidatorSelection__InvalidCommitteeCommitment`
- Only swap two signed attestation positions in
`manipulateAttestations`, preserving the bitmap so
`MaliciousCommitteeAttestationsAndSigners` signers stay aligned with
L1's `reconstructCommitteeFromSigners`

## Root cause

`trimAttestations` reduces signed attestations to the minimum required
(4 of 5), leaving one position with only an address (no signature). The
old swap formula `(proposerIndex+1)%N, (proposerIndex+2)%N` could swap a
signed position with an unsigned one, changing the bitmap pattern.
`MaliciousCommitteeAttestationsAndSigners` provides the `_signers` array
in the original bitmap order, but L1's `reconstructCommitteeFromSigners`
maps signers to the new bitmap positions, producing a mismatched
committee commitment hash. Whether the swap crosses a signed/unsigned
boundary depends on the proposer index and which attestation was
trimmed, explaining the flakiness.

## Test plan

- Confirmed the bug reproduces deterministically by forcing a
signed/unsigned swap (test times out with 15
`InvalidCommitteeCommitment` reverts)
- After fix, test passes cleanly with zero committee commitment errors

Fixes A-590
…int attestations (#20971)

Test that high-s or invalid ECDSA signatures posted by a malicious
proposer can be properly invalidated.

## Summary
- Adds e2e tests for A-71: nodes correctly detect and invalidate
checkpoints with malleable (high-s) or unrecoverable attestation
signatures
- Adds `injectHighSValueAttestation` and
`injectUnrecoverableSignatureAttestation` sequencer config options for
testing
- Updates `Signature.random()` to produce valid ECDSA signatures with
low s-values
- Adds `generateRecoverableSignature` / `generateUnrecoverableSignature`
utilities with unit tests
- Adds unit test for high-s value attestation validation in archiver
- Refactors e2e invalidate block tests with shared helpers to reduce
duplication

Fixes A-71

## Test plan
- [x] Two new e2e tests pass: high-s value attestation invalidation,
unrecoverable signature invalidation
- [x] Unit tests pass: `Signature.random()` validity,
`generateRecoverableSignature`, `generateUnrecoverableSignature`, high-s
validation
- [x] Existing e2e invalidate block tests unchanged in behavior
(refactored with helpers)

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

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Fix flakiness in duplicate_proposal_slash and
duplicate_attestation_slash e2e tests.

Both tests flake at a 3-13% rate because the malicious proposer (1 of 4
validators) is never selected within the timeout window. Three root
causes are addressed:

1. **GossipSub mesh checked only for TX topic** — The tests need block
proposals and checkpoint proposals to propagate, but
`waitForP2PMeshConnectivity` only verified the `tx` topic mesh. Added a
`topics` parameter so callers can specify which topics to wait on. Both
slash tests now wait on `tx`, `block_proposal`, and
`checkpoint_proposal`.

2. **Proposer selection is probabilistic** — With 4 validators and 2
slots/epoch, the malicious proposer has ~25% chance per slot. Added
`awaitEpochWithProposer` helper that advances epochs (via L1 time warp)
until the target proposer is deterministically selected for at least one
slot in the current epoch.

3. **Race between node startup and first proposal** — Nodes started
sequencing immediately upon creation, potentially proposing before the
P2P mesh was ready. Now all nodes are created with `dontStartSequencer:
true`, and sequencers are started simultaneously only after mesh
formation, committee existence, and epoch advancement are confirmed.

Fixes A-593
Fixes A-595
## Summary

- Fixes flaky `epochs_mbps.parallel` test by adding a `retryUntil` poll
to `assertMultipleBlocksPerSlot`, closing a race condition between two
independently-syncing archivers

## Details

The `epochs_mbps.parallel` test has been flaking in CI (9 recent
failures across PRs 20562-20868) on the "checkpointed block" test case.
The root cause is a race condition:

1. `waitForTx` polls the **initial setup node's** archiver and returns
when it sees the tx as `CHECKPOINTED`.
2. `assertMultipleBlocksPerSlot` then queries the **first validator
node's** (`nodes[0]`) archiver via `archiver.getCheckpoints()`.
3. These are different nodes with independent L1 polling cycles (~50ms
interval each).
4. The first validator's archiver may not have indexed the latest
checkpoint yet (~200-400ms race window).

CI logs confirm: the checkpoint with the expected block count is always
produced and published to L1, but the first validator's archiver hasn't
indexed it when the assertion runs.

### Fix

Added a `retryUntil` poll at the start of `assertMultipleBlocksPerSlot`
that waits (up to `L2_SLOT_DURATION_IN_S * 3` = 108s, polling every
0.5s) for `nodes[0]`'s archiver to index a checkpoint with at least
`targetBlockCount` blocks. Once found, the existing validation logic
runs as before.

Fixes A-594
## Summary

Re-enables function selector checking in the transaction setup phase
allow list. Previously, selector restrictions were removed with the
comment "We can't restrict the selector because public functions get
routed via dispatch," but the current code already correctly extracts
selectors from calldata (`calldata[0]` contains the target selector).
This fix closes a vulnerability where ANY public function on whitelisted
contracts/classes was permitted during setup.

- **Made `AllowedElement` require selectors**: Removed `AllowedInstance`
and `AllowedClass` variants — all entries now require both an identifier
(address or classId) and a function selector
- **Re-enabled selectors in the default allow list** with the five
traced setup-phase functions: `AuthRegistry._set_authorized` (private
FPC path), `AuthRegistry.set_authorized` (public FPC path),
`FeeJuice._increase_public_balance`, `Token._increase_public_balance`,
`Token.transfer_in_public`
- **Removed the unnecessary FPC entry** — FPC's public functions
(`_complete_refund`, `_pay_refund`) are set via `set_as_teardown()`, not
enqueued in setup
- **Changed config from override to extend**: The internal config key is
now `txPublicSetupAllowListExtend` (env var `TX_PUBLIC_SETUP_ALLOWLIST`
unchanged). Defaults are always present; the config only adds entries on
top of them
- **Added network-json support**: The extend list can be distributed via
`txPublicSetupAllowListExtend` in the network config schema
- **Replaced `isOnAllowList` with `checkAllowList`**: Returns a specific
rejection reason instead of a boolean. Removed dead branches for
selector-less entries. Contract instance is now fetched lazily (only
once, only when class-based entries exist). Unknown contracts now return
`TX_ERROR_SETUP_FUNCTION_UNKNOWN_CONTRACT` instead of a generic
validation error
- **Improved `parseAllowList`**: Requires selectors, rejects unknown
type prefixes, handles whitespace
- **Added tests**: Wrong-selector rejection for both address and class
matches, unknown contract rejection, lazy fetch verification, config
parsing edge cases

Fixes A-463
So it is respected when we deploy a network.
@AztecBot
Copy link
Collaborator Author

AztecBot commented Mar 3, 2026

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

@AztecBot AztecBot added this pull request to the merge queue Mar 3, 2026
Merged via the queue into next with commit 3cdddf6 Mar 3, 2026
20 checks passed
johnathan79717 pushed a commit that referenced this pull request Mar 4, 2026
## Summary
- Sets `wallet.setMinFeePadding(config.minFeePadding)` in the
`BotFactory` constructor so that all transactions during bot setup
(token deployment, minting) use the configured fee padding instead of
the wallet's default (0.5).
- Previously, only account deployment explicitly used
`config.minFeePadding` (line 226 of factory.ts), while token deploy and
minting relied on the wallet default, causing insufficient fee headroom
when gas prices escalate during rapid block building in tests.

## Root cause
The merge-train/spartan PR (#20899) was dequeued because
`e2e_bot.test.ts` ("does not reuse prior bridge claims if recipient
address changes") failed with:
```
maxFeesPerGas.feePerL2Gas must be greater than or equal to gasFees.feePerL2Gas,
but got maxFeesPerGas.feePerL2Gas=698400000 and gasFees.feePerL2Gas=932700000
```
The test config had `minFeePadding: 99` (100x multiplier), but this was
only applied during account deployment, not during subsequent token
deployment/minting which used the wallet's default 1.5x multiplier.

## Test plan
- [x] `make yarn-project` builds successfully
- [ ] CI e2e_bot test passes with the fix

[ClaudeBox log](http://ci.aztec-labs.com/e9fdbc1ddac8e593-1)
AztecBot added a commit that referenced this pull request Mar 5, 2026
## Summary
- Sets `wallet.setMinFeePadding(config.minFeePadding)` in the
`BotFactory` constructor so that all transactions during bot setup
(token deployment, minting) use the configured fee padding instead of
the wallet's default (0.5).
- Previously, only account deployment explicitly used
`config.minFeePadding` (line 226 of factory.ts), while token deploy and
minting relied on the wallet default, causing insufficient fee headroom
when gas prices escalate during rapid block building in tests.

## Root cause
The merge-train/spartan PR (#20899) was dequeued because
`e2e_bot.test.ts` ("does not reuse prior bridge claims if recipient
address changes") failed with:
```
maxFeesPerGas.feePerL2Gas must be greater than or equal to gasFees.feePerL2Gas,
but got maxFeesPerGas.feePerL2Gas=698400000 and gasFees.feePerL2Gas=932700000
```
The test config had `minFeePadding: 99` (100x multiplier), but this was
only applied during account deployment, not during subsequent token
deployment/minting which used the wallet's default 1.5x multiplier.

## Test plan
- [x] `make yarn-project` builds successfully
- [ ] CI e2e_bot test passes with the fix

[ClaudeBox log](http://ci.aztec-labs.com/e9fdbc1ddac8e593-1)
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.

10 participants