@aztec/validator-client 0.0.1-commit.7d4e6cd → 0.0.1-commit.7ffbba4

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 (69) hide show
  1. package/README.md +95 -24
  2. package/dest/block_proposal_handler.d.ts +10 -10
  3. package/dest/block_proposal_handler.d.ts.map +1 -1
  4. package/dest/block_proposal_handler.js +76 -76
  5. package/dest/checkpoint_builder.d.ts +31 -25
  6. package/dest/checkpoint_builder.d.ts.map +1 -1
  7. package/dest/checkpoint_builder.js +114 -41
  8. package/dest/config.d.ts +1 -1
  9. package/dest/config.d.ts.map +1 -1
  10. package/dest/config.js +33 -14
  11. package/dest/duties/validation_service.d.ts +20 -7
  12. package/dest/duties/validation_service.d.ts.map +1 -1
  13. package/dest/duties/validation_service.js +69 -22
  14. package/dest/factory.d.ts +2 -2
  15. package/dest/factory.d.ts.map +1 -1
  16. package/dest/factory.js +3 -2
  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 +99 -0
  21. package/dest/key_store/ha_key_store.d.ts.map +1 -0
  22. package/dest/key_store/ha_key_store.js +208 -0
  23. package/dest/key_store/index.d.ts +2 -1
  24. package/dest/key_store/index.d.ts.map +1 -1
  25. package/dest/key_store/index.js +1 -0
  26. package/dest/key_store/interface.d.ts +36 -6
  27. package/dest/key_store/interface.d.ts.map +1 -1
  28. package/dest/key_store/local_key_store.d.ts +10 -5
  29. package/dest/key_store/local_key_store.d.ts.map +1 -1
  30. package/dest/key_store/local_key_store.js +8 -4
  31. package/dest/key_store/node_keystore_adapter.d.ts +18 -5
  32. package/dest/key_store/node_keystore_adapter.d.ts.map +1 -1
  33. package/dest/key_store/node_keystore_adapter.js +18 -4
  34. package/dest/key_store/web3signer_key_store.d.ts +10 -5
  35. package/dest/key_store/web3signer_key_store.d.ts.map +1 -1
  36. package/dest/key_store/web3signer_key_store.js +8 -4
  37. package/dest/metrics.d.ts +12 -3
  38. package/dest/metrics.d.ts.map +1 -1
  39. package/dest/metrics.js +46 -5
  40. package/dest/validator.d.ts +45 -18
  41. package/dest/validator.d.ts.map +1 -1
  42. package/dest/validator.js +262 -98
  43. package/package.json +21 -17
  44. package/src/block_proposal_handler.ts +93 -95
  45. package/src/checkpoint_builder.ts +171 -48
  46. package/src/config.ts +32 -13
  47. package/src/duties/validation_service.ts +94 -25
  48. package/src/factory.ts +2 -0
  49. package/src/index.ts +0 -1
  50. package/src/key_store/ha_key_store.ts +269 -0
  51. package/src/key_store/index.ts +1 -0
  52. package/src/key_store/interface.ts +44 -5
  53. package/src/key_store/local_key_store.ts +13 -4
  54. package/src/key_store/node_keystore_adapter.ts +27 -4
  55. package/src/key_store/web3signer_key_store.ts +17 -4
  56. package/src/metrics.ts +63 -6
  57. package/src/validator.ts +326 -116
  58. package/dest/tx_validator/index.d.ts +0 -3
  59. package/dest/tx_validator/index.d.ts.map +0 -1
  60. package/dest/tx_validator/index.js +0 -2
  61. package/dest/tx_validator/nullifier_cache.d.ts +0 -14
  62. package/dest/tx_validator/nullifier_cache.d.ts.map +0 -1
  63. package/dest/tx_validator/nullifier_cache.js +0 -24
  64. package/dest/tx_validator/tx_validator_factory.d.ts +0 -18
  65. package/dest/tx_validator/tx_validator_factory.d.ts.map +0 -1
  66. package/dest/tx_validator/tx_validator_factory.js +0 -53
  67. package/src/tx_validator/index.ts +0 -2
  68. package/src/tx_validator/nullifier_cache.ts +0 -30
  69. package/src/tx_validator/tx_validator_factory.ts +0 -133
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,19 +156,44 @@ 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 |
170
+
171
+ ### High Availability (HA) Keystore
172
+
173
+ When running multiple validator nodes with the same validator keys in a high-availability setup, enable HA signing to prevent double-signing:
174
+
175
+ | Environment Variable | Purpose |
176
+ | -------------------------------------- | ---------------------------------------------------------------------- |
177
+ | `VALIDATOR_HA_SIGNING_ENABLED` | Enable HA signing / slashing protection (default: false) |
178
+ | `VALIDATOR_HA_DATABASE_URL` | PostgreSQL connection string for coordination (required when enabled) |
179
+ | `VALIDATOR_HA_NODE_ID` | Unique identifier for this validator node (required when enabled) |
180
+ | `VALIDATOR_HA_POLLING_INTERVAL_MS` | How often to check duty status (default: 100) |
181
+ | `VALIDATOR_HA_SIGNING_TIMEOUT_MS` | Max wait for in-progress signing (default: 3000) |
182
+ | `VALIDATOR_HA_MAX_STUCK_DUTIES_AGE_MS` | Max age of stuck duties before cleanup (default: 2\*aztecSlotDuration) |
183
+
184
+ When `VALIDATOR_HA_SIGNING_ENABLED=true`, the validator client automatically:
185
+
186
+ - Creates an HA signer using the provided configuration
187
+ - Wraps the base keystore with `HAKeyStore` for HA-protected signing
188
+ - Coordinates signing across nodes via PostgreSQL to prevent double-signing
189
+ - Provides slashing protection to block conflicting signatures
190
+
191
+ See [`@aztec/validator-ha-signer`](../validator-ha-signer/README.md) for more details.
167
192
 
168
193
  ### Fisherman Mode
169
194
 
170
195
  When `fishermanMode: true`, the validator:
196
+
171
197
  - Validates all proposals (block and checkpoint)
172
198
  - Re-executes transactions
173
199
  - Creates attestations internally for validation
@@ -179,6 +205,7 @@ This is useful for monitoring network health without participating in consensus.
179
205
  ### Key Methods
180
206
 
181
207
  **ValidatorClient** (`validator.ts`):
208
+
182
209
  - `validateBlockProposal(proposal, sender)` → `boolean`: Validates block, optionally re-executes, emits slash events
183
210
  - `attestToCheckpointProposal(proposal, sender)` → `CheckpointAttestation[]?`: Validates checkpoint and creates attestations
184
211
  - `collectAttestations(proposal, required, deadline)` → `CheckpointAttestation[]`: Waits for attestations from other validators
@@ -186,14 +213,58 @@ This is useful for monitoring network health without participating in consensus.
186
213
  - `createCheckpointProposal(...)` → `CheckpointProposal`: Creates and signs a checkpoint proposal
187
214
 
188
215
  **BlockProposalHandler** (`block_proposal_handler.ts`):
216
+
189
217
  - `handleBlockProposal(proposal, sender, shouldReexecute)` → `ValidationResult`: Full block validation pipeline
190
218
  - `reexecuteTransactions(proposal, blockNumber, txs, messages)` → `ReexecutionResult`: Re-runs transactions and compares state
191
219
 
192
220
  **ValidationService** (`duties/validation_service.ts`):
221
+
193
222
  - `createBlockProposal(...)` → `BlockProposal`: Signs block proposal with validator key
194
223
  - `createCheckpointProposal(...)` → `CheckpointProposal`: Signs checkpoint proposal
195
224
  - `attestToCheckpointProposal(proposal, attestors)` → `CheckpointAttestation[]`: Creates attestations for given addresses
196
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
+
197
268
  ## Testing Patterns
198
269
 
199
270
  ### Common Mocks
@@ -204,7 +275,7 @@ Tests typically mock these dependencies:
204
275
  let epochCache: MockProxy<EpochCache>;
205
276
  let blockSource: MockProxy<L2BlockSource>;
206
277
  let txProvider: MockProxy<TxProvider>;
207
- let blockBuilder: MockProxy<IFullNodeBlockBuilder>;
278
+ let checkpointsBuilder: MockProxy<FullNodeCheckpointsBuilder>;
208
279
  let p2pClient: MockProxy<P2P>;
209
280
 
210
281
  beforeEach(() => {
@@ -219,19 +290,19 @@ beforeEach(() => {
219
290
  Use factory functions from `@aztec/stdlib/testing`:
220
291
 
221
292
  ```typescript
222
- import { makeBlockProposal, makeCheckpointProposal, makeL2BlockHeader } from '@aztec/stdlib/testing';
293
+ import { makeBlockHeader, makeBlockProposal, makeCheckpointHeader, makeCheckpointProposal } from '@aztec/stdlib/testing';
223
294
 
224
295
  // These are async - always await
225
296
  const blockProposal = await makeBlockProposal({
226
- blockHeader: makeL2BlockHeader(1, 100, 100), // epoch, block, slot
297
+ blockHeader: makeBlockHeader(1, { blockNumber: BlockNumber(100), slotNumber: SlotNumber(100) }),
227
298
  indexWithinCheckpoint: 0,
228
299
  signer: Secp256k1Signer.random(),
229
300
  });
230
301
 
231
302
  const checkpointProposal = await makeCheckpointProposal({
232
- checkpointHeader: makeL2BlockHeader(1, 100, 100).toCheckpointHeader(),
303
+ checkpointHeader: makeCheckpointHeader(1, { slotNumber: SlotNumber(100) }),
233
304
  signer: proposer,
234
- lastBlock: { blockHeader, txs },
305
+ lastBlock: { blockHeader: makeBlockHeader(1), txs },
235
306
  });
236
307
  ```
237
308
 
@@ -242,7 +313,7 @@ For tests that exercise re-execution:
242
313
  ```typescript
243
314
  // Mock parent block lookup
244
315
  blockSource.getBlockHeaderByArchive.mockResolvedValue(parentBlockHeader);
245
- blockSource.getL2BlockNew.mockResolvedValue({
316
+ blockSource.getL2Block.mockResolvedValue({
246
317
  checkpointNumber: CheckpointNumber(1),
247
318
  indexWithinCheckpoint: 0,
248
319
  header: { globalVariables: parentGlobalVariables },
@@ -1,20 +1,20 @@
1
+ import type { EpochCache } from '@aztec/epoch-cache';
1
2
  import { BlockNumber, CheckpointNumber } from '@aztec/foundation/branded-types';
2
3
  import { Fr } from '@aztec/foundation/curves/bn254';
3
4
  import { DateProvider } from '@aztec/foundation/timer';
4
5
  import type { P2P, PeerId } from '@aztec/p2p';
5
- import { TxProvider } from '@aztec/p2p';
6
6
  import { BlockProposalValidator } from '@aztec/p2p/msg_validators';
7
- import type { L2BlockNew, L2BlockSink, L2BlockSource } from '@aztec/stdlib/block';
8
- 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';
9
9
  import { type L1ToL2MessageSource } from '@aztec/stdlib/messaging';
10
10
  import type { BlockProposal } from '@aztec/stdlib/p2p';
11
- import { type FailedTx, type Tx } from '@aztec/stdlib/tx';
11
+ import type { FailedTx, Tx } from '@aztec/stdlib/tx';
12
12
  import { type TelemetryClient, type Tracer } from '@aztec/telemetry-client';
13
13
  import type { FullNodeCheckpointsBuilder } from './checkpoint_builder.js';
14
14
  import type { ValidatorMetrics } from './metrics.js';
15
15
  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';
16
16
  type ReexecuteTransactionsResult = {
17
- block: L2BlockNew;
17
+ block: L2Block;
18
18
  failedTxs: FailedTx[];
19
19
  reexecutionTimeMs: number;
20
20
  totalManaUsed: number;
@@ -38,13 +38,14 @@ export declare class BlockProposalHandler {
38
38
  private l1ToL2MessageSource;
39
39
  private txProvider;
40
40
  private blockProposalValidator;
41
+ private epochCache;
41
42
  private config;
42
43
  private metrics?;
43
44
  private dateProvider;
44
45
  private log;
45
46
  readonly tracer: Tracer;
46
- constructor(checkpointsBuilder: FullNodeCheckpointsBuilder, worldState: WorldStateSynchronizer, blockSource: L2BlockSource & L2BlockSink, l1ToL2MessageSource: L1ToL2MessageSource, txProvider: TxProvider, blockProposalValidator: BlockProposalValidator, config: ValidatorClientFullConfig, metrics?: ValidatorMetrics | undefined, dateProvider?: DateProvider, telemetry?: TelemetryClient, log?: import("@aztec/foundation/log").Logger);
47
- 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;
48
49
  handleBlockProposal(proposal: BlockProposal, proposalSender: PeerId, shouldReexecute: boolean): Promise<BlockProposalValidationResult>;
49
50
  private getParentBlock;
50
51
  private computeCheckpointNumber;
@@ -55,9 +56,8 @@ export declare class BlockProposalHandler {
55
56
  */
56
57
  private validateNonFirstBlockInCheckpoint;
57
58
  private getReexecutionDeadline;
58
- private getBlocksInCheckpoint;
59
59
  private getReexecuteFailureReason;
60
- reexecuteTransactions(proposal: BlockProposal, blockNumber: BlockNumber, checkpointNumber: CheckpointNumber, txs: Tx[], l1ToL2Messages: Fr[]): Promise<ReexecuteTransactionsResult>;
60
+ reexecuteTransactions(proposal: BlockProposal, blockNumber: BlockNumber, checkpointNumber: CheckpointNumber, txs: Tx[], l1ToL2Messages: Fr[], previousCheckpointOutHashes: Fr[]): Promise<ReexecuteTransactionsResult>;
61
61
  }
62
62
  export {};
63
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYmxvY2tfcHJvcG9zYWxfaGFuZGxlci5kLnRzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL2Jsb2NrX3Byb3Bvc2FsX2hhbmRsZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQ0EsT0FBTyxFQUFFLFdBQVcsRUFBRSxnQkFBZ0IsRUFBYyxNQUFNLGlDQUFpQyxDQUFDO0FBQzVGLE9BQU8sRUFBRSxFQUFFLEVBQUUsTUFBTSxnQ0FBZ0MsQ0FBQztBQUlwRCxPQUFPLEVBQUUsWUFBWSxFQUFTLE1BQU0seUJBQXlCLENBQUM7QUFDOUQsT0FBTyxLQUFLLEVBQUUsR0FBRyxFQUFFLE1BQU0sRUFBRSxNQUFNLFlBQVksQ0FBQztBQUM5QyxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sWUFBWSxDQUFDO0FBQ3hDLE9BQU8sRUFBRSxzQkFBc0IsRUFBRSxNQUFNLDJCQUEyQixDQUFDO0FBQ25FLE9BQU8sS0FBSyxFQUFFLFVBQVUsRUFBRSxXQUFXLEVBQUUsYUFBYSxFQUFFLE1BQU0scUJBQXFCLENBQUM7QUFFbEYsT0FBTyxLQUFLLEVBQUUseUJBQXlCLEVBQUUsc0JBQXNCLEVBQUUsTUFBTSxpQ0FBaUMsQ0FBQztBQUN6RyxPQUFPLEVBQUUsS0FBSyxtQkFBbUIsRUFBbUMsTUFBTSx5QkFBeUIsQ0FBQztBQUNwRyxPQUFPLEtBQUssRUFBRSxhQUFhLEVBQUUsTUFBTSxtQkFBbUIsQ0FBQztBQUN2RCxPQUFPLEVBQStDLEtBQUssUUFBUSxFQUFFLEtBQUssRUFBRSxFQUFFLE1BQU0sa0JBQWtCLENBQUM7QUFPdkcsT0FBTyxFQUFFLEtBQUssZUFBZSxFQUFFLEtBQUssTUFBTSxFQUFzQixNQUFNLHlCQUF5QixDQUFDO0FBRWhHLE9BQU8sS0FBSyxFQUFFLDBCQUEwQixFQUFFLE1BQU0seUJBQXlCLENBQUM7QUFDMUUsT0FBTyxLQUFLLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSxjQUFjLENBQUM7QUFFckQsTUFBTSxNQUFNLG9DQUFvQyxHQUM1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
63
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYmxvY2tfcHJvcG9zYWxfaGFuZGxlci5kLnRzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL2Jsb2NrX3Byb3Bvc2FsX2hhbmRsZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQ0EsT0FBTyxLQUFLLEVBQUUsVUFBVSxFQUFFLE1BQU0sb0JBQW9CLENBQUM7QUFDckQsT0FBTyxFQUFFLFdBQVcsRUFBRSxnQkFBZ0IsRUFBYyxNQUFNLGlDQUFpQyxDQUFDO0FBRTVGLE9BQU8sRUFBRSxFQUFFLEVBQUUsTUFBTSxnQ0FBZ0MsQ0FBQztBQUlwRCxPQUFPLEVBQUUsWUFBWSxFQUFTLE1BQU0seUJBQXlCLENBQUM7QUFDOUQsT0FBTyxLQUFLLEVBQUUsR0FBRyxFQUFFLE1BQU0sRUFBRSxNQUFNLFlBQVksQ0FBQztBQUM5QyxPQUFPLEVBQUUsc0JBQXNCLEVBQUUsTUFBTSwyQkFBMkIsQ0FBQztBQUNuRSxPQUFPLEtBQUssRUFBYSxPQUFPLEVBQUUsV0FBVyxFQUFFLGFBQWEsRUFBRSxNQUFNLHFCQUFxQixDQUFDO0FBRzFGLE9BQU8sS0FBSyxFQUFFLFdBQVcsRUFBRSx5QkFBeUIsRUFBRSxzQkFBc0IsRUFBRSxNQUFNLGlDQUFpQyxDQUFDO0FBQ3RILE9BQU8sRUFBRSxLQUFLLG1CQUFtQixFQUFtQyxNQUFNLHlCQUF5QixDQUFDO0FBQ3BHLE9BQU8sS0FBSyxFQUFFLGFBQWEsRUFBRSxNQUFNLG1CQUFtQixDQUFDO0FBQ3ZELE9BQU8sS0FBSyxFQUE2QixRQUFRLEVBQUUsRUFBRSxFQUFFLE1BQU0sa0JBQWtCLENBQUM7QUFPaEYsT0FBTyxFQUFFLEtBQUssZUFBZSxFQUFFLEtBQUssTUFBTSxFQUFzQixNQUFNLHlCQUF5QixDQUFDO0FBRWhHLE9BQU8sS0FBSyxFQUFFLDBCQUEwQixFQUFFLE1BQU0seUJBQXlCLENBQUM7QUFDMUUsT0FBTyxLQUFLLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSxjQUFjLENBQUM7QUFFckQsTUFBTSxNQUFN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
@@ -1 +1 @@
1
- {"version":3,"file":"block_proposal_handler.d.ts","sourceRoot":"","sources":["../src/block_proposal_handler.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"block_proposal_handler.d.ts","sourceRoot":"","sources":["../src/block_proposal_handler.ts"],"names":[],"mappings":"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"}
@@ -65,12 +65,14 @@ 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
- import { getTimestampForSlot } from '@aztec/stdlib/epoch-helpers';
74
+ import { getEpochAtSlot, getTimestampForSlot } from '@aztec/stdlib/epoch-helpers';
75
+ import { Gas } from '@aztec/stdlib/gas';
74
76
  import { computeInHashFromL1ToL2Messages } from '@aztec/stdlib/messaging';
75
77
  import { ReExFailedTxsError, ReExStateMismatchError, ReExTimeoutError, TransactionsNotAvailableError } from '@aztec/stdlib/validators';
76
78
  import { getTelemetryClient } from '@aztec/telemetry-client';
@@ -81,18 +83,20 @@ export class BlockProposalHandler {
81
83
  l1ToL2MessageSource;
82
84
  txProvider;
83
85
  blockProposalValidator;
86
+ epochCache;
84
87
  config;
85
88
  metrics;
86
89
  dateProvider;
87
90
  log;
88
91
  tracer;
89
- constructor(checkpointsBuilder, worldState, blockSource, l1ToL2MessageSource, txProvider, blockProposalValidator, config, metrics, dateProvider = new DateProvider(), telemetry = getTelemetryClient(), log = createLogger('validator:block-proposal-handler')){
92
+ constructor(checkpointsBuilder, worldState, blockSource, l1ToL2MessageSource, txProvider, blockProposalValidator, epochCache, config, metrics, dateProvider = new DateProvider(), telemetry = getTelemetryClient(), log = createLogger('validator:block-proposal-handler')){
90
93
  this.checkpointsBuilder = checkpointsBuilder;
91
94
  this.worldState = worldState;
92
95
  this.blockSource = blockSource;
93
96
  this.l1ToL2MessageSource = l1ToL2MessageSource;
94
97
  this.txProvider = txProvider;
95
98
  this.blockProposalValidator = blockProposalValidator;
99
+ this.epochCache = epochCache;
96
100
  this.config = config;
97
101
  this.metrics = metrics;
98
102
  this.dateProvider = dateProvider;
@@ -102,23 +106,27 @@ export class BlockProposalHandler {
102
106
  }
103
107
  this.tracer = telemetry.getTracer('BlockProposalHandler');
104
108
  }
105
- registerForReexecution(p2pClient) {
106
- // Non-validator handler that re-executes for monitoring but does not attest.
109
+ register(p2pClient, shouldReexecute) {
110
+ // Non-validator handler that processes or re-executes for monitoring but does not attest.
107
111
  // Returns boolean indicating whether the proposal was valid.
108
112
  const handler = async (proposal, proposalSender)=>{
109
113
  try {
110
- const result = await this.handleBlockProposal(proposal, proposalSender, true);
114
+ const { slotNumber, blockNumber } = proposal;
115
+ const result = await this.handleBlockProposal(proposal, proposalSender, shouldReexecute);
111
116
  if (result.isValid) {
112
- this.log.info(`Non-validator reexecution completed for slot ${proposal.slotNumber}`, {
117
+ this.log.info(`Non-validator block proposal ${blockNumber} at slot ${slotNumber} handled`, {
113
118
  blockNumber: result.blockNumber,
119
+ slotNumber,
114
120
  reexecutionTimeMs: result.reexecutionResult?.reexecutionTimeMs,
115
121
  totalManaUsed: result.reexecutionResult?.totalManaUsed,
116
- numTxs: result.reexecutionResult?.block?.body?.txEffects?.length ?? 0
122
+ numTxs: result.reexecutionResult?.block?.body?.txEffects?.length ?? 0,
123
+ reexecuted: shouldReexecute
117
124
  });
118
125
  return true;
119
126
  } else {
120
- this.log.warn(`Non-validator reexecution failed for slot ${proposal.slotNumber}`, {
127
+ this.log.warn(`Non-validator block proposal ${blockNumber} at slot ${slotNumber} failed processing with ${result.reason}`, {
121
128
  blockNumber: result.blockNumber,
129
+ slotNumber,
122
130
  reason: result.reason
123
131
  });
124
132
  return false;
@@ -153,8 +161,8 @@ export class BlockProposalHandler {
153
161
  });
154
162
  // Check that the proposal is from the current proposer, or the next proposer
155
163
  // This should have been handled by the p2p layer, but we double check here out of caution
156
- const invalidProposal = await this.blockProposalValidator.validate(proposal);
157
- if (invalidProposal) {
164
+ const validationResult = await this.blockProposalValidator.validate(proposal);
165
+ if (validationResult.result !== 'accept') {
158
166
  this.log.warn(`Proposal is not valid, skipping processing`, proposalInfo);
159
167
  return {
160
168
  isValid: false,
@@ -162,18 +170,18 @@ export class BlockProposalHandler {
162
170
  };
163
171
  }
164
172
  // Check that the parent proposal is a block we know, otherwise reexecution would fail
165
- const parentBlockHeader = await this.getParentBlock(proposal);
166
- if (parentBlockHeader === undefined) {
173
+ const parentBlock = await this.getParentBlock(proposal);
174
+ if (parentBlock === undefined) {
167
175
  this.log.warn(`Parent block for proposal not found, skipping processing`, proposalInfo);
168
176
  return {
169
177
  isValid: false,
170
178
  reason: 'parent_block_not_found'
171
179
  };
172
180
  }
173
- // Check that the parent block's slot is less than the proposal's slot (should not happen, but we check anyway)
174
- if (parentBlockHeader !== 'genesis' && parentBlockHeader.getSlot() >= slotNumber) {
175
- this.log.warn(`Parent block slot is greater than or equal to proposal slot, skipping processing`, {
176
- parentBlockSlot: parentBlockHeader.getSlot().toString(),
181
+ // Check that the parent block's slot is not greater than the proposal's slot.
182
+ if (parentBlock !== 'genesis' && parentBlock.header.getSlot() > slotNumber) {
183
+ this.log.warn(`Parent block slot is greater than proposal slot, skipping processing`, {
184
+ parentBlockSlot: parentBlock.header.getSlot().toString(),
177
185
  proposalSlot: slotNumber.toString(),
178
186
  ...proposalInfo
179
187
  });
@@ -183,7 +191,7 @@ export class BlockProposalHandler {
183
191
  };
184
192
  }
185
193
  // Compute the block number based on the parent block
186
- const blockNumber = parentBlockHeader === 'genesis' ? BlockNumber(INITIAL_L2_BLOCK_NUM) : BlockNumber(parentBlockHeader.getBlockNumber() + 1);
194
+ const blockNumber = parentBlock === 'genesis' ? BlockNumber(INITIAL_L2_BLOCK_NUM) : BlockNumber(parentBlock.header.getBlockNumber() + 1);
187
195
  // Check that this block number does not exist already
188
196
  const existingBlock = await this.blockSource.getBlockHeader(blockNumber);
189
197
  if (existingBlock) {
@@ -200,8 +208,16 @@ export class BlockProposalHandler {
200
208
  pinnedPeer: proposalSender,
201
209
  deadline: this.getReexecutionDeadline(slotNumber, config)
202
210
  });
211
+ // If reexecution is disabled, bail. We are just interested in triggering tx collection.
212
+ if (!shouldReexecute) {
213
+ this.log.info(`Received valid block ${blockNumber} proposal at index ${proposal.indexWithinCheckpoint} on slot ${slotNumber}`, proposalInfo);
214
+ return {
215
+ isValid: true,
216
+ blockNumber
217
+ };
218
+ }
203
219
  // Compute the checkpoint number for this block and validate checkpoint consistency
204
- const checkpointResult = await this.computeCheckpointNumber(proposal, parentBlockHeader, proposalInfo);
220
+ const checkpointResult = this.computeCheckpointNumber(proposal, parentBlock, proposalInfo);
205
221
  if (checkpointResult.reason) {
206
222
  return {
207
223
  isValid: false,
@@ -238,29 +254,32 @@ export class BlockProposalHandler {
238
254
  reason: 'txs_not_available'
239
255
  };
240
256
  }
257
+ // Collect the out hashes of all the checkpoints before this one in the same epoch
258
+ const epoch = getEpochAtSlot(slotNumber, this.epochCache.getL1Constants());
259
+ const previousCheckpointOutHashes = (await this.blockSource.getCheckpointsDataForEpoch(epoch)).filter((c)=>c.checkpointNumber < checkpointNumber).map((c)=>c.checkpointOutHash);
241
260
  // Try re-executing the transactions in the proposal if needed
242
261
  let reexecutionResult;
243
- if (shouldReexecute) {
244
- try {
245
- this.log.verbose(`Re-executing transactions in the proposal`, proposalInfo);
246
- reexecutionResult = await this.reexecuteTransactions(proposal, blockNumber, checkpointNumber, txs, l1ToL2Messages);
247
- } catch (error) {
248
- this.log.error(`Error reexecuting txs while processing block proposal`, error, proposalInfo);
249
- const reason = this.getReexecuteFailureReason(error);
250
- return {
251
- isValid: false,
252
- blockNumber,
253
- reason,
254
- reexecutionResult
255
- };
256
- }
262
+ try {
263
+ this.log.verbose(`Re-executing transactions in the proposal`, proposalInfo);
264
+ reexecutionResult = await this.reexecuteTransactions(proposal, blockNumber, checkpointNumber, txs, l1ToL2Messages, previousCheckpointOutHashes);
265
+ } catch (error) {
266
+ this.log.error(`Error reexecuting txs while processing block proposal`, error, proposalInfo);
267
+ const reason = this.getReexecuteFailureReason(error);
268
+ return {
269
+ isValid: false,
270
+ blockNumber,
271
+ reason,
272
+ reexecutionResult
273
+ };
257
274
  }
258
275
  // If we succeeded, push this block into the archiver (unless disabled)
259
- // TODO(palla/mbps): Change default to false once block sync is stable.
260
276
  if (reexecutionResult?.block && this.config.skipPushProposedBlocksToArchiver === false) {
261
277
  await this.blockSource.addBlock(reexecutionResult?.block);
262
278
  }
263
- this.log.info(`Successfully processed block ${blockNumber} proposal at index ${proposal.indexWithinCheckpoint} on slot ${slotNumber}`, proposalInfo);
279
+ this.log.info(`Successfully re-executed block ${blockNumber} proposal at index ${proposal.indexWithinCheckpoint} on slot ${slotNumber}`, {
280
+ ...proposalInfo,
281
+ ...pick(reexecutionResult, 'reexecutionTimeMs', 'totalManaUsed')
282
+ });
264
283
  return {
265
284
  isValid: true,
266
285
  blockNumber,
@@ -279,7 +298,7 @@ export class BlockProposalHandler {
279
298
  const currentTime = this.dateProvider.now();
280
299
  const timeoutDurationMs = deadline.getTime() - currentTime;
281
300
  try {
282
- 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));
301
+ 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));
283
302
  } catch (err) {
284
303
  if (err instanceof TimeoutError) {
285
304
  this.log.debug(`Timed out getting parent block by archive root`, {
@@ -293,8 +312,8 @@ export class BlockProposalHandler {
293
312
  return undefined;
294
313
  }
295
314
  }
296
- async computeCheckpointNumber(proposal, parentBlockHeader, proposalInfo) {
297
- if (parentBlockHeader === 'genesis') {
315
+ computeCheckpointNumber(proposal, parentBlock, proposalInfo) {
316
+ if (parentBlock === 'genesis') {
298
317
  // First block is in checkpoint 1
299
318
  if (proposal.indexWithinCheckpoint !== 0) {
300
319
  this.log.warn(`First block proposal has non-zero indexWithinCheckpoint`, proposalInfo);
@@ -306,20 +325,9 @@ export class BlockProposalHandler {
306
325
  checkpointNumber: CheckpointNumber.INITIAL
307
326
  };
308
327
  }
309
- // Get the parent block to find its checkpoint number
310
- // TODO(palla/mbps): The block header should include the checkpoint number to avoid this lookup,
311
- // or at least the L2BlockSource should return a different struct that includes it.
312
- const parentBlockNumber = parentBlockHeader.getBlockNumber();
313
- const parentBlock = await this.blockSource.getL2BlockNew(parentBlockNumber);
314
- if (!parentBlock) {
315
- this.log.warn(`Parent block ${parentBlockNumber} not found in archiver`, proposalInfo);
316
- return {
317
- reason: 'invalid_proposal'
318
- };
319
- }
320
328
  if (proposal.indexWithinCheckpoint === 0) {
321
329
  // If this is the first block in a new checkpoint, increment the checkpoint number
322
- if (!(proposal.blockHeader.getSlot() > parentBlockHeader.getSlot())) {
330
+ if (!(proposal.blockHeader.getSlot() > parentBlock.header.getSlot())) {
323
331
  this.log.warn(`Slot should be greater than parent block slot for first block in checkpoint`, proposalInfo);
324
332
  return {
325
333
  reason: 'invalid_proposal'
@@ -336,7 +344,7 @@ export class BlockProposalHandler {
336
344
  reason: 'invalid_proposal'
337
345
  };
338
346
  }
339
- if (proposal.blockHeader.getSlot() !== parentBlockHeader.getSlot()) {
347
+ if (proposal.blockHeader.getSlot() !== parentBlock.header.getSlot()) {
340
348
  this.log.warn(`Slot should be equal to parent block slot for non-first block in checkpoint`, proposalInfo);
341
349
  return {
342
350
  reason: 'invalid_proposal'
@@ -434,23 +442,7 @@ export class BlockProposalHandler {
434
442
  }
435
443
  getReexecutionDeadline(slot, config) {
436
444
  const nextSlotTimestampSeconds = Number(getTimestampForSlot(SlotNumber(slot + 1), config));
437
- const msNeededForPropagationAndPublishing = this.config.validatorReexecuteDeadlineMs;
438
- return new Date(nextSlotTimestampSeconds * 1000 - msNeededForPropagationAndPublishing);
439
- }
440
- /**
441
- * Gets all prior blocks in the same checkpoint (same slot and checkpoint number) up to but not including upToBlockNumber.
442
- */ async getBlocksInCheckpoint(slot, upToBlockNumber, checkpointNumber) {
443
- const blocks = [];
444
- let currentBlockNumber = BlockNumber(upToBlockNumber - 1);
445
- while(currentBlockNumber >= INITIAL_L2_BLOCK_NUM){
446
- const block = await this.blockSource.getL2BlockNew(currentBlockNumber);
447
- if (!block || block.header.getSlot() !== slot || block.checkpointNumber !== checkpointNumber) {
448
- break;
449
- }
450
- blocks.unshift(block);
451
- currentBlockNumber = BlockNumber(currentBlockNumber - 1);
452
- }
453
- return blocks;
445
+ return new Date(nextSlotTimestampSeconds * 1000);
454
446
  }
455
447
  getReexecuteFailureReason(err) {
456
448
  if (err instanceof ReExStateMismatchError) {
@@ -463,7 +455,7 @@ export class BlockProposalHandler {
463
455
  return 'unknown_error';
464
456
  }
465
457
  }
466
- async reexecuteTransactions(proposal, blockNumber, checkpointNumber, txs, l1ToL2Messages) {
458
+ async reexecuteTransactions(proposal, blockNumber, checkpointNumber, txs, l1ToL2Messages, previousCheckpointOutHashes) {
467
459
  const env = {
468
460
  stack: [],
469
461
  error: void 0,
@@ -480,34 +472,41 @@ export class BlockProposalHandler {
480
472
  const timer = new Timer();
481
473
  const slot = proposal.slotNumber;
482
474
  const config = this.checkpointsBuilder.getConfig();
483
- // Get prior blocks in this checkpoint (same slot and checkpoint number)
484
- const priorBlocks = await this.getBlocksInCheckpoint(slot, blockNumber, checkpointNumber);
475
+ // Get prior blocks in this checkpoint (same slot before current block)
476
+ const allBlocksInSlot = await this.blockSource.getBlocksForSlot(slot);
477
+ const priorBlocks = allBlocksInSlot.filter((b)=>b.number < blockNumber && b.header.getSlot() === slot);
485
478
  // Fork before the block to be built
486
479
  const parentBlockNumber = BlockNumber(blockNumber - 1);
487
- const fork = _ts_add_disposable_resource(env, await this.worldState.fork(parentBlockNumber), false);
488
- // Build checkpoint constants from proposal (excludes blockNumber and timestamp which are per-block)
480
+ await this.worldState.syncImmediate(parentBlockNumber);
481
+ const fork = _ts_add_disposable_resource(env, await this.worldState.fork(parentBlockNumber), true);
482
+ // Build checkpoint constants from proposal (excludes blockNumber which is per-block)
489
483
  const constants = {
490
484
  chainId: new Fr(config.l1ChainId),
491
485
  version: new Fr(config.rollupVersion),
492
486
  slotNumber: slot,
487
+ timestamp: blockHeader.globalVariables.timestamp,
493
488
  coinbase: blockHeader.globalVariables.coinbase,
494
489
  feeRecipient: blockHeader.globalVariables.feeRecipient,
495
490
  gasFees: blockHeader.globalVariables.gasFees
496
491
  };
497
492
  // Create checkpoint builder with prior blocks
498
- const checkpointBuilder = await this.checkpointsBuilder.openCheckpoint(checkpointNumber, constants, l1ToL2Messages, fork, priorBlocks);
493
+ const checkpointBuilder = await this.checkpointsBuilder.openCheckpoint(checkpointNumber, constants, 0n, l1ToL2Messages, previousCheckpointOutHashes, fork, priorBlocks, this.log.getBindings());
499
494
  // Build the new block
500
495
  const deadline = this.getReexecutionDeadline(slot, config);
496
+ const maxBlockGas = this.config.validateMaxL2BlockGas !== undefined || this.config.validateMaxDABlockGas !== undefined ? new Gas(this.config.validateMaxDABlockGas ?? Infinity, this.config.validateMaxL2BlockGas ?? Infinity) : undefined;
501
497
  const result = await checkpointBuilder.buildBlock(txs, blockNumber, blockHeader.globalVariables.timestamp, {
502
498
  deadline,
503
- expectedEndState: blockHeader.state
499
+ expectedEndState: blockHeader.state,
500
+ maxTransactions: this.config.validateMaxTxsPerBlock,
501
+ maxBlockGas
504
502
  });
505
503
  const { block, failedTxs } = result;
506
504
  const numFailedTxs = failedTxs.length;
507
- this.log.verbose(`Transaction re-execution complete for slot ${slot}`, {
505
+ this.log.verbose(`Block proposal ${blockNumber} at slot ${slot} transaction re-execution complete`, {
508
506
  numFailedTxs,
509
507
  numProposalTxs: txHashes.length,
510
508
  numProcessedTxs: block.body.txEffects.length,
509
+ blockNumber,
511
510
  slot
512
511
  });
513
512
  if (numFailedTxs > 0) {
@@ -545,7 +544,8 @@ export class BlockProposalHandler {
545
544
  env.error = e;
546
545
  env.hasError = true;
547
546
  } finally{
548
- _ts_dispose_resources(env);
547
+ const result = _ts_dispose_resources(env);
548
+ if (result) await result;
549
549
  }
550
550
  }
551
551
  }