Skip to content
Merged
Show file tree
Hide file tree
Changes from 28 commits
Commits
Show all changes
62 commits
Select commit Hold shift + click to select a range
93b8be2
test: update proving-real test to mbps (#20991)
alexghr Mar 3, 2026
88ea0a9
chore: epoch proving log analyzer (#21033)
alexghr Mar 3, 2026
abcea4a
chore: update pause script to allow resume (#21032)
alexghr Mar 3, 2026
22a8254
feat: price bump for RPC transaction replacement (#20806)
mrzeszutko Mar 3, 2026
54a5ecd
refactor: remove update checker, retain version checks (#20898)
alexghr Mar 3, 2026
df0235c
fix: (A-592) p2p client proposal tx collector test (#20998)
danielntmd Mar 3, 2026
d77a652
refactor: use publishers-per-pod in deployments (#21039)
alexghr Mar 3, 2026
3058541
chore: web3signer refreshes keystore (#21045)
alexghr Mar 3, 2026
bfa7fa8
feat(sequencer): set block building limits from checkpoint limits (#2…
spalladino Mar 3, 2026
d471d24
chore(e2e): fix e2e bot L1 tx nonce reuse (#21052)
spalladino Mar 3, 2026
85e0888
Merge branch 'next' into merge-train/spartan
Mar 3, 2026
8f3c774
Merge branch 'next' into merge-train/spartan
Mar 3, 2026
dfa95b3
Merge branch 'next' into merge-train/spartan
Mar 3, 2026
7bd1eaa
feat: Update L1 to L2 message APIs (#20913)
PhilWindle Mar 3, 2026
13ae331
fix: (A-589) epochs l1 reorgs test (#20999)
danielntmd Mar 3, 2026
503d0f9
feat(sequencer): add SEQ_MAX_TX_PER_CHECKPOINT config (#21016)
spalladino Mar 3, 2026
6fe5583
Merge branch 'next' into merge-train/spartan
Mar 3, 2026
e786be4
Merge branch 'next' into merge-train/spartan
Mar 3, 2026
0ef2abf
Merge branch 'next' into merge-train/spartan
Mar 3, 2026
09830c0
Merge branch 'next' into merge-train/spartan
Mar 3, 2026
6f71311
Merge branch 'next' into merge-train/spartan
Mar 3, 2026
87f196c
fix: drop --pid=host from docker_isolate
AztecBot Mar 3, 2026
22bcddb
fix: use mktemp for output dirs in acir_tests scripts
AztecBot Mar 4, 2026
9bdfcb6
fix: drop --pid=host from docker_isolate (#21081)
ludamad Mar 4, 2026
2c769a2
Merge branch 'next' into merge-train/spartan
Mar 4, 2026
875bada
Merge branch 'next' into merge-train/spartan
Mar 4, 2026
8f66d2b
Merge branch 'next' into merge-train/spartan
Mar 4, 2026
d734750
Merge branch 'next' into merge-train/spartan
Mar 4, 2026
928846b
feat: standby mode for prover broker (#21098)
mrzeszutko Mar 4, 2026
c2758d8
refactor(p2p): maintain sorted array in tx pool instead of sorting on…
Mar 3, 2026
9be0610
fix(p2p): remove default block handler in favor of block handler (#21…
spalladino Mar 4, 2026
84a36e9
feat(validator): add VALIDATOR_ env vars for independent block limits…
spalladino Mar 4, 2026
445b8a4
refactor(p2p): decouple proposal validators from base class via compo…
spalladino Mar 4, 2026
8f4c48d
feat: additional validation in public setup allowlist (onlySelf + nul…
mrzeszutko Mar 4, 2026
b83e379
Merge branch 'next' into merge-train/spartan
Mar 4, 2026
3d931c7
Merge branch 'next' into merge-train/spartan
Mar 4, 2026
1d2bbde
fix: (A-591) aztecProofSubmissionEpochs incorrectly named as aztecPro…
danielntmd Mar 4, 2026
cfc9780
refactor(sequencer): rename SEQ_GAS_PER_BLOCK_ALLOCATION_MULTIPLIER t…
spalladino Mar 4, 2026
e000681
fix: unbound variable in check_doc_references.sh with set -u (#21126)
AztecBot Mar 4, 2026
c7fba6c
Merge branch 'next' into merge-train/spartan
Mar 4, 2026
11184b0
Merge branch 'next' into merge-train/spartan
Mar 4, 2026
b7d9cd4
feat: calldata length validation of public setup function allowlist (…
mrzeszutko Mar 4, 2026
55ddc15
fix: include mismatched values in tx metadata validation errors
AztecBot Mar 4, 2026
1da690b
Merge branch 'next' into merge-train/spartan
Mar 4, 2026
04143a7
fix: include mismatched values in tx metadata validation errors (#21147)
ludamad Mar 4, 2026
ccbec34
Merge branch 'next' into merge-train/spartan
Mar 5, 2026
bea8dd7
feat: single-node implementation of slash-protection signer
spypsy Mar 5, 2026
c5cc991
feat: single-node implementation of slash-protection signer (#20894)
AztecBot Mar 5, 2026
12495e6
Merge branch 'next' into merge-train/spartan
Mar 5, 2026
b9b4a21
feat: Remove non-protocol contracts from public setup allowlist (#21154)
mrzeszutko Mar 5, 2026
7b016af
chore: More updated Alpha configuration (#21155)
PhilWindle Mar 5, 2026
b0d126b
chore: tally slashing pruning improvements (#21161)
spypsy Mar 5, 2026
215ac2d
fix: update dependencies (#20997)
deffrian Mar 5, 2026
c224f17
Merge branch 'next' into merge-train/spartan
Mar 5, 2026
5497841
fix: omit bigint priceBumpPercentage from IPC config in testbench wor…
AztecBot Mar 5, 2026
09fa4ad
refactor(p2p): (A-588) maintain sorted array in tx pool instead of so…
danielntmd Mar 5, 2026
6a205f7
fix(p2p): report most severe failure in runValidations (#21185)
spalladino Mar 5, 2026
8d93316
fix: use dedicated L1 account for bot bridge resume tests to avoid no…
AztecBot Mar 6, 2026
6c1a355
fix: parse error.message in formatViemError (#21163)
spypsy Mar 6, 2026
145f089
fix: bump lighthouse consensus client v7.1.0 -> v8.0.1 (#21170)
danielntmd Mar 6, 2026
dad460e
chore: code decuplication + refactor (public setup allowlist) (#21200)
mrzeszutko Mar 6, 2026
e388b77
Merge remote-tracking branch 'origin/next' into merge-train/spartan
PhilWindle Mar 6, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 0 additions & 21 deletions .test_patterns.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,27 +56,6 @@ tests:
error_regex: "field_t::range_constraint"
owners:
- *luke
- regex: "barretenberg/acir_tests/scripts/browser_prove.sh"
error_regex: "Failed to fetch"
owners:
- *adam
- regex: "barretenberg/acir_tests/scripts/browser_prove.sh"
error_regex: "RuntimeError: Out of bounds memory access"
owners:
- *adam
- regex: "barretenberg/acir_tests/scripts/browser_prove.sh"
error_regex: "call_indirect to a null table entry"
owners:
- *adam
- regex: "barretenberg/acir_tests/scripts/browser_prove.sh"
error_regex: "Input is not large enough"
owners:
- *adam
# https://gist.github.com/spalladino/4fd3d2abd7b7fb05be2e556649868626
- regex: "barretenberg/acir_tests/scripts/browser_prove.sh"
error_regex: "sending signal TERM to command"
owners:
- *adam
- regex: "barretenberg/cpp/scripts/run_bench.sh wasm bb-micro-bench/wasm/ultra_honk"
error_regex: "Aborted.*core dumped"
owners:
Expand Down
24 changes: 12 additions & 12 deletions barretenberg/acir_tests/scripts/bb_prove.sh
Original file line number Diff line number Diff line change
Expand Up @@ -12,31 +12,31 @@ shift
# Base flags + our commandline args
flags="-v --scheme ultra_honk $*"

mkdir -p output-$$
trap "rm -rf output-$$" EXIT
output_dir=$(mktemp -d ./output-XXXXXX)
trap "rm -rf $output_dir" EXIT

# Tests prefixed with failing_ are expected to fail.
if [[ $test_name == failing_* ]]; then
if $bb write_vk $flags -b target/program.json -o output-$$ && \
$bb prove $flags -b target/program.json -k output-$$/vk -o output-$$ && \
if $bb write_vk $flags -b target/program.json -o $output_dir && \
$bb prove $flags -b target/program.json -k $output_dir/vk -o $output_dir && \
$bb verify $flags \
-k output-$$/vk \
-p output-$$/proof \
-i output-$$/public_inputs; then
-k $output_dir/vk \
-p $output_dir/proof \
-i $output_dir/public_inputs; then
echo "ERROR: Expected test '$test_name' to fail, but it passed!"
exit 1
fi
else
# Generate VK
$bb write_vk $flags -b target/program.json -o output-$$
$bb write_vk $flags -b target/program.json -o $output_dir

# Prove
$bb prove $flags -b target/program.json -k output-$$/vk -o output-$$
$bb prove $flags -b target/program.json -k $output_dir/vk -o $output_dir

# Verify
$bb verify $flags \
-k output-$$/vk \
-p output-$$/proof \
-i output-$$/public_inputs
-k $output_dir/vk \
-p $output_dir/proof \
-i $output_dir/public_inputs
fi

12 changes: 6 additions & 6 deletions barretenberg/acir_tests/scripts/bb_prove_bbjs_verify.sh
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,23 @@ export HARDWARE_CONCURRENCY=8

bb=$(../../../cpp/scripts/find-bb)

mkdir -p output-$$
trap "rm -rf output-$$" EXIT
output_dir=$(mktemp -d ./output-XXXXXX)
trap "rm -rf $output_dir" EXIT

# Generate the VK using BB CLI
$bb write_vk \
--scheme ultra_honk \
-b target/program.json \
-o output-$$
-o $output_dir

# Generate the proof using BB CLI (save as both bytes and fields)
$bb prove \
--scheme ultra_honk \
-b target/program.json \
-w target/witness.gz \
-k output-$$/vk \
-o output-$$
-k $output_dir/vk \
-o $output_dir

# Verify the proof with bb.js classes
node ../../bbjs-test verify \
-d output-$$
-d $output_dir
18 changes: 9 additions & 9 deletions barretenberg/acir_tests/scripts/bb_prove_sol_verify.sh
Original file line number Diff line number Diff line change
Expand Up @@ -30,21 +30,21 @@ else
has_zk="true"
fi

mkdir -p output-$$
trap "rm -rf output-$$" EXIT
output_dir=$(mktemp -d ./output-XXXXXX)
trap "rm -rf $output_dir" EXIT

# Create a proof, write the solidity contract, write the proof as fields in order to extract the public inputs
$bb prove $flags -b target/program.json --oracle_hash keccak --write_vk -o output-$$
$bb verify $flags --oracle_hash keccak -i output-$$/public_inputs -k output-$$/vk -p output-$$/proof
$bb write_solidity_verifier $write_contract_flags -k output-$$/vk -o output-$$/Verifier.sol
$bb prove $flags -b target/program.json --oracle_hash keccak --write_vk -o $output_dir
$bb verify $flags --oracle_hash keccak -i $output_dir/public_inputs -k $output_dir/vk -p $output_dir/proof
$bb write_solidity_verifier $write_contract_flags -k $output_dir/vk -o $output_dir/Verifier.sol

# Use solcjs to compile the generated key contract with the template verifier and test contract
# index.js will start an anvil, on a random port
# Deploy the verifier then send a test transaction
PROOF="output-$$/proof" \
PUBLIC_INPUTS="output-$$/public_inputs" \
VERIFIER_PATH="output-$$/Verifier.sol" \
PROOF="$output_dir/proof" \
PUBLIC_INPUTS="$output_dir/public_inputs" \
VERIFIER_PATH="$output_dir/Verifier.sol" \
TEST_PATH="../../sol-test/HonkTest.sol" \
HAS_ZK="$has_zk" \
TEST_NAME=$(basename output-$$) \
TEST_NAME=$(basename $output_dir) \
node ../../sol-test/src/index.js
12 changes: 6 additions & 6 deletions barretenberg/acir_tests/scripts/bbjs_legacy_cli_prove.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,16 @@ cd ../acir_tests/$1
# NOTE The bb.js main file is deprecated!
bbjs_bin="../../../ts/dest/node/main.js"

mkdir -p output-$$
trap "rm -rf output-$$" EXIT
output_dir=$(mktemp -d ./output-XXXXXX)
trap "rm -rf $output_dir" EXIT

# Generate VK
node $bbjs_bin write_vk_ultra_honk -v -b target/program.json -o output-$$/vk
node $bbjs_bin write_vk_ultra_honk -v -b target/program.json -o $output_dir/vk

# Prove
node $bbjs_bin prove_ultra_honk -o output-$$/proof -v -b target/program.json -k output-$$/vk
node $bbjs_bin prove_ultra_honk -o $output_dir/proof -v -b target/program.json -k $output_dir/vk

# Verify
node $bbjs_bin verify_ultra_honk -v \
-k output-$$/vk \
-p output-$$/proof
-k $output_dir/vk \
-p $output_dir/proof
8 changes: 4 additions & 4 deletions barretenberg/acir_tests/scripts/bbjs_prove.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,16 @@ cd ../acir_tests/$1

export HARDWARE_CONCURRENCY=8

mkdir -p output-$$
trap "rm -rf output-$$" EXIT
output_dir=$(mktemp -d ./output-XXXXXX)
trap "rm -rf $output_dir" EXIT

# Writes the proof, public inputs ./target; this also writes the VK
node ../../bbjs-test prove \
-b target/program.json \
-w target/witness.gz \
-o output-$$ \
-o $output_dir \
--multi-threaded

# Verify the proof by reading the files in ./target
node ../../bbjs-test verify \
-d output-$$
-d $output_dir
12 changes: 6 additions & 6 deletions barretenberg/acir_tests/scripts/bbjs_prove_bb_verify.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,21 @@ export HARDWARE_CONCURRENCY=8

cd ../acir_tests/$1

mkdir -p output-$$
trap "rm -rf output-$$" EXIT
output_dir=$(mktemp -d ./output-XXXXXX)
trap "rm -rf $output_dir" EXIT

# Writes the proof, public inputs ./target; this also writes the VK
node ../../bbjs-test prove \
-b target/program.json \
-w target/witness.gz \
-o output-$$
-o $output_dir

# The proof and public_inputs are already in binary format from bbjs-test

bb=$(../../../cpp/scripts/find-bb)
# Verify the proof with bb cli
$bb verify \
--scheme ultra_honk \
-k output-$$/vk \
-p output-$$/proof \
-i output-$$/public_inputs
-k $output_dir/vk \
-p $output_dir/proof \
-i $output_dir/public_inputs
14 changes: 7 additions & 7 deletions barretenberg/acir_tests/scripts/bbjs_prove_sol_verify.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@ export HARDWARE_CONCURRENCY=8

cd ../acir_tests/$1

mkdir -p output-$$
trap "rm -rf output-$$" EXIT
output_dir=$(mktemp -d ./output-XXXXXX)
trap "rm -rf $output_dir" EXIT

# Generate the proof and VK
node ../../bbjs-test prove \
-b target/program.json \
-w target/witness.gz \
-o output-$$ \
-o $output_dir \
--oracle-hash $oracle_hash

bb=$(../../../cpp/scripts/find-bb)
Expand All @@ -31,12 +31,12 @@ for arg in "$@"; do
done

# Use the BB CLI to write the solidity verifier - this can also be done with bb.js
$bb write_solidity_verifier --scheme ultra_honk -k output-$$/vk -o output-$$/Verifier.sol
$bb write_solidity_verifier --scheme ultra_honk -k $output_dir/vk -o $output_dir/Verifier.sol

# Verify the proof using the solidity verifier
PROOF="output-$$/proof" \
PUBLIC_INPUTS="output-$$/public_inputs" \
VERIFIER_PATH="output-$$/Verifier.sol" \
PROOF="$output_dir/proof" \
PUBLIC_INPUTS="$output_dir/public_inputs" \
VERIFIER_PATH="$output_dir/Verifier.sol" \
TEST_PATH="../../sol-test/HonkTest.sol" \
HAS_ZK="$has_zk" \
TEST_NAME=$(basename $(realpath .)) \
Expand Down
Binary file not shown.
Binary file not shown.
2 changes: 0 additions & 2 deletions ci3/docker_isolate
Original file line number Diff line number Diff line change
Expand Up @@ -55,15 +55,13 @@ fi
# Launch the container in the background.
# Don't launch in the foreground or you can't process SIGINT/SIGTERM.
# Don't use & as we want to block, and be sure it starts before processing any signals.
# We use --pid=host so that we can rely on $$ in bash scripts being (temporally) unique.
set -x
cid=$(docker run -d \
${name_arg:-} \
${network_arg:-} \
${cpuset_arg:-} \
--cpus=$CPUS \
--memory=$MEM \
--pid=host \
--user $(id -u):$(id -g) \
-v$HOME:$HOME \
$tmp_mount \
Expand Down
40 changes: 40 additions & 0 deletions docs/docs-operate/operators/reference/changelog/v4.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,31 @@ The `getL2Tips()` RPC endpoint now returns a restructured response with addition
- Replace `tips.latest` with `tips.proposed`
- For `checkpointed`, `proven`, and `finalized` tips, access block info via `.block` (e.g., `tips.proven.block.number`)

### Block gas limits reworked

The byte-based block size limit has been removed and replaced with field-based blob limits and automatic gas budget computation from L1 rollup limits.

**Removed:**

```bash
--maxBlockSizeInBytes <value> ($SEQ_MAX_BLOCK_SIZE_IN_BYTES)
```

**Changed to optional (now auto-computed from L1 if not set):**

```bash
--maxL2BlockGas <value> ($SEQ_MAX_L2_BLOCK_GAS)
--maxDABlockGas <value> ($SEQ_MAX_DA_BLOCK_GAS)
```

**New:**

```bash
--gasPerBlockAllocationMultiplier <value> ($SEQ_GAS_PER_BLOCK_ALLOCATION_MULTIPLIER)
```

**Migration**: Remove `SEQ_MAX_BLOCK_SIZE_IN_BYTES` from your configuration. Per-block L2 and DA gas budgets are now derived automatically as `(checkpointLimit / maxBlocks) * multiplier`, where the multiplier defaults to 2. You can still override `SEQ_MAX_L2_BLOCK_GAS` and `SEQ_MAX_DA_BLOCK_GAS` explicitly, but they will be capped at the checkpoint-level limits.

### Setup phase allow list requires function selectors

The transaction setup phase allow list now enforces function selectors, restricting which specific functions can run during setup on whitelisted contracts. Previously, any public function on a whitelisted contract or class was permitted.
Expand Down Expand Up @@ -117,11 +142,13 @@ This replaces the previous hardcoded default and allows network operators to set
Node operators can now update validator attester keys, coinbase, and fee recipient without restarting the node by calling the new `reloadKeystore` admin RPC endpoint.

What is updated on reload:

- Validator attester keys (add, remove, or replace)
- Coinbase and fee recipient per validator
- Publisher-to-validator mapping

What is NOT updated (requires restart):

- L1 publisher signers
- Prover keys
- HA signer connections
Expand All @@ -133,6 +160,7 @@ New validators must use a publisher key already initialized at startup. Reload i
The admin JSON-RPC endpoint now supports auto-generated API key authentication.

**Behavior:**

- A cryptographically secure API key is auto-generated at first startup and displayed once via stdout
- Only the SHA-256 hash is persisted to `<dataDirectory>/admin/api_key_hash`
- The key is reused across restarts when `--data-directory` is set
Expand Down Expand Up @@ -161,6 +189,18 @@ Transaction submission via RPC now returns structured rejection codes when a tra

**Impact**: Improved developer experience — callers can now programmatically handle specific rejection reasons.

### RPC transaction replacement price bump

Transactions submitted via RPC that clash on nullifiers with existing pool transactions must now pay at least X% more in priority fee to replace them. The same bump applies when the pool is full and the incoming tx needs to evict the lowest-priority tx. P2P gossip behavior is unchanged.

**Configuration:**

```bash
P2P_RPC_PRICE_BUMP_PERCENTAGE=10 # default: 10 (percent)
```

Set to `0` to disable the percentage-based bump (still requires strictly higher fee).

### Setup allow list extendable via network config

The setup phase allow list can now be extended via the network configuration JSON (`txPublicSetupAllowListExtend` field). This allows network operators to distribute additional allowed setup functions to all nodes without requiring code changes. The local environment variable takes precedence over the network-json value.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1064,7 +1064,7 @@ pub global GAS_ESTIMATION_DA_GAS_LIMIT: u32 =
GAS_ESTIMATION_TEARDOWN_DA_GAS_LIMIT + MAX_PROCESSABLE_DA_GAS_PER_CHECKPOINT;

// Default gas limits. Users should use gas estimation, or they will overpay gas fees.
// TODO: consider moving to typescript
// TODO: These are overridden in typescript-land. Remove them from here.
pub global DEFAULT_TEARDOWN_L2_GAS_LIMIT: u32 = 1_000_000; // Arbitrary default number.
pub global DEFAULT_L2_GAS_LIMIT: u32 = MAX_PROCESSABLE_L2_GAS; // Arbitrary default number.
pub global DEFAULT_TEARDOWN_DA_GAS_LIMIT: u32 = MAX_PROCESSABLE_DA_GAS_PER_CHECKPOINT / 2; // Arbitrary default number.
Expand Down
19 changes: 12 additions & 7 deletions spartan/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ The main entry point is `terraform/deploy-aztec-infra/`:
**aztec-validator** (extends aztec-node):
- Wrapper chart with `aztec-node` as dependency (aliased as `validator`)
- Adds validator-specific ConfigMap (`env.configmap.yaml`)
- Configures mnemonic, validators-per-node, publisher keys
- Configures mnemonic, validators-per-node, publishers-per-replica

**aztec-prover-stack**:
- Multi-component: prover node, broker, and agent replicas
Expand Down Expand Up @@ -263,26 +263,31 @@ locals {

**Key derivation via Terraform + `setup-attester-keystore.sh`:**

Each release receives a different `PUBLISHER_KEY_INDEX_START` from Terraform:
Publishers are allocated **per replica (pod)**, not per attester key. Each release receives a different `PUBLISHER_KEY_INDEX_START` from Terraform:

```hcl
# In main.tf custom_settings per release:
"validator.node.env.PUBLISHER_KEY_INDEX_START" = var.VALIDATOR_PUBLISHER_MNEMONIC_START_INDEX +
(idx * (var.VALIDATORS_PER_NODE * var.VALIDATOR_PUBLISHERS_PER_VALIDATOR_KEY * var.VALIDATOR_REPLICAS))
(idx * (var.VALIDATOR_PUBLISHERS_PER_REPLICA * var.VALIDATOR_REPLICAS))
```

Example with 4 replicas, 12 validators/node, 2 publishers/key, base index 5000:
Example with 4 replicas, 4 publishers/replica, base index 5000:
- Primary (idx=0): `PUBLISHER_KEY_INDEX_START = 5000`
- HA-1 (idx=1): `PUBLISHER_KEY_INDEX_START = 5000 + (1 * 12 * 2 * 4) = 5096`
- HA-1 (idx=1): `PUBLISHER_KEY_INDEX_START = 5000 + (1 * 4 * 4) = 5016`

At runtime, `setup-attester-keystore.sh` calculates publisher indices:

```bash
# POD_INDEX extracted from pod name (validator-0 → 0, validator-1 → 1, etc.)
PUBLISHER_KEY_INDEX=$((POD_INDEX * VALIDATORS_PER_NODE * PUBLISHERS_PER_VALIDATOR_KEY + PUBLISHER_KEY_INDEX_START))
PUBLISHER_KEY_INDEX=$((POD_INDEX * VALIDATOR_PUBLISHERS_PER_REPLICA + PUBLISHER_KEY_INDEX_START))
```

This ensures each release uses non-overlapping publisher key ranges.
The keystore uses **schema v2** with a top-level `publisher` array shared by all validators on the pod:
```json
{"schemaVersion": 2, "publisher": ["0x1", "0x2", "0x3", "0x4"], "validators": [{"attester": "..."}]}
```

This ensures each release uses non-overlapping publisher key ranges while decoupling publisher count from attester count.

**HA coordination:**
- Both releases connect to shared PostgreSQL via `VALIDATOR_HA_DATABASE_URL`
Expand Down
Loading