@aztec/validator-client 0.0.1-commit.3469e52 → 0.0.1-commit.381b1a9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/README.md +64 -19
  2. package/dest/block_proposal_handler.d.ts +9 -10
  3. package/dest/block_proposal_handler.d.ts.map +1 -1
  4. package/dest/block_proposal_handler.js +129 -82
  5. package/dest/checkpoint_builder.d.ts +23 -13
  6. package/dest/checkpoint_builder.d.ts.map +1 -1
  7. package/dest/checkpoint_builder.js +127 -46
  8. package/dest/config.d.ts +1 -1
  9. package/dest/config.d.ts.map +1 -1
  10. package/dest/config.js +28 -6
  11. package/dest/duties/validation_service.d.ts +2 -2
  12. package/dest/duties/validation_service.d.ts.map +1 -1
  13. package/dest/duties/validation_service.js +6 -12
  14. package/dest/factory.d.ts +1 -1
  15. package/dest/factory.d.ts.map +1 -1
  16. package/dest/factory.js +2 -1
  17. package/dest/index.d.ts +1 -2
  18. package/dest/index.d.ts.map +1 -1
  19. package/dest/index.js +0 -1
  20. package/dest/key_store/ha_key_store.d.ts +1 -1
  21. package/dest/key_store/ha_key_store.d.ts.map +1 -1
  22. package/dest/key_store/ha_key_store.js +3 -3
  23. package/dest/metrics.d.ts +12 -3
  24. package/dest/metrics.d.ts.map +1 -1
  25. package/dest/metrics.js +46 -5
  26. package/dest/validator.d.ts +40 -14
  27. package/dest/validator.d.ts.map +1 -1
  28. package/dest/validator.js +211 -55
  29. package/package.json +19 -17
  30. package/src/block_proposal_handler.ts +159 -109
  31. package/src/checkpoint_builder.ts +172 -52
  32. package/src/config.ts +28 -6
  33. package/src/duties/validation_service.ts +12 -11
  34. package/src/factory.ts +1 -0
  35. package/src/index.ts +0 -1
  36. package/src/key_store/ha_key_store.ts +3 -3
  37. package/src/metrics.ts +63 -6
  38. package/src/validator.ts +270 -68
  39. package/dest/tx_validator/index.d.ts +0 -3
  40. package/dest/tx_validator/index.d.ts.map +0 -1
  41. package/dest/tx_validator/index.js +0 -2
  42. package/dest/tx_validator/nullifier_cache.d.ts +0 -14
  43. package/dest/tx_validator/nullifier_cache.d.ts.map +0 -1
  44. package/dest/tx_validator/nullifier_cache.js +0 -24
  45. package/dest/tx_validator/tx_validator_factory.d.ts +0 -18
  46. package/dest/tx_validator/tx_validator_factory.d.ts.map +0 -1
  47. package/dest/tx_validator/tx_validator_factory.js +0 -54
  48. package/src/tx_validator/index.ts +0 -2
  49. package/src/tx_validator/nullifier_cache.ts +0 -30
  50. package/src/tx_validator/tx_validator_factory.ts +0 -135
package/README.md CHANGED
@@ -77,6 +77,8 @@ These rules must always hold:
77
77
  2. **Global variables match within checkpoint**: All blocks within the same checkpoint must have identical global variables (except `blockNumber`), which includes the slot number
78
78
  3. **inHash is constant**: All blocks in a checkpoint share the same L1-to-L2 messages hash
79
79
  4. **Sequential indexWithinCheckpoint**: Block N must have `indexWithinCheckpoint = parent.indexWithinCheckpoint + 1`
80
+ 5. **One proposer per slot**: Each slot has exactly one designated proposer. Sending multiple proposals for the same position (slot, indexWithinCheckpoint) with different content is equivocation and slashable
81
+ 6. **One attestation per slot**: Validators should only attest to one checkpoint per slot. Attesting to different proposals (different archives) for the same slot is equivocation and slashable
80
82
 
81
83
  ## Validation Flow
82
84
 
@@ -87,15 +89,14 @@ When a `BlockProposal` is received via P2P, the `BlockProposalHandler` performs:
87
89
  ```
88
90
  1. Verify proposer signature
89
91
  2. Check proposal is from current/next slot proposer (via BlockProposalValidator)
90
- 3. Find parent block by archive root (wait/retry if not synced)
91
- 4. Compute checkpoint number from parent
92
- 5. If indexWithinCheckpoint > 0:
93
- - Validate global variables match parent (chainId, version, slotNumber,
94
- timestamp, coinbase, feeRecipient, gasFees)
95
- 6. Verify inHash matches computed from L1-to-L2 messages
96
- 7. Collect transactions from pool/network/proposal
97
- 8. Re-execute transactions (if enabled)
98
- 9. Compare re-execution result with proposal
92
+ 3. Detect duplicate proposals (same slot + indexWithinCheckpoint, different archive) slashing proposer on equivocation
93
+ 4. Find parent block by archive root (wait/retry if not synced)
94
+ 5. Compute checkpoint number from parent
95
+ 6. If indexWithinCheckpoint > 0, then validate global variables match parent (chainId, version, slotNumber, timestamp, coinbase, feeRecipient, gasFees)
96
+ 7. Verify inHash matches computed from L1-to-L2 messages
97
+ 8. Collect transactions from pool/network/proposal
98
+ 9. Re-execute transactions (if enabled)
99
+ 10. Compare re-execution result with proposal
99
100
  ```
100
101
 
101
102
  ### Checkpoint Proposal Validation
@@ -155,15 +156,17 @@ Time | Proposer | Validator
155
156
 
156
157
  ## Configuration
157
158
 
158
- | Flag | Purpose |
159
- | ------------------------------------- | --------------------------------------------------------------------- |
160
- | `validatorReexecute` | Re-execute transactions to verify proposals |
161
- | `fishermanMode` | Validate proposals but don't broadcast attestations (monitoring only) |
162
- | `alwaysReexecuteBlockProposals` | Force re-execution even when not in committee |
163
- | `slashBroadcastedInvalidBlockPenalty` | Penalty amount for invalid proposals (0 = disabled) |
164
- | `validatorReexecuteDeadlineMs` | Time reserved at end of slot for propagation/publishing |
165
- | `attestationPollingIntervalMs` | How often to poll for attestations when collecting |
166
- | `disabledValidators` | Validator addresses to exclude from duties |
159
+ | Flag | Purpose |
160
+ | ------------------------------------- | -------------------------------------------------------------------------------------- |
161
+ | `validatorReexecute` | Re-execute transactions to verify proposals |
162
+ | `fishermanMode` | Validate proposals but don't broadcast attestations (monitoring only) |
163
+ | `alwaysReexecuteBlockProposals` | Force re-execution even when not in committee |
164
+ | `slashBroadcastedInvalidBlockPenalty` | Penalty amount for invalid proposals (0 = disabled) |
165
+ | `slashDuplicateProposalPenalty` | Penalty amount for duplicate proposals (0 = disabled) |
166
+ | `slashDuplicateAttestationPenalty` | Penalty amount for duplicate attestations (0 = disabled) |
167
+ | `validatorReexecuteDeadlineMs` | Time reserved at end of slot for propagation/publishing |
168
+ | `attestationPollingIntervalMs` | How often to poll for attestations when collecting |
169
+ | `disabledValidators` | Validator addresses to exclude from duties |
167
170
 
168
171
  ### High Availability (HA) Keystore
169
172
 
@@ -220,6 +223,48 @@ This is useful for monitoring network health without participating in consensus.
220
223
  - `createCheckpointProposal(...)` → `CheckpointProposal`: Signs checkpoint proposal
221
224
  - `attestToCheckpointProposal(proposal, attestors)` → `CheckpointAttestation[]`: Creates attestations for given addresses
222
225
 
226
+ ## Block Building Limits
227
+
228
+ L1 enforces gas and blob capacity per checkpoint. The node enforces these during block building to avoid L1 rejection. Three dimensions are metered: L2 gas (mana), DA gas, and blob fields. DA gas maps to blob fields today (`daGas = blobFields * 32`) but both are tracked independently.
229
+
230
+ ### Checkpoint limits
231
+
232
+ | Dimension | Source | Budget |
233
+ | --- | --- | --- |
234
+ | L2 gas (mana) | `rollup.getManaLimit()` | Fetched from L1 at startup |
235
+ | DA gas | `MAX_PROCESSABLE_DA_GAS_PER_CHECKPOINT` | 786,432 (6 blobs × 4096 fields × 32 gas/field) |
236
+ | Blob fields | `BLOBS_PER_CHECKPOINT × FIELDS_PER_BLOB` | 24,576 minus checkpoint/block-end overhead |
237
+
238
+ ### Per-block budgets
239
+
240
+ Per-block budgets prevent one block from consuming the entire checkpoint budget.
241
+
242
+ **Proposer**: `computeBlockLimits()` derives budgets at startup as `min(checkpointLimit, ceil(checkpointLimit / maxBlocks * multiplier))`, where `maxBlocks` comes from the timetable and `multiplier` defaults to 2. The multiplier greater than 1 allows early blocks to use more than their even share of the checkpoint budget, since different blocks hit different limit dimensions (L2 gas, DA gas, blob fields) — a strict even split would waste capacity. Operators can override via `SEQ_MAX_L2_BLOCK_GAS` / `SEQ_MAX_DA_BLOCK_GAS` / `SEQ_MAX_TX_PER_BLOCK` (capped at checkpoint limits). Per-block TX limits follow the same derivation pattern when `SEQ_MAX_TX_PER_CHECKPOINT` is set.
243
+
244
+ **Validator**: Optionally enforces per-block limits via `VALIDATOR_MAX_L2_BLOCK_GAS`, `VALIDATOR_MAX_DA_BLOCK_GAS`, and `VALIDATOR_MAX_TX_PER_BLOCK`. When set, these are passed to `buildBlock` during re-execution and to `validateCheckpoint` for final validation. When unset, no per-block limit is enforced for that dimension (checkpoint-level protocol limits still apply). These are independent of the `SEQ_` vars so operators can tune proposer and validation limits separately.
245
+
246
+ **Checkpoint-level capping**: `CheckpointBuilder.capLimitsByCheckpointBudgets()` always runs before tx processing, capping per-block limits by `checkpointBudget - sum(used by prior blocks)` for all three gas dimensions and for transaction count (when `SEQ_MAX_TX_PER_CHECKPOINT` is set). This applies to both proposer and validator paths.
247
+
248
+ ### Per-transaction enforcement
249
+
250
+ **Mempool entry** (`GasLimitsValidator`): L2 gas must be ≤ `MAX_PROCESSABLE_L2_GAS` (6,540,000) and ≥ fixed minimums.
251
+
252
+ **Block building** (`PublicProcessor.process`): Before processing, txs are skipped if their estimated blob fields or gas limits would exceed the block budget. After processing, actual values are checked and the tx is reverted if limits are exceeded.
253
+
254
+ ### Gas limit configuration
255
+
256
+ | Variable | Default | Description |
257
+ | --- | --- | --- |
258
+ | `SEQ_MAX_L2_BLOCK_GAS` | *auto* | Per-block L2 gas. Auto-derived from `rollupManaLimit / maxBlocks * multiplier`. |
259
+ | `SEQ_MAX_DA_BLOCK_GAS` | *auto* | Per-block DA gas. Auto-derived from checkpoint DA limit / maxBlocks * multiplier. |
260
+ | `SEQ_MAX_TX_PER_BLOCK` | *none* | Per-block tx count. If `SEQ_MAX_TX_PER_CHECKPOINT` is set and per-block is not, derived as `ceil(checkpointLimit / maxBlocks * multiplier)`. |
261
+ | `SEQ_MAX_TX_PER_CHECKPOINT` | *none* | Total txs across all blocks in a checkpoint. When set, per-block tx limit is derived from it (unless explicitly overridden) and checkpoint-level capping is enforced. |
262
+ | `SEQ_PER_BLOCK_ALLOCATION_MULTIPLIER` | 2 | Multiplier for per-block budget computation. |
263
+ | `VALIDATOR_MAX_L2_BLOCK_GAS` | *none* | Per-block L2 gas limit for validation. Proposals exceeding this are rejected. |
264
+ | `VALIDATOR_MAX_DA_BLOCK_GAS` | *none* | Per-block DA gas limit for validation. Proposals exceeding this are rejected. |
265
+ | `VALIDATOR_MAX_TX_PER_BLOCK` | *none* | Per-block tx count limit for validation. Proposals exceeding this are rejected. |
266
+ | `VALIDATOR_MAX_TX_PER_CHECKPOINT` | *none* | Per-checkpoint tx count limit for validation. Proposals exceeding this are rejected. |
267
+
223
268
  ## Testing Patterns
224
269
 
225
270
  ### Common Mocks
@@ -268,7 +313,7 @@ For tests that exercise re-execution:
268
313
  ```typescript
269
314
  // Mock parent block lookup
270
315
  blockSource.getBlockHeaderByArchive.mockResolvedValue(parentBlockHeader);
271
- blockSource.getL2BlockNew.mockResolvedValue({
316
+ blockSource.getL2Block.mockResolvedValue({
272
317
  checkpointNumber: CheckpointNumber(1),
273
318
  indexWithinCheckpoint: 0,
274
319
  header: { globalVariables: parentGlobalVariables },
@@ -3,19 +3,18 @@ import { BlockNumber, CheckpointNumber } from '@aztec/foundation/branded-types';
3
3
  import { Fr } from '@aztec/foundation/curves/bn254';
4
4
  import { DateProvider } from '@aztec/foundation/timer';
5
5
  import type { P2P, PeerId } from '@aztec/p2p';
6
- import { TxProvider } from '@aztec/p2p';
7
6
  import { BlockProposalValidator } from '@aztec/p2p/msg_validators';
8
- import type { L2BlockNew, L2BlockSink, L2BlockSource } from '@aztec/stdlib/block';
9
- import type { ValidatorClientFullConfig, WorldStateSynchronizer } from '@aztec/stdlib/interfaces/server';
7
+ import type { L2Block, L2BlockSink, L2BlockSource } from '@aztec/stdlib/block';
8
+ import type { ITxProvider, ValidatorClientFullConfig, WorldStateSynchronizer } from '@aztec/stdlib/interfaces/server';
10
9
  import { type L1ToL2MessageSource } from '@aztec/stdlib/messaging';
11
10
  import type { BlockProposal } from '@aztec/stdlib/p2p';
12
- import { type FailedTx, type Tx } from '@aztec/stdlib/tx';
11
+ import type { FailedTx, Tx } from '@aztec/stdlib/tx';
13
12
  import { type TelemetryClient, type Tracer } from '@aztec/telemetry-client';
14
13
  import type { FullNodeCheckpointsBuilder } from './checkpoint_builder.js';
15
14
  import type { ValidatorMetrics } from './metrics.js';
16
- export type BlockProposalValidationFailureReason = 'invalid_proposal' | 'parent_block_not_found' | 'parent_block_wrong_slot' | 'in_hash_mismatch' | 'global_variables_mismatch' | 'block_number_already_exists' | 'txs_not_available' | 'state_mismatch' | 'failed_txs' | 'timeout' | 'unknown_error';
15
+ export type BlockProposalValidationFailureReason = 'invalid_proposal' | 'parent_block_not_found' | 'block_source_not_synced' | 'parent_block_wrong_slot' | 'in_hash_mismatch' | 'global_variables_mismatch' | 'block_number_already_exists' | 'txs_not_available' | 'state_mismatch' | 'failed_txs' | 'initial_state_mismatch' | 'timeout' | 'unknown_error';
17
16
  type ReexecuteTransactionsResult = {
18
- block: L2BlockNew;
17
+ block: L2Block;
19
18
  failedTxs: FailedTx[];
20
19
  reexecutionTimeMs: number;
21
20
  totalManaUsed: number;
@@ -45,8 +44,8 @@ export declare class BlockProposalHandler {
45
44
  private dateProvider;
46
45
  private log;
47
46
  readonly tracer: Tracer;
48
- constructor(checkpointsBuilder: FullNodeCheckpointsBuilder, worldState: WorldStateSynchronizer, blockSource: L2BlockSource & L2BlockSink, l1ToL2MessageSource: L1ToL2MessageSource, txProvider: TxProvider, blockProposalValidator: BlockProposalValidator, epochCache: EpochCache, config: ValidatorClientFullConfig, metrics?: ValidatorMetrics | undefined, dateProvider?: DateProvider, telemetry?: TelemetryClient, log?: import("@aztec/foundation/log").Logger);
49
- registerForReexecution(p2pClient: P2P): BlockProposalHandler;
47
+ constructor(checkpointsBuilder: FullNodeCheckpointsBuilder, worldState: WorldStateSynchronizer, blockSource: L2BlockSource & L2BlockSink, l1ToL2MessageSource: L1ToL2MessageSource, txProvider: ITxProvider, blockProposalValidator: BlockProposalValidator, epochCache: EpochCache, config: ValidatorClientFullConfig, metrics?: ValidatorMetrics | undefined, dateProvider?: DateProvider, telemetry?: TelemetryClient, log?: import("@aztec/foundation/log").Logger);
48
+ register(p2pClient: P2P, shouldReexecute: boolean): BlockProposalHandler;
50
49
  handleBlockProposal(proposal: BlockProposal, proposalSender: PeerId, shouldReexecute: boolean): Promise<BlockProposalValidationResult>;
51
50
  private getParentBlock;
52
51
  private computeCheckpointNumber;
@@ -57,9 +56,9 @@ export declare class BlockProposalHandler {
57
56
  */
58
57
  private validateNonFirstBlockInCheckpoint;
59
58
  private getReexecutionDeadline;
60
- private getBlocksInCheckpoint;
59
+ private waitForBlockSourceSync;
61
60
  private getReexecuteFailureReason;
62
61
  reexecuteTransactions(proposal: BlockProposal, blockNumber: BlockNumber, checkpointNumber: CheckpointNumber, txs: Tx[], l1ToL2Messages: Fr[], previousCheckpointOutHashes: Fr[]): Promise<ReexecuteTransactionsResult>;
63
62
  }
64
63
  export {};
65
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYmxvY2tfcHJvcG9zYWxfaGFuZGxlci5kLnRzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL2Jsb2NrX3Byb3Bvc2FsX2hhbmRsZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQ0EsT0FBTyxLQUFLLEVBQUUsVUFBVSxFQUFFLE1BQU0sb0JBQW9CLENBQUM7QUFDckQsT0FBTyxFQUFFLFdBQVcsRUFBRSxnQkFBZ0IsRUFBYyxNQUFNLGlDQUFpQyxDQUFDO0FBQzVGLE9BQU8sRUFBRSxFQUFFLEVBQUUsTUFBTSxnQ0FBZ0MsQ0FBQztBQUlwRCxPQUFPLEVBQUUsWUFBWSxFQUFTLE1BQU0seUJBQXlCLENBQUM7QUFDOUQsT0FBTyxLQUFLLEVBQUUsR0FBRyxFQUFFLE1BQU0sRUFBRSxNQUFNLFlBQVksQ0FBQztBQUM5QyxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sWUFBWSxDQUFDO0FBQ3hDLE9BQU8sRUFBRSxzQkFBc0IsRUFBRSxNQUFNLDJCQUEyQixDQUFDO0FBQ25FLE9BQU8sS0FBSyxFQUFFLFVBQVUsRUFBRSxXQUFXLEVBQUUsYUFBYSxFQUFFLE1BQU0scUJBQXFCLENBQUM7QUFFbEYsT0FBTyxLQUFLLEVBQUUseUJBQXlCLEVBQUUsc0JBQXNCLEVBQUUsTUFBTSxpQ0FBaUMsQ0FBQztBQUN6RyxPQUFPLEVBQ0wsS0FBSyxtQkFBbUIsRUFHekIsTUFBTSx5QkFBeUIsQ0FBQztBQUNqQyxPQUFPLEtBQUssRUFBRSxhQUFhLEVBQUUsTUFBTSxtQkFBbUIsQ0FBQztBQUN2RCxPQUFPLEVBQStDLEtBQUssUUFBUSxFQUFFLEtBQUssRUFBRSxFQUFFLE1BQU0sa0JBQWtCLENBQUM7QUFPdkcsT0FBTyxFQUFFLEtBQUssZUFBZSxFQUFFLEtBQUssTUFBTSxFQUFzQixNQUFNLHlCQUF5QixDQUFDO0FBRWhHLE9BQU8sS0FBSyxFQUFFLDBCQUEwQixFQUFFLE1BQU0seUJBQXlCLENBQUM7QUFDMUUsT0FBTyxLQUFLLEVBQUUsZ0JBQWdC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
64
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYmxvY2tfcHJvcG9zYWxfaGFuZGxlci5kLnRzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL2Jsb2NrX3Byb3Bvc2FsX2hhbmRsZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQ0EsT0FBTyxLQUFLLEVBQUUsVUFBVSxFQUFFLE1BQU0sb0JBQW9CLENBQUM7QUFDckQsT0FBTyxFQUFFLFdBQVcsRUFBRSxnQkFBZ0IsRUFBYyxNQUFNLGlDQUFpQyxDQUFDO0FBRTVGLE9BQU8sRUFBRSxFQUFFLEVBQUUsTUFBTSxnQ0FBZ0MsQ0FBQztBQUlwRCxPQUFPLEVBQUUsWUFBWSxFQUFTLE1BQU0seUJBQXlCLENBQUM7QUFDOUQsT0FBTyxLQUFLLEVBQUUsR0FBRyxFQUFFLE1BQU0sRUFBRSxNQUFNLFlBQVksQ0FBQztBQUM5QyxPQUFPLEVBQUUsc0JBQXNCLEVBQUUsTUFBTSwyQkFBMkIsQ0FBQztBQUNuRSxPQUFPLEtBQUssRUFBYSxPQUFPLEVBQUUsV0FBVyxFQUFFLGFBQWEsRUFBRSxNQUFNLHFCQUFxQixDQUFDO0FBRzFGLE9BQU8sS0FBSyxFQUFFLFdBQVcsRUFBRSx5QkFBeUIsRUFBRSxzQkFBc0IsRUFBRSxNQUFNLGlDQUFpQyxDQUFDO0FBQ3RILE9BQU8sRUFBRSxLQUFLLG1CQUFtQixFQUFtQyxNQUFNLHlCQUF5QixDQUFDO0FBQ3BHLE9BQU8sS0FBSyxFQUFFLGFBQWEsRUFBRSxNQUFNLG1CQUFtQixDQUFDO0FBRXZELE9BQU8sS0FBSyxFQUE2QixRQUFRLEVBQUUsRUFBRSxFQUFFLE1BQU0sa0JBQWtCLENBQUM7QUFRaEYsT0FBTyxFQUFFLEtBQUssZUFBZSxFQUFFLEtBQUssTUFBTSxFQUFzQixNQUFNLHlCQUF5QixDQUFDO0FBRWhHLE9BQU8sS0FBSyxFQUFFLDBCQUEwQixFQUFFLE1BQU0seUJBQXlCLENBQUM7QUFDMUUsT0FBTyxLQUFLLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSxjQUFjLENBQUM7QUFFckQsTUFBTSxNQUFNLG9DQUFvQyxHQUM1QyxrQkFBa0IsR0FDbEIsd0JBQXdCLEdBQ3hCLHlCQUF5QixHQUN6Qix5QkFBeUIsR0FDekIsa0JBQWtCLEdBQ2xCLDJCQUEyQixHQUMzQiw2QkFBNkIsR0FDN0IsbUJBQW1CLEdBQ25CLGdCQUFnQixHQUNoQixZQUFZLEdBQ1osd0JBQXdCLEdBQ3hCLFNBQVMsR0FDVCxlQUFlLENBQUM7QUFFcEIsS0FBSywyQkFBMkIsR0FBRztJQUNqQyxLQUFLLEVBQUUsT0FBTyxDQUFDO0lBQ2YsU0FBUyxFQUFFLFFBQVEsRUFBRSxDQUFDO0lBQ3RCLGlCQUFpQixFQUFFLE1BQU0sQ0FBQztJQUMxQixhQUFhLEVBQUUsTUFBTSxDQUFDO0NBQ3ZCLENBQUM7QUFFRixNQUFNLE1BQU0sb0NBQW9DLEdBQUc7SUFDakQsT0FBTyxFQUFFLElBQUksQ0FBQztJQUNkLFdBQVcsRUFBRSxXQUFXLENBQUM7SUFDekIsaUJBQWlCLENBQUMsRUFBRSwyQkFBMkIsQ0FBQztDQUNqRCxDQUFDO0FBRUYsTUFBTSxNQUFNLG9DQUFvQyxHQUFHO0lBQ2pELE9BQU8sRUFBRSxLQUFLLENBQUM7SUFDZixNQUFNLEVBQUUsb0NBQW9DLENBQUM7SUFDN0MsV0FBVyxDQUFDLEVBQUUsV0FBVyxDQUFDO0lBQzFCLGlCQUFpQixDQUFDLEVBQUUsMkJBQTJCLENBQUM7Q0FDakQsQ0FBQztBQUVGLE1BQU0sTUFBTSw2QkFBNkIsR0FBRyxvQ0FBb0MsR0FBRyxvQ0FBb0MsQ0FBQztBQU14SCxxQkFBYSxvQkFBb0I7SUFJN0IsT0FBTyxDQUFDLGtCQUFrQjtJQUMxQixPQUFPLENBQUMsVUFBVTtJQUNsQixPQUFPLENBQUMsV0FBVztJQUNuQixPQUFPLENBQUMsbUJBQW1CO0lBQzNCLE9BQU8sQ0FBQyxVQUFVO0lBQ2xCLE9BQU8sQ0FBQyxzQkFBc0I7SUFDOUIsT0FBTyxDQUFDLFVBQVU7SUFDbEIsT0FBTyxDQUFDLE1BQU07SUFDZCxPQUFPLENBQUMsT0FBTyxDQUFDO0lBQ2hCLE9BQU8sQ0FBQyxZQUFZO0lBRXBCLE9BQU8sQ0FBQyxHQUFHO0lBZGIsU0FBZ0IsTUFBTSxFQUFFLE1BQU0sQ0FBQztJQUUvQixZQUNVLGtCQUFrQixFQUFFLDBCQUEwQixFQUM5QyxVQUFVLEVBQUUsc0JBQXNCLEVBQ2xDLFdBQVcsRUFBRSxhQUFhLEdBQUcsV0FBVyxFQUN4QyxtQkFBbUIsRUFBRSxtQkFBbUIsRUFDeEMsVUFBVSxFQUFFLFdBQVcsRUFDdkIsc0JBQXNCLEVBQUUsc0JBQXNCLEVBQzlDLFVBQVUsRUFBRSxVQUFVLEVBQ3RCLE1BQU0sRUFBRSx5QkFBeUIsRUFDakMsT0FBTyxDQUFDLDhCQUFrQixFQUMxQixZQUFZLEdBQUUsWUFBaUMsRUFDdkQsU0FBUyxHQUFFLGVBQXNDLEVBQ3pDLEdBQUcseUNBQW1ELEVBTS9EO0lBRUQsUUFBUSxDQUFDLFNBQVMsRUFBRSxHQUFHLEVBQUUsZUFBZSxFQUFFLE9BQU8sR0FBRyxvQkFBb0IsQ0FnQ3ZFO0lBRUssbUJBQW1CLENBQ3ZCLFFBQVEsRUFBRSxhQUFhLEVBQ3ZCLGNBQWMsRUFBRSxNQUFNLEVBQ3RCLGVBQWUsRUFBRSxPQUFPLEdBQ3ZCLE9BQU8sQ0FBQyw2QkFBNkIsQ0FBQyxDQXlKeEM7WUFFYSxjQUFjO0lBb0M1QixPQUFPLENBQUMsdUJBQXVCO0lBMEMvQjs7OztPQUlHO0lBQ0gsT0FBTyxDQUFDLGlDQUFpQztJQTRFekMsT0FBTyxDQUFDLHNCQUFzQjtZQU1oQixzQkFBc0I7SUFtQ3BDLE9BQU8sQ0FBQyx5QkFBeUI7SUFnQjNCLHFCQUFxQixDQUN6QixRQUFRLEVBQUUsYUFBYSxFQUN2QixXQUFXLEVBQUUsV0FBVyxFQUN4QixnQkFBZ0IsRUFBRSxnQkFBZ0IsRUFDbEMsR0FBRyxFQUFFLEVBQUUsRUFBRSxFQUNULGNBQWMsRUFBRSxFQUFFLEVBQUUsRUFDcEIsMkJBQTJCLEVBQUUsRUFBRSxFQUFFLEdBQ2hDLE9BQU8sQ0FBQywyQkFBMkIsQ0FBQyxDQWlIdEM7Q0FDRiJ9
@@ -1 +1 @@
1
- {"version":3,"file":"block_proposal_handler.d.ts","sourceRoot":"","sources":["../src/block_proposal_handler.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAc,MAAM,iCAAiC,CAAC;AAC5F,OAAO,EAAE,EAAE,EAAE,MAAM,gCAAgC,CAAC;AAIpD,OAAO,EAAE,YAAY,EAAS,MAAM,yBAAyB,CAAC;AAC9D,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,EAAE,sBAAsB,EAAE,MAAM,2BAA2B,CAAC;AACnE,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAElF,OAAO,KAAK,EAAE,yBAAyB,EAAE,sBAAsB,EAAE,MAAM,iCAAiC,CAAC;AACzG,OAAO,EACL,KAAK,mBAAmB,EAGzB,MAAM,yBAAyB,CAAC;AACjC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,EAA+C,KAAK,QAAQ,EAAE,KAAK,EAAE,EAAE,MAAM,kBAAkB,CAAC;AAOvG,OAAO,EAAE,KAAK,eAAe,EAAE,KAAK,MAAM,EAAsB,MAAM,yBAAyB,CAAC;AAEhG,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,yBAAyB,CAAC;AAC1E,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAErD,MAAM,MAAM,oCAAoC,GAC5C,kBAAkB,GAClB,wBAAwB,GACxB,yBAAyB,GACzB,kBAAkB,GAClB,2BAA2B,GAC3B,6BAA6B,GAC7B,mBAAmB,GACnB,gBAAgB,GAChB,YAAY,GACZ,SAAS,GACT,eAAe,CAAC;AAEpB,KAAK,2BAA2B,GAAG;IACjC,KAAK,EAAE,UAAU,CAAC;IAClB,SAAS,EAAE,QAAQ,EAAE,CAAC;IACtB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,aAAa,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,oCAAoC,GAAG;IACjD,OAAO,EAAE,IAAI,CAAC;IACd,WAAW,EAAE,WAAW,CAAC;IACzB,iBAAiB,CAAC,EAAE,2BAA2B,CAAC;CACjD,CAAC;AAEF,MAAM,MAAM,oCAAoC,GAAG;IACjD,OAAO,EAAE,KAAK,CAAC;IACf,MAAM,EAAE,oCAAoC,CAAC;IAC7C,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,iBAAiB,CAAC,EAAE,2BAA2B,CAAC;CACjD,CAAC;AAEF,MAAM,MAAM,6BAA6B,GAAG,oCAAoC,GAAG,oCAAoC,CAAC;AAMxH,qBAAa,oBAAoB;IAI7B,OAAO,CAAC,kBAAkB;IAC1B,OAAO,CAAC,UAAU;IAClB,OAAO,CAAC,WAAW;IACnB,OAAO,CAAC,mBAAmB;IAC3B,OAAO,CAAC,UAAU;IAClB,OAAO,CAAC,sBAAsB;IAC9B,OAAO,CAAC,UAAU;IAClB,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,OAAO,CAAC;IAChB,OAAO,CAAC,YAAY;IAEpB,OAAO,CAAC,GAAG;IAdb,SAAgB,MAAM,EAAE,MAAM,CAAC;IAE/B,YACU,kBAAkB,EAAE,0BAA0B,EAC9C,UAAU,EAAE,sBAAsB,EAClC,WAAW,EAAE,aAAa,GAAG,WAAW,EACxC,mBAAmB,EAAE,mBAAmB,EACxC,UAAU,EAAE,UAAU,EACtB,sBAAsB,EAAE,sBAAsB,EAC9C,UAAU,EAAE,UAAU,EACtB,MAAM,EAAE,yBAAyB,EACjC,OAAO,CAAC,8BAAkB,EAC1B,YAAY,GAAE,YAAiC,EACvD,SAAS,GAAE,eAAsC,EACzC,GAAG,yCAAmD,EAM/D;IAED,sBAAsB,CAAC,SAAS,EAAE,GAAG,GAAG,oBAAoB,CA6B3D;IAEK,mBAAmB,CACvB,QAAQ,EAAE,aAAa,EACvB,cAAc,EAAE,MAAM,EACtB,eAAe,EAAE,OAAO,GACvB,OAAO,CAAC,6BAA6B,CAAC,CAoIxC;YAEa,cAAc;YAqCd,uBAAuB;IAoDrC;;;;OAIG;IACH,OAAO,CAAC,iCAAiC;IA4EzC,OAAO,CAAC,sBAAsB;YAQhB,qBAAqB;IAoBnC,OAAO,CAAC,yBAAyB;IAY3B,qBAAqB,CACzB,QAAQ,EAAE,aAAa,EACvB,WAAW,EAAE,WAAW,EACxB,gBAAgB,EAAE,gBAAgB,EAClC,GAAG,EAAE,EAAE,EAAE,EACT,cAAc,EAAE,EAAE,EAAE,EACpB,2BAA2B,EAAE,EAAE,EAAE,GAChC,OAAO,CAAC,2BAA2B,CAAC,CA8FtC;CACF"}
1
+ {"version":3,"file":"block_proposal_handler.d.ts","sourceRoot":"","sources":["../src/block_proposal_handler.ts"],"names":[],"mappings":"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"}
@@ -65,14 +65,17 @@ function _ts_dispose_resources(env) {
65
65
  }
66
66
  import { INITIAL_L2_BLOCK_NUM } from '@aztec/constants';
67
67
  import { BlockNumber, CheckpointNumber, SlotNumber } from '@aztec/foundation/branded-types';
68
+ import { pick } from '@aztec/foundation/collection';
68
69
  import { Fr } from '@aztec/foundation/curves/bn254';
69
70
  import { TimeoutError } from '@aztec/foundation/error';
70
71
  import { createLogger } from '@aztec/foundation/log';
71
72
  import { retryUntil } from '@aztec/foundation/retry';
72
73
  import { DateProvider, Timer } from '@aztec/foundation/timer';
73
74
  import { getEpochAtSlot, getTimestampForSlot } from '@aztec/stdlib/epoch-helpers';
74
- import { computeCheckpointOutHash, computeInHashFromL1ToL2Messages } from '@aztec/stdlib/messaging';
75
- import { ReExFailedTxsError, ReExStateMismatchError, ReExTimeoutError, TransactionsNotAvailableError } from '@aztec/stdlib/validators';
75
+ import { Gas } from '@aztec/stdlib/gas';
76
+ import { computeInHashFromL1ToL2Messages } from '@aztec/stdlib/messaging';
77
+ import { MerkleTreeId } from '@aztec/stdlib/trees';
78
+ import { ReExFailedTxsError, ReExInitialStateMismatchError, ReExStateMismatchError, ReExTimeoutError, TransactionsNotAvailableError } from '@aztec/stdlib/validators';
76
79
  import { getTelemetryClient } from '@aztec/telemetry-client';
77
80
  export class BlockProposalHandler {
78
81
  checkpointsBuilder;
@@ -104,23 +107,27 @@ export class BlockProposalHandler {
104
107
  }
105
108
  this.tracer = telemetry.getTracer('BlockProposalHandler');
106
109
  }
107
- registerForReexecution(p2pClient) {
108
- // Non-validator handler that re-executes for monitoring but does not attest.
110
+ register(p2pClient, shouldReexecute) {
111
+ // Non-validator handler that processes or re-executes for monitoring but does not attest.
109
112
  // Returns boolean indicating whether the proposal was valid.
110
113
  const handler = async (proposal, proposalSender)=>{
111
114
  try {
112
- const result = await this.handleBlockProposal(proposal, proposalSender, true);
115
+ const { slotNumber, blockNumber } = proposal;
116
+ const result = await this.handleBlockProposal(proposal, proposalSender, shouldReexecute);
113
117
  if (result.isValid) {
114
- this.log.info(`Non-validator reexecution completed for slot ${proposal.slotNumber}`, {
118
+ this.log.info(`Non-validator block proposal ${blockNumber} at slot ${slotNumber} handled`, {
115
119
  blockNumber: result.blockNumber,
120
+ slotNumber,
116
121
  reexecutionTimeMs: result.reexecutionResult?.reexecutionTimeMs,
117
122
  totalManaUsed: result.reexecutionResult?.totalManaUsed,
118
- numTxs: result.reexecutionResult?.block?.body?.txEffects?.length ?? 0
123
+ numTxs: result.reexecutionResult?.block?.body?.txEffects?.length ?? 0,
124
+ reexecuted: shouldReexecute
119
125
  });
120
126
  return true;
121
127
  } else {
122
- this.log.warn(`Non-validator reexecution failed for slot ${proposal.slotNumber}`, {
128
+ this.log.warn(`Non-validator block proposal ${blockNumber} at slot ${slotNumber} failed processing with ${result.reason}`, {
123
129
  blockNumber: result.blockNumber,
130
+ slotNumber,
124
131
  reason: result.reason
125
132
  });
126
133
  return false;
@@ -147,7 +154,9 @@ export class BlockProposalHandler {
147
154
  }
148
155
  const proposalInfo = {
149
156
  ...proposal.toBlockInfo(),
150
- proposer: proposer.toString()
157
+ proposer: proposer.toString(),
158
+ blockNumber: undefined,
159
+ checkpointNumber: undefined
151
160
  };
152
161
  this.log.info(`Processing proposal for slot ${slotNumber}`, {
153
162
  ...proposalInfo,
@@ -155,27 +164,42 @@ export class BlockProposalHandler {
155
164
  });
156
165
  // Check that the proposal is from the current proposer, or the next proposer
157
166
  // This should have been handled by the p2p layer, but we double check here out of caution
158
- const invalidProposal = await this.blockProposalValidator.validate(proposal);
159
- if (invalidProposal) {
167
+ const validationResult = await this.blockProposalValidator.validate(proposal);
168
+ if (validationResult.result !== 'accept') {
160
169
  this.log.warn(`Proposal is not valid, skipping processing`, proposalInfo);
161
170
  return {
162
171
  isValid: false,
163
172
  reason: 'invalid_proposal'
164
173
  };
165
174
  }
166
- // Check that the parent proposal is a block we know, otherwise reexecution would fail
167
- const parentBlockHeader = await this.getParentBlock(proposal);
168
- if (parentBlockHeader === undefined) {
175
+ // Ensure the block source is synced before checking for existing blocks,
176
+ // since a pending checkpoint prune may remove blocks we'd otherwise find.
177
+ // This affects mostly the block_number_already_exists check, since a pending
178
+ // checkpoint prune could remove a block that would conflict with this proposal.
179
+ // TODO(@Maddiaa0): This may break staggered slots.
180
+ const blockSourceSync = await this.waitForBlockSourceSync(slotNumber);
181
+ if (!blockSourceSync) {
182
+ this.log.warn(`Block source is not synced, skipping processing`, proposalInfo);
183
+ return {
184
+ isValid: false,
185
+ reason: 'block_source_not_synced'
186
+ };
187
+ }
188
+ // Check that the parent proposal is a block we know, otherwise reexecution would fail.
189
+ // If we don't find it immediately, we keep retrying for a while; it may be we still
190
+ // need to process other block proposals to get to it.
191
+ const parentBlock = await this.getParentBlock(proposal);
192
+ if (parentBlock === undefined) {
169
193
  this.log.warn(`Parent block for proposal not found, skipping processing`, proposalInfo);
170
194
  return {
171
195
  isValid: false,
172
196
  reason: 'parent_block_not_found'
173
197
  };
174
198
  }
175
- // Check that the parent block's slot is less than the proposal's slot (should not happen, but we check anyway)
176
- if (parentBlockHeader !== 'genesis' && parentBlockHeader.getSlot() >= slotNumber) {
177
- this.log.warn(`Parent block slot is greater than or equal to proposal slot, skipping processing`, {
178
- parentBlockSlot: parentBlockHeader.getSlot().toString(),
199
+ // Check that the parent block's slot is not greater than the proposal's slot.
200
+ if (parentBlock !== 'genesis' && parentBlock.header.getSlot() > slotNumber) {
201
+ this.log.warn(`Parent block slot is greater than proposal slot, skipping processing`, {
202
+ parentBlockSlot: parentBlock.header.getSlot().toString(),
179
203
  proposalSlot: slotNumber.toString(),
180
204
  ...proposalInfo
181
205
  });
@@ -185,7 +209,8 @@ export class BlockProposalHandler {
185
209
  };
186
210
  }
187
211
  // Compute the block number based on the parent block
188
- const blockNumber = parentBlockHeader === 'genesis' ? BlockNumber(INITIAL_L2_BLOCK_NUM) : BlockNumber(parentBlockHeader.getBlockNumber() + 1);
212
+ const blockNumber = parentBlock === 'genesis' ? BlockNumber(INITIAL_L2_BLOCK_NUM) : BlockNumber(parentBlock.header.getBlockNumber() + 1);
213
+ proposalInfo.blockNumber = blockNumber;
189
214
  // Check that this block number does not exist already
190
215
  const existingBlock = await this.blockSource.getBlockHeader(blockNumber);
191
216
  if (existingBlock) {
@@ -202,8 +227,16 @@ export class BlockProposalHandler {
202
227
  pinnedPeer: proposalSender,
203
228
  deadline: this.getReexecutionDeadline(slotNumber, config)
204
229
  });
230
+ // If reexecution is disabled, bail. We were just interested in triggering tx collection.
231
+ if (!shouldReexecute) {
232
+ this.log.info(`Received valid block ${blockNumber} proposal at index ${proposal.indexWithinCheckpoint} on slot ${slotNumber}`, proposalInfo);
233
+ return {
234
+ isValid: true,
235
+ blockNumber
236
+ };
237
+ }
205
238
  // Compute the checkpoint number for this block and validate checkpoint consistency
206
- const checkpointResult = await this.computeCheckpointNumber(proposal, parentBlockHeader, proposalInfo);
239
+ const checkpointResult = this.computeCheckpointNumber(proposal, parentBlock, proposalInfo);
207
240
  if (checkpointResult.reason) {
208
241
  return {
209
242
  isValid: false,
@@ -212,6 +245,7 @@ export class BlockProposalHandler {
212
245
  };
213
246
  }
214
247
  const checkpointNumber = checkpointResult.checkpointNumber;
248
+ proposalInfo.checkpointNumber = checkpointNumber;
215
249
  // Check that I have the same set of l1ToL2Messages as the proposal
216
250
  const l1ToL2Messages = await this.l1ToL2MessageSource.getL1ToL2Messages(checkpointNumber);
217
251
  const computedInHash = computeInHashFromL1ToL2Messages(l1ToL2Messages);
@@ -240,38 +274,32 @@ export class BlockProposalHandler {
240
274
  reason: 'txs_not_available'
241
275
  };
242
276
  }
277
+ // Collect the out hashes of all the checkpoints before this one in the same epoch
278
+ const epoch = getEpochAtSlot(slotNumber, this.epochCache.getL1Constants());
279
+ const previousCheckpointOutHashes = (await this.blockSource.getCheckpointsDataForEpoch(epoch)).filter((c)=>c.checkpointNumber < checkpointNumber).map((c)=>c.checkpointOutHash);
243
280
  // Try re-executing the transactions in the proposal if needed
244
281
  let reexecutionResult;
245
- if (shouldReexecute) {
246
- // Compute the previous checkpoint out hashes for the epoch.
247
- // TODO(mbps): This assumes one block per checkpoint, which is only true for now.
248
- // TODO: There can be a more efficient way to get the previous checkpoint out hashes without having to fetch all
249
- // the blocks.
250
- const epoch = getEpochAtSlot(slotNumber, this.epochCache.getL1Constants());
251
- const previousBlocks = (await this.blockSource.getBlocksForEpoch(epoch)).filter((b)=>b.number < blockNumber).sort((a, b)=>a.number - b.number);
252
- const previousCheckpointOutHashes = previousBlocks.map((b)=>computeCheckpointOutHash([
253
- b.body.txEffects.map((tx)=>tx.l2ToL1Msgs)
254
- ]));
255
- try {
256
- this.log.verbose(`Re-executing transactions in the proposal`, proposalInfo);
257
- reexecutionResult = await this.reexecuteTransactions(proposal, blockNumber, checkpointNumber, txs, l1ToL2Messages, previousCheckpointOutHashes);
258
- } catch (error) {
259
- this.log.error(`Error reexecuting txs while processing block proposal`, error, proposalInfo);
260
- const reason = this.getReexecuteFailureReason(error);
261
- return {
262
- isValid: false,
263
- blockNumber,
264
- reason,
265
- reexecutionResult
266
- };
267
- }
282
+ try {
283
+ this.log.verbose(`Re-executing transactions in the proposal`, proposalInfo);
284
+ reexecutionResult = await this.reexecuteTransactions(proposal, blockNumber, checkpointNumber, txs, l1ToL2Messages, previousCheckpointOutHashes);
285
+ } catch (error) {
286
+ this.log.error(`Error reexecuting txs while processing block proposal`, error, proposalInfo);
287
+ const reason = this.getReexecuteFailureReason(error);
288
+ return {
289
+ isValid: false,
290
+ blockNumber,
291
+ reason,
292
+ reexecutionResult
293
+ };
268
294
  }
269
295
  // If we succeeded, push this block into the archiver (unless disabled)
270
- // TODO(palla/mbps): Change default to false once block sync is stable.
271
296
  if (reexecutionResult?.block && this.config.skipPushProposedBlocksToArchiver === false) {
272
297
  await this.blockSource.addBlock(reexecutionResult?.block);
273
298
  }
274
- this.log.info(`Successfully processed block ${blockNumber} proposal at index ${proposal.indexWithinCheckpoint} on slot ${slotNumber}`, proposalInfo);
299
+ this.log.info(`Successfully re-executed block ${blockNumber} proposal at index ${proposal.indexWithinCheckpoint} on slot ${slotNumber}`, {
300
+ ...proposalInfo,
301
+ ...pick(reexecutionResult, 'reexecutionTimeMs', 'totalManaUsed')
302
+ });
275
303
  return {
276
304
  isValid: true,
277
305
  blockNumber,
@@ -290,7 +318,7 @@ export class BlockProposalHandler {
290
318
  const currentTime = this.dateProvider.now();
291
319
  const timeoutDurationMs = deadline.getTime() - currentTime;
292
320
  try {
293
- return await this.blockSource.getBlockHeaderByArchive(parentArchive) ?? (timeoutDurationMs <= 0 ? undefined : await retryUntil(()=>this.blockSource.syncImmediate().then(()=>this.blockSource.getBlockHeaderByArchive(parentArchive)), 'force archiver sync', timeoutDurationMs / 1000, 0.5));
321
+ return await this.blockSource.getBlockDataByArchive(parentArchive) ?? (timeoutDurationMs <= 0 ? undefined : await retryUntil(()=>this.blockSource.syncImmediate().then(()=>this.blockSource.getBlockDataByArchive(parentArchive)), 'force archiver sync', timeoutDurationMs / 1000, 0.5));
294
322
  } catch (err) {
295
323
  if (err instanceof TimeoutError) {
296
324
  this.log.debug(`Timed out getting parent block by archive root`, {
@@ -304,8 +332,8 @@ export class BlockProposalHandler {
304
332
  return undefined;
305
333
  }
306
334
  }
307
- async computeCheckpointNumber(proposal, parentBlockHeader, proposalInfo) {
308
- if (parentBlockHeader === 'genesis') {
335
+ computeCheckpointNumber(proposal, parentBlock, proposalInfo) {
336
+ if (parentBlock === 'genesis') {
309
337
  // First block is in checkpoint 1
310
338
  if (proposal.indexWithinCheckpoint !== 0) {
311
339
  this.log.warn(`First block proposal has non-zero indexWithinCheckpoint`, proposalInfo);
@@ -317,20 +345,9 @@ export class BlockProposalHandler {
317
345
  checkpointNumber: CheckpointNumber.INITIAL
318
346
  };
319
347
  }
320
- // Get the parent block to find its checkpoint number
321
- // TODO(palla/mbps): The block header should include the checkpoint number to avoid this lookup,
322
- // or at least the L2BlockSource should return a different struct that includes it.
323
- const parentBlockNumber = parentBlockHeader.getBlockNumber();
324
- const parentBlock = await this.blockSource.getL2BlockNew(parentBlockNumber);
325
- if (!parentBlock) {
326
- this.log.warn(`Parent block ${parentBlockNumber} not found in archiver`, proposalInfo);
327
- return {
328
- reason: 'invalid_proposal'
329
- };
330
- }
331
348
  if (proposal.indexWithinCheckpoint === 0) {
332
349
  // If this is the first block in a new checkpoint, increment the checkpoint number
333
- if (!(proposal.blockHeader.getSlot() > parentBlockHeader.getSlot())) {
350
+ if (!(proposal.blockHeader.getSlot() > parentBlock.header.getSlot())) {
334
351
  this.log.warn(`Slot should be greater than parent block slot for first block in checkpoint`, proposalInfo);
335
352
  return {
336
353
  reason: 'invalid_proposal'
@@ -347,7 +364,7 @@ export class BlockProposalHandler {
347
364
  reason: 'invalid_proposal'
348
365
  };
349
366
  }
350
- if (proposal.blockHeader.getSlot() !== parentBlockHeader.getSlot()) {
367
+ if (proposal.blockHeader.getSlot() !== parentBlock.header.getSlot()) {
351
368
  this.log.warn(`Slot should be equal to parent block slot for non-first block in checkpoint`, proposalInfo);
352
369
  return {
353
370
  reason: 'invalid_proposal'
@@ -447,23 +464,39 @@ export class BlockProposalHandler {
447
464
  const nextSlotTimestampSeconds = Number(getTimestampForSlot(SlotNumber(slot + 1), config));
448
465
  return new Date(nextSlotTimestampSeconds * 1000);
449
466
  }
450
- /**
451
- * Gets all prior blocks in the same checkpoint (same slot and checkpoint number) up to but not including upToBlockNumber.
452
- */ async getBlocksInCheckpoint(slot, upToBlockNumber, checkpointNumber) {
453
- const blocks = [];
454
- let currentBlockNumber = BlockNumber(upToBlockNumber - 1);
455
- while(currentBlockNumber >= INITIAL_L2_BLOCK_NUM){
456
- const block = await this.blockSource.getL2BlockNew(currentBlockNumber);
457
- if (!block || block.header.getSlot() !== slot || block.checkpointNumber !== checkpointNumber) {
458
- break;
467
+ /** Waits for the block source to sync L1 data up to at least the slot before the given one. */ async waitForBlockSourceSync(slot) {
468
+ const deadline = this.getReexecutionDeadline(slot, this.checkpointsBuilder.getConfig());
469
+ const timeoutMs = deadline.getTime() - this.dateProvider.now();
470
+ if (slot === 0) {
471
+ return true;
472
+ }
473
+ // Make a quick check before triggering an archiver sync
474
+ const syncedSlot = await this.blockSource.getSyncedL2SlotNumber();
475
+ if (syncedSlot !== undefined && syncedSlot + 1 >= slot) {
476
+ return true;
477
+ }
478
+ try {
479
+ // Trigger an immediate sync of the block source, and wait until it reports being synced to the required slot
480
+ return await retryUntil(async ()=>{
481
+ await this.blockSource.syncImmediate();
482
+ const syncedSlot = await this.blockSource.getSyncedL2SlotNumber();
483
+ return syncedSlot !== undefined && syncedSlot + 1 >= slot;
484
+ }, 'wait for block source sync', timeoutMs / 1000, 0.5);
485
+ } catch (err) {
486
+ if (err instanceof TimeoutError) {
487
+ this.log.warn(`Timed out waiting for block source to sync to slot ${slot}`);
488
+ return false;
489
+ } else {
490
+ throw err;
459
491
  }
460
- blocks.unshift(block);
461
- currentBlockNumber = BlockNumber(currentBlockNumber - 1);
462
492
  }
463
- return blocks;
464
493
  }
465
494
  getReexecuteFailureReason(err) {
466
- if (err instanceof ReExStateMismatchError) {
495
+ if (err instanceof TransactionsNotAvailableError) {
496
+ return 'txs_not_available';
497
+ } else if (err instanceof ReExInitialStateMismatchError) {
498
+ return 'initial_state_mismatch';
499
+ } else if (err instanceof ReExStateMismatchError) {
467
500
  return 'state_mismatch';
468
501
  } else if (err instanceof ReExFailedTxsError) {
469
502
  return 'failed_txs';
@@ -490,34 +523,47 @@ export class BlockProposalHandler {
490
523
  const timer = new Timer();
491
524
  const slot = proposal.slotNumber;
492
525
  const config = this.checkpointsBuilder.getConfig();
493
- // Get prior blocks in this checkpoint (same slot and checkpoint number)
494
- const priorBlocks = await this.getBlocksInCheckpoint(slot, blockNumber, checkpointNumber);
526
+ // Get prior blocks in this checkpoint (same slot before current block)
527
+ const allBlocksInSlot = await this.blockSource.getBlocksForSlot(slot);
528
+ const priorBlocks = allBlocksInSlot.filter((b)=>b.number < blockNumber && b.header.getSlot() === slot);
495
529
  // Fork before the block to be built
496
530
  const parentBlockNumber = BlockNumber(blockNumber - 1);
497
- const fork = _ts_add_disposable_resource(env, await this.worldState.fork(parentBlockNumber), false);
498
- // Build checkpoint constants from proposal (excludes blockNumber and timestamp which are per-block)
531
+ await this.worldState.syncImmediate(parentBlockNumber);
532
+ const fork = _ts_add_disposable_resource(env, await this.worldState.fork(parentBlockNumber), true);
533
+ // Verify the fork's archive root matches the proposal's expected last archive.
534
+ // If they don't match, our world state synced to a different chain and reexecution would fail.
535
+ const forkArchiveRoot = new Fr((await fork.getTreeInfo(MerkleTreeId.ARCHIVE)).root);
536
+ if (!forkArchiveRoot.equals(proposal.blockHeader.lastArchive.root)) {
537
+ throw new ReExInitialStateMismatchError(proposal.blockHeader.lastArchive.root, forkArchiveRoot);
538
+ }
539
+ // Build checkpoint constants from proposal (excludes blockNumber which is per-block)
499
540
  const constants = {
500
541
  chainId: new Fr(config.l1ChainId),
501
542
  version: new Fr(config.rollupVersion),
502
543
  slotNumber: slot,
544
+ timestamp: blockHeader.globalVariables.timestamp,
503
545
  coinbase: blockHeader.globalVariables.coinbase,
504
546
  feeRecipient: blockHeader.globalVariables.feeRecipient,
505
547
  gasFees: blockHeader.globalVariables.gasFees
506
548
  };
507
549
  // Create checkpoint builder with prior blocks
508
- const checkpointBuilder = await this.checkpointsBuilder.openCheckpoint(checkpointNumber, constants, l1ToL2Messages, previousCheckpointOutHashes, fork, priorBlocks);
550
+ const checkpointBuilder = await this.checkpointsBuilder.openCheckpoint(checkpointNumber, constants, 0n, l1ToL2Messages, previousCheckpointOutHashes, fork, priorBlocks, this.log.getBindings());
509
551
  // Build the new block
510
552
  const deadline = this.getReexecutionDeadline(slot, config);
553
+ const maxBlockGas = this.config.validateMaxL2BlockGas !== undefined || this.config.validateMaxDABlockGas !== undefined ? new Gas(this.config.validateMaxDABlockGas ?? Infinity, this.config.validateMaxL2BlockGas ?? Infinity) : undefined;
511
554
  const result = await checkpointBuilder.buildBlock(txs, blockNumber, blockHeader.globalVariables.timestamp, {
512
555
  deadline,
513
- expectedEndState: blockHeader.state
556
+ expectedEndState: blockHeader.state,
557
+ maxTransactions: this.config.validateMaxTxsPerBlock,
558
+ maxBlockGas
514
559
  });
515
560
  const { block, failedTxs } = result;
516
561
  const numFailedTxs = failedTxs.length;
517
- this.log.verbose(`Transaction re-execution complete for slot ${slot}`, {
562
+ this.log.verbose(`Block proposal ${blockNumber} at slot ${slot} transaction re-execution complete`, {
518
563
  numFailedTxs,
519
564
  numProposalTxs: txHashes.length,
520
565
  numProcessedTxs: block.body.txEffects.length,
566
+ blockNumber,
521
567
  slot
522
568
  });
523
569
  if (numFailedTxs > 0) {
@@ -555,7 +601,8 @@ export class BlockProposalHandler {
555
601
  env.error = e;
556
602
  env.hasError = true;
557
603
  } finally{
558
- _ts_dispose_resources(env);
604
+ const result = _ts_dispose_resources(env);
605
+ if (result) await result;
559
606
  }
560
607
  }
561
608
  }