Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Binary file not shown.
Binary file not shown.
28 changes: 28 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
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.
Copy link
Contributor Author

@spalladino spalladino Feb 27, 2026

Choose a reason for hiding this comment

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

I didn't delete them in this PR because I was scared of triggering a change in a vk

Copy link
Contributor

Choose a reason for hiding this comment

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

If they are not used in any circuit / protocol contract, it shouldn't trigger a vk change

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
9 changes: 5 additions & 4 deletions yarn-project/aztec-node/src/aztec-node/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -271,10 +271,11 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
config.l1Contracts = { ...config.l1Contracts, ...l1ContractsAddresses };

const rollupContract = new RollupContract(publicClient, config.l1Contracts.rollupAddress.toString());
const [l1GenesisTime, slotDuration, rollupVersionFromRollup] = await Promise.all([
const [l1GenesisTime, slotDuration, rollupVersionFromRollup, rollupManaLimit] = await Promise.all([
rollupContract.getL1GenesisTime(),
rollupContract.getSlotDuration(),
rollupContract.getVersion(),
rollupContract.getManaLimit().then(Number),
] as const);

config.rollupVersion ??= Number(rollupVersionFromRollup);
Expand Down Expand Up @@ -347,7 +348,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {

// Create FullNodeCheckpointsBuilder for block proposal handling and tx validation
const validatorCheckpointsBuilder = new FullNodeCheckpointsBuilder(
{ ...config, l1GenesisTime, slotDuration: Number(slotDuration) },
{ ...config, l1GenesisTime, slotDuration: Number(slotDuration), rollupManaLimit },
worldStateSynchronizer,
archiver,
dateProvider,
Expand Down Expand Up @@ -484,7 +485,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {

// Create and start the sequencer client
const checkpointsBuilder = new CheckpointsBuilder(
{ ...config, l1GenesisTime, slotDuration: Number(slotDuration) },
{ ...config, l1GenesisTime, slotDuration: Number(slotDuration), rollupManaLimit },
worldStateSynchronizer,
archiver,
dateProvider,
Expand Down Expand Up @@ -1275,7 +1276,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
const processor = publicProcessorFactory.create(merkleTreeFork, newGlobalVariables, config);

// REFACTOR: Consider merging ProcessReturnValues into ProcessedTx
const [processedTxs, failedTxs, _usedTxs, returns, _blobFields, debugLogs] = await processor.process([tx]);
const [processedTxs, failedTxs, _usedTxs, returns, debugLogs] = await processor.process([tx]);
// REFACTOR: Consider returning the error rather than throwing
if (failedTxs.length) {
this.log.warn(`Simulated tx ${txHash} fails: ${failedTxs[0].error}`, { txHash });
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { DEFAULT_L2_GAS_LIMIT, MAX_PROCESSABLE_DA_GAS_PER_CHECKPOINT } from '@aztec/constants';
import { Fr } from '@aztec/foundation/curves/bn254';
import { EthAddress } from '@aztec/foundation/eth-address';
import { AvmTestContractArtifact } from '@aztec/noir-test-contracts.js/AvmTest';
import { AztecAddress } from '@aztec/stdlib/aztec-address';
import type { ContractInstanceWithAddress } from '@aztec/stdlib/contract';
import { Gas } from '@aztec/stdlib/gas';
import { L2ToL1Message, ScopedL2ToL1Message } from '@aztec/stdlib/messaging';
import { NativeWorldStateService } from '@aztec/world-state';

Expand Down Expand Up @@ -187,9 +189,14 @@ describe('AVM check-circuit – unhappy paths 3', () => {
it(
'a nested exceptional halt is recovered from in caller',
async () => {
// The contract requires >200k DA gas (it allocates da_gas_left - 200_000 to the nested call).
// Use a higher DA gas limit than the default since DEFAULT_DA_GAS_LIMIT is ~196k.
const gasLimits = new Gas(MAX_PROCESSABLE_DA_GAS_PER_CHECKPOINT, DEFAULT_L2_GAS_LIMIT);
await tester.simProveVerifyAppLogic(
{ address: avmTestContractInstance.address, fnName: 'external_call_to_divide_by_zero_recovers', args: [] },
/*expectRevert=*/ false,
/*txLabel=*/ 'unlabeledTx',
gasLimits,
);
},
TIMEOUT,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
import type { PublicTxResult } from '@aztec/simulator/server';
import { AvmCircuitInputs, AvmCircuitPublicInputs, PublicSimulatorConfig } from '@aztec/stdlib/avm';
import { AztecAddress } from '@aztec/stdlib/aztec-address';
import type { Gas } from '@aztec/stdlib/gas';
import type { MerkleTreeWriteOperations } from '@aztec/stdlib/interfaces/server';
import type { GlobalVariables } from '@aztec/stdlib/tx';
import { NativeWorldStateService } from '@aztec/world-state';
Expand Down Expand Up @@ -211,6 +212,7 @@ export class AvmProvingTester extends PublicTxSimulationTester {
privateInsertions?: TestPrivateInsertions,
txLabel: string = 'unlabeledTx',
disableRevertCheck: boolean = false,
gasLimits?: Gas,
): Promise<PublicTxResult> {
const simTimer = new Timer();
const simRes = await this.simulateTx(
Expand All @@ -221,6 +223,7 @@ export class AvmProvingTester extends PublicTxSimulationTester {
feePayer,
privateInsertions,
txLabel,
gasLimits,
);
const simDuration = simTimer.ms();
this.logger.info(`Simulation took ${simDuration} ms for tx ${txLabel}`);
Expand All @@ -247,6 +250,7 @@ export class AvmProvingTester extends PublicTxSimulationTester {
teardownCall?: TestEnqueuedCall,
feePayer?: AztecAddress,
privateInsertions?: TestPrivateInsertions,
gasLimits?: Gas,
) {
return await this.simProveVerify(
sender,
Expand All @@ -258,13 +262,15 @@ export class AvmProvingTester extends PublicTxSimulationTester {
privateInsertions,
txLabel,
true,
gasLimits,
);
}

public async simProveVerifyAppLogic(
appCall: TestEnqueuedCall,
expectRevert?: boolean,
txLabel: string = 'unlabeledTx',
gasLimits?: Gas,
) {
await this.simProveVerify(
/*sender=*/ AztecAddress.fromNumber(42),
Expand All @@ -275,6 +281,8 @@ export class AvmProvingTester extends PublicTxSimulationTester {
/*feePayer=*/ undefined,
/*privateInsertions=*/ undefined,
txLabel,
/*disableRevertCheck=*/ false,
gasLimits,
);
}
}
20 changes: 15 additions & 5 deletions yarn-project/constants/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import {
GENESIS_BLOCK_HEADER_HASH as GENESIS_BLOCK_HEADER_HASH_BIGINT,
INITIAL_CHECKPOINT_NUMBER as INITIAL_CHECKPOINT_NUM_RAW,
INITIAL_L2_BLOCK_NUM as INITIAL_L2_BLOCK_NUM_RAW,
MAX_PROCESSABLE_DA_GAS_PER_CHECKPOINT,
MAX_PROCESSABLE_L2_GAS,
} from './constants.gen.js';

// Typescript-land-only constants
Expand All @@ -17,16 +19,24 @@ export const SPONSORED_FPC_SALT = BigInt(0);
export * from './constants.gen.js';

/** The initial L2 block number (typed as BlockNumber). This is the first block number in the Aztec L2 chain. */
// Shadow the export from constants.gen above
// eslint-disable-next-line import-x/export
export const INITIAL_L2_BLOCK_NUM: BlockNumber = BlockNumber(INITIAL_L2_BLOCK_NUM_RAW);

/** The initial L2 checkpoint number (typed as CheckpointNumber). This is the first checkpoint number in the Aztec L2 chain. */
// Shadow the export from constants.gen above

export const INITIAL_L2_CHECKPOINT_NUM: CheckpointNumber = CheckpointNumber(INITIAL_CHECKPOINT_NUM_RAW);
// eslint-disable-next-line import-x/export
export const INITIAL_CHECKPOINT_NUMBER: CheckpointNumber = CheckpointNumber(INITIAL_CHECKPOINT_NUM_RAW);

/** The block header hash for the genesis block 0. */
// Shadow the export from constants.gen above
// eslint-disable-next-line import-x/export
export const GENESIS_BLOCK_HEADER_HASH = new Fr(GENESIS_BLOCK_HEADER_HASH_BIGINT);

// Override the default gas limits set in noir-protocol-circuit constants with saner ones
// Note that these values are not used in noir-land and are only for use in TypeScript code, so we can set them to whatever we want.
// eslint-disable-next-line import-x/export
export const DEFAULT_L2_GAS_LIMIT = MAX_PROCESSABLE_L2_GAS;
// eslint-disable-next-line import-x/export
export const DEFAULT_TEARDOWN_L2_GAS_LIMIT = DEFAULT_L2_GAS_LIMIT / 8;
// eslint-disable-next-line import-x/export
export const DEFAULT_DA_GAS_LIMIT = MAX_PROCESSABLE_DA_GAS_PER_CHECKPOINT / 4;
// eslint-disable-next-line import-x/export
export const DEFAULT_TEARDOWN_DA_GAS_LIMIT = DEFAULT_DA_GAS_LIMIT / 2;
Original file line number Diff line number Diff line change
Expand Up @@ -450,7 +450,7 @@ describe('L1Publisher integration', () => {
const checkpoint = await buildCheckpoint(globalVariables, txs, currentL1ToL2Messages);
const block = checkpoint.blocks[0];

const totalManaUsed = txs.reduce((acc, tx) => acc.add(new Fr(tx.gasUsed.totalGas.l2Gas)), Fr.ZERO);
const totalManaUsed = txs.reduce((acc, tx) => acc.add(new Fr(tx.gasUsed.billedGas.l2Gas)), Fr.ZERO);
expect(totalManaUsed.toBigInt()).toEqual(block.header.totalManaUsed.toBigInt());

prevHeader = block.header;
Expand Down
2 changes: 1 addition & 1 deletion yarn-project/foundation/src/config/env_var.ts
Original file line number Diff line number Diff line change
Expand Up @@ -200,12 +200,12 @@ export type EnvVar =
| 'SENTINEL_ENABLED'
| 'SENTINEL_HISTORY_LENGTH_IN_EPOCHS'
| 'SENTINEL_HISTORIC_PROVEN_PERFORMANCE_LENGTH_IN_EPOCHS'
| 'SEQ_MAX_BLOCK_SIZE_IN_BYTES'
| 'SEQ_MAX_TX_PER_BLOCK'
| 'SEQ_MIN_TX_PER_BLOCK'
| 'SEQ_PUBLISH_TXS_WITH_PROPOSALS'
| 'SEQ_MAX_DA_BLOCK_GAS'
| 'SEQ_MAX_L2_BLOCK_GAS'
| 'SEQ_GAS_PER_BLOCK_ALLOCATION_MULTIPLIER'
| 'SEQ_PUBLISHER_PRIVATE_KEY'
| 'SEQ_PUBLISHER_PRIVATE_KEYS'
| 'SEQ_PUBLISHER_ADDRESSES'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,10 @@ export class LightweightCheckpointBuilder {
return this.blocks.length;
}

public getBlocks() {
return this.blocks;
}

/**
* Adds a new block to the checkpoint. The tx effects must have already been inserted into the db if
* this is called after tx processing, if that's not the case, then set `insertTxsEffects` to true.
Expand Down
6 changes: 3 additions & 3 deletions yarn-project/prover-node/src/job/epoch-proving-job.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ describe('epoch-proving-job', () => {
publicProcessor.process.mockImplementation(async txs => {
const txsArray = await toArray(txs);
const processedTxs = await Promise.all(txsArray.map(tx => mock<ProcessedTx>({ hash: tx.getTxHash() })));
return [processedTxs, [], txsArray, [], 0, []];
return [processedTxs, [], txsArray, [], []];
});
});

Expand Down Expand Up @@ -179,7 +179,7 @@ describe('epoch-proving-job', () => {
publicProcessor.process.mockImplementation(async txs => {
const txsArray = await toArray(txs);
const errors = txsArray.map(tx => ({ error: new Error('Failed to process tx'), tx }));
return [[], errors, [], [], 0, []];
return [[], errors, [], [], []];
});

const job = createJob();
Expand All @@ -190,7 +190,7 @@ describe('epoch-proving-job', () => {
});

it('fails if does not process all txs for a block', async () => {
publicProcessor.process.mockImplementation(_txs => Promise.resolve([[], [], [], [], 0, []]));
publicProcessor.process.mockImplementation(_txs => Promise.resolve([[], [], [], [], []]));

const job = createJob();
await job.run();
Expand Down
Loading
Loading