@aztec/validator-client 0.0.1-commit.f146247c → 0.0.1-commit.f224bb98b
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.
- package/README.md +63 -18
- package/dest/block_proposal_handler.d.ts +5 -4
- package/dest/block_proposal_handler.d.ts.map +1 -1
- package/dest/block_proposal_handler.js +122 -62
- package/dest/checkpoint_builder.d.ts +15 -5
- package/dest/checkpoint_builder.d.ts.map +1 -1
- package/dest/checkpoint_builder.js +80 -25
- package/dest/config.d.ts +1 -1
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +26 -1
- package/dest/duties/validation_service.d.ts +2 -2
- package/dest/duties/validation_service.d.ts.map +1 -1
- package/dest/duties/validation_service.js +5 -11
- package/dest/factory.d.ts +1 -1
- package/dest/factory.d.ts.map +1 -1
- package/dest/factory.js +2 -1
- package/dest/index.d.ts +1 -2
- package/dest/index.d.ts.map +1 -1
- package/dest/index.js +0 -1
- package/dest/key_store/ha_key_store.d.ts +1 -1
- package/dest/key_store/ha_key_store.d.ts.map +1 -1
- package/dest/key_store/ha_key_store.js +3 -3
- package/dest/metrics.d.ts +9 -1
- package/dest/metrics.d.ts.map +1 -1
- package/dest/metrics.js +12 -0
- package/dest/validator.d.ts +35 -8
- package/dest/validator.d.ts.map +1 -1
- package/dest/validator.js +196 -34
- package/package.json +19 -19
- package/src/block_proposal_handler.ts +149 -80
- package/src/checkpoint_builder.ts +90 -14
- package/src/config.ts +26 -1
- package/src/duties/validation_service.ts +11 -10
- package/src/factory.ts +1 -0
- package/src/index.ts +0 -1
- package/src/key_store/ha_key_store.ts +3 -3
- package/src/metrics.ts +18 -0
- package/src/validator.ts +246 -40
- package/dest/tx_validator/index.d.ts +0 -3
- package/dest/tx_validator/index.d.ts.map +0 -1
- package/dest/tx_validator/index.js +0 -2
- package/dest/tx_validator/nullifier_cache.d.ts +0 -14
- package/dest/tx_validator/nullifier_cache.d.ts.map +0 -1
- package/dest/tx_validator/nullifier_cache.js +0 -24
- package/dest/tx_validator/tx_validator_factory.d.ts +0 -19
- package/dest/tx_validator/tx_validator_factory.d.ts.map +0 -1
- package/dest/tx_validator/tx_validator_factory.js +0 -54
- package/src/tx_validator/index.ts +0 -2
- package/src/tx_validator/nullifier_cache.ts +0 -30
- package/src/tx_validator/tx_validator_factory.ts +0 -154
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.
|
|
91
|
-
4.
|
|
92
|
-
5.
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
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
|
-
| `
|
|
165
|
-
| `
|
|
166
|
-
| `
|
|
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
|
|
@@ -8,11 +8,11 @@ import type { L2Block, L2BlockSink, L2BlockSource } from '@aztec/stdlib/block';
|
|
|
8
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 {
|
|
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
|
-
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';
|
|
16
16
|
type ReexecuteTransactionsResult = {
|
|
17
17
|
block: L2Block;
|
|
18
18
|
failedTxs: FailedTx[];
|
|
@@ -45,7 +45,7 @@ export declare class BlockProposalHandler {
|
|
|
45
45
|
private log;
|
|
46
46
|
readonly tracer: Tracer;
|
|
47
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
|
-
|
|
48
|
+
register(p2pClient: P2P, shouldReexecute: boolean): BlockProposalHandler;
|
|
49
49
|
handleBlockProposal(proposal: BlockProposal, proposalSender: PeerId, shouldReexecute: boolean): Promise<BlockProposalValidationResult>;
|
|
50
50
|
private getParentBlock;
|
|
51
51
|
private computeCheckpointNumber;
|
|
@@ -56,8 +56,9 @@ export declare class BlockProposalHandler {
|
|
|
56
56
|
*/
|
|
57
57
|
private validateNonFirstBlockInCheckpoint;
|
|
58
58
|
private getReexecutionDeadline;
|
|
59
|
+
private waitForBlockSourceSync;
|
|
59
60
|
private getReexecuteFailureReason;
|
|
60
61
|
reexecuteTransactions(proposal: BlockProposal, blockNumber: BlockNumber, checkpointNumber: CheckpointNumber, txs: Tx[], l1ToL2Messages: Fr[], previousCheckpointOutHashes: Fr[]): Promise<ReexecuteTransactionsResult>;
|
|
61
62
|
}
|
|
62
63
|
export {};
|
|
63
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
64
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYmxvY2tfcHJvcG9zYWxfaGFuZGxlci5kLnRzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL2Jsb2NrX3Byb3Bvc2FsX2hhbmRsZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQ0EsT0FBTyxLQUFLLEVBQUUsVUFBVSxFQUFFLE1BQU0sb0JBQW9CLENBQUM7QUFDckQsT0FBTyxFQUFFLFdBQVcsRUFBRSxnQkFBZ0IsRUFBYyxNQUFNLGlDQUFpQyxDQUFDO0FBRTVGLE9BQU8sRUFBRSxFQUFFLEVBQUUsTUFBTSxnQ0FBZ0MsQ0FBQztBQUlwRCxPQUFPLEVBQUUsWUFBWSxFQUFTLE1BQU0seUJBQXlCLENBQUM7QUFDOUQsT0FBTyxLQUFLLEVBQUUsR0FBRyxFQUFFLE1BQU0sRUFBRSxNQUFNLFlBQVksQ0FBQztBQUM5QyxPQUFPLEVBQUUsc0JBQXNCLEVBQUUsTUFBTSwyQkFBMkIsQ0FBQztBQUNuRSxPQUFPLEtBQUssRUFBYSxPQUFPLEVBQUUsV0FBVyxFQUFFLGFBQWEsRUFBRSxNQUFNLHFCQUFxQixDQUFDO0FBRzFGLE9BQU8sS0FBSyxFQUFFLFdBQVcsRUFBRSx5QkFBeUIsRUFBRSxzQkFBc0IsRUFBRSxNQUFNLGlDQUFpQyxDQUFDO0FBQ3RILE9BQU8sRUFBRSxLQUFLLG1CQUFtQixFQUFtQyxNQUFNLHlCQUF5QixDQUFDO0FBQ3BHLE9BQU8sS0FBSyxFQUFFLGFBQWEsRUFBRSxNQUFNLG1CQUFtQixDQUFDO0FBRXZELE9BQU8sS0FBSyxFQUE2QixRQUFRLEVBQUUsRUFBRSxFQUFFLE1BQU0sa0JBQWtCLENBQUM7QUFRaEYsT0FBTyxFQUFFLEtBQUssZUFBZSxFQUFFLEtBQUssTUFBTSxFQUFzQixNQUFNLHlCQUF5QixDQUFDO0FBRWhHLE9BQU8sS0FBSyxFQUFFLDBCQUEwQixFQUFFLE1BQU0seUJBQXlCLENBQUM7QUFDMUUsT0FBTyxLQUFLLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSxjQUFjLENBQUM7QUFFckQsTUFBTSxNQUFN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
|
|
@@ -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;AAE5F,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,sBAAsB,EAAE,MAAM,2BAA2B,CAAC;AACnE,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"block_proposal_handler.d.ts","sourceRoot":"","sources":["../src/block_proposal_handler.ts"],"names":[],"mappings":"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"}
|
|
@@ -65,15 +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 {
|
|
68
|
+
import { pick } from '@aztec/foundation/collection';
|
|
69
69
|
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
70
70
|
import { TimeoutError } from '@aztec/foundation/error';
|
|
71
71
|
import { createLogger } from '@aztec/foundation/log';
|
|
72
72
|
import { retryUntil } from '@aztec/foundation/retry';
|
|
73
73
|
import { DateProvider, Timer } from '@aztec/foundation/timer';
|
|
74
74
|
import { getEpochAtSlot, getTimestampForSlot } from '@aztec/stdlib/epoch-helpers';
|
|
75
|
-
import {
|
|
76
|
-
import {
|
|
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';
|
|
77
79
|
import { getTelemetryClient } from '@aztec/telemetry-client';
|
|
78
80
|
export class BlockProposalHandler {
|
|
79
81
|
checkpointsBuilder;
|
|
@@ -105,23 +107,27 @@ export class BlockProposalHandler {
|
|
|
105
107
|
}
|
|
106
108
|
this.tracer = telemetry.getTracer('BlockProposalHandler');
|
|
107
109
|
}
|
|
108
|
-
|
|
109
|
-
// 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.
|
|
110
112
|
// Returns boolean indicating whether the proposal was valid.
|
|
111
113
|
const handler = async (proposal, proposalSender)=>{
|
|
112
114
|
try {
|
|
113
|
-
const
|
|
115
|
+
const { slotNumber, blockNumber } = proposal;
|
|
116
|
+
const result = await this.handleBlockProposal(proposal, proposalSender, shouldReexecute);
|
|
114
117
|
if (result.isValid) {
|
|
115
|
-
this.log.info(`Non-validator
|
|
118
|
+
this.log.info(`Non-validator block proposal ${blockNumber} at slot ${slotNumber} handled`, {
|
|
116
119
|
blockNumber: result.blockNumber,
|
|
120
|
+
slotNumber,
|
|
117
121
|
reexecutionTimeMs: result.reexecutionResult?.reexecutionTimeMs,
|
|
118
122
|
totalManaUsed: result.reexecutionResult?.totalManaUsed,
|
|
119
|
-
numTxs: result.reexecutionResult?.block?.body?.txEffects?.length ?? 0
|
|
123
|
+
numTxs: result.reexecutionResult?.block?.body?.txEffects?.length ?? 0,
|
|
124
|
+
reexecuted: shouldReexecute
|
|
120
125
|
});
|
|
121
126
|
return true;
|
|
122
127
|
} else {
|
|
123
|
-
this.log.warn(`Non-validator
|
|
128
|
+
this.log.warn(`Non-validator block proposal ${blockNumber} at slot ${slotNumber} failed processing with ${result.reason}`, {
|
|
124
129
|
blockNumber: result.blockNumber,
|
|
130
|
+
slotNumber,
|
|
125
131
|
reason: result.reason
|
|
126
132
|
});
|
|
127
133
|
return false;
|
|
@@ -148,7 +154,9 @@ export class BlockProposalHandler {
|
|
|
148
154
|
}
|
|
149
155
|
const proposalInfo = {
|
|
150
156
|
...proposal.toBlockInfo(),
|
|
151
|
-
proposer: proposer.toString()
|
|
157
|
+
proposer: proposer.toString(),
|
|
158
|
+
blockNumber: undefined,
|
|
159
|
+
checkpointNumber: undefined
|
|
152
160
|
};
|
|
153
161
|
this.log.info(`Processing proposal for slot ${slotNumber}`, {
|
|
154
162
|
...proposalInfo,
|
|
@@ -164,9 +172,24 @@ export class BlockProposalHandler {
|
|
|
164
172
|
reason: 'invalid_proposal'
|
|
165
173
|
};
|
|
166
174
|
}
|
|
167
|
-
//
|
|
168
|
-
|
|
169
|
-
|
|
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) {
|
|
170
193
|
this.log.warn(`Parent block for proposal not found, skipping processing`, proposalInfo);
|
|
171
194
|
return {
|
|
172
195
|
isValid: false,
|
|
@@ -174,9 +197,9 @@ export class BlockProposalHandler {
|
|
|
174
197
|
};
|
|
175
198
|
}
|
|
176
199
|
// Check that the parent block's slot is not greater than the proposal's slot.
|
|
177
|
-
if (
|
|
200
|
+
if (parentBlock !== 'genesis' && parentBlock.header.getSlot() > slotNumber) {
|
|
178
201
|
this.log.warn(`Parent block slot is greater than proposal slot, skipping processing`, {
|
|
179
|
-
parentBlockSlot:
|
|
202
|
+
parentBlockSlot: parentBlock.header.getSlot().toString(),
|
|
180
203
|
proposalSlot: slotNumber.toString(),
|
|
181
204
|
...proposalInfo
|
|
182
205
|
});
|
|
@@ -186,7 +209,8 @@ export class BlockProposalHandler {
|
|
|
186
209
|
};
|
|
187
210
|
}
|
|
188
211
|
// Compute the block number based on the parent block
|
|
189
|
-
const blockNumber =
|
|
212
|
+
const blockNumber = parentBlock === 'genesis' ? BlockNumber(INITIAL_L2_BLOCK_NUM) : BlockNumber(parentBlock.header.getBlockNumber() + 1);
|
|
213
|
+
proposalInfo.blockNumber = blockNumber;
|
|
190
214
|
// Check that this block number does not exist already
|
|
191
215
|
const existingBlock = await this.blockSource.getBlockHeader(blockNumber);
|
|
192
216
|
if (existingBlock) {
|
|
@@ -203,8 +227,16 @@ export class BlockProposalHandler {
|
|
|
203
227
|
pinnedPeer: proposalSender,
|
|
204
228
|
deadline: this.getReexecutionDeadline(slotNumber, config)
|
|
205
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
|
+
}
|
|
206
238
|
// Compute the checkpoint number for this block and validate checkpoint consistency
|
|
207
|
-
const checkpointResult =
|
|
239
|
+
const checkpointResult = this.computeCheckpointNumber(proposal, parentBlock, proposalInfo);
|
|
208
240
|
if (checkpointResult.reason) {
|
|
209
241
|
return {
|
|
210
242
|
isValid: false,
|
|
@@ -213,6 +245,7 @@ export class BlockProposalHandler {
|
|
|
213
245
|
};
|
|
214
246
|
}
|
|
215
247
|
const checkpointNumber = checkpointResult.checkpointNumber;
|
|
248
|
+
proposalInfo.checkpointNumber = checkpointNumber;
|
|
216
249
|
// Check that I have the same set of l1ToL2Messages as the proposal
|
|
217
250
|
const l1ToL2Messages = await this.l1ToL2MessageSource.getL1ToL2Messages(checkpointNumber);
|
|
218
251
|
const computedInHash = computeInHashFromL1ToL2Messages(l1ToL2Messages);
|
|
@@ -241,35 +274,32 @@ export class BlockProposalHandler {
|
|
|
241
274
|
reason: 'txs_not_available'
|
|
242
275
|
};
|
|
243
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);
|
|
244
280
|
// Try re-executing the transactions in the proposal if needed
|
|
245
281
|
let reexecutionResult;
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
const
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
reexecutionResult
|
|
257
|
-
}
|
|
258
|
-
this.log.error(`Error reexecuting txs while processing block proposal`, error, proposalInfo);
|
|
259
|
-
const reason = this.getReexecuteFailureReason(error);
|
|
260
|
-
return {
|
|
261
|
-
isValid: false,
|
|
262
|
-
blockNumber,
|
|
263
|
-
reason,
|
|
264
|
-
reexecutionResult
|
|
265
|
-
};
|
|
266
|
-
}
|
|
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
|
+
};
|
|
267
294
|
}
|
|
268
295
|
// If we succeeded, push this block into the archiver (unless disabled)
|
|
269
296
|
if (reexecutionResult?.block && this.config.skipPushProposedBlocksToArchiver === false) {
|
|
270
297
|
await this.blockSource.addBlock(reexecutionResult?.block);
|
|
271
298
|
}
|
|
272
|
-
this.log.info(`Successfully
|
|
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
|
+
});
|
|
273
303
|
return {
|
|
274
304
|
isValid: true,
|
|
275
305
|
blockNumber,
|
|
@@ -288,7 +318,7 @@ export class BlockProposalHandler {
|
|
|
288
318
|
const currentTime = this.dateProvider.now();
|
|
289
319
|
const timeoutDurationMs = deadline.getTime() - currentTime;
|
|
290
320
|
try {
|
|
291
|
-
return await this.blockSource.
|
|
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));
|
|
292
322
|
} catch (err) {
|
|
293
323
|
if (err instanceof TimeoutError) {
|
|
294
324
|
this.log.debug(`Timed out getting parent block by archive root`, {
|
|
@@ -302,8 +332,8 @@ export class BlockProposalHandler {
|
|
|
302
332
|
return undefined;
|
|
303
333
|
}
|
|
304
334
|
}
|
|
305
|
-
|
|
306
|
-
if (
|
|
335
|
+
computeCheckpointNumber(proposal, parentBlock, proposalInfo) {
|
|
336
|
+
if (parentBlock === 'genesis') {
|
|
307
337
|
// First block is in checkpoint 1
|
|
308
338
|
if (proposal.indexWithinCheckpoint !== 0) {
|
|
309
339
|
this.log.warn(`First block proposal has non-zero indexWithinCheckpoint`, proposalInfo);
|
|
@@ -315,20 +345,9 @@ export class BlockProposalHandler {
|
|
|
315
345
|
checkpointNumber: CheckpointNumber.INITIAL
|
|
316
346
|
};
|
|
317
347
|
}
|
|
318
|
-
// Get the parent block to find its checkpoint number
|
|
319
|
-
// TODO(palla/mbps): The block header should include the checkpoint number to avoid this lookup,
|
|
320
|
-
// or at least the L2BlockSource should return a different struct that includes it.
|
|
321
|
-
const parentBlockNumber = parentBlockHeader.getBlockNumber();
|
|
322
|
-
const parentBlock = await this.blockSource.getL2Block(parentBlockNumber);
|
|
323
|
-
if (!parentBlock) {
|
|
324
|
-
this.log.warn(`Parent block ${parentBlockNumber} not found in archiver`, proposalInfo);
|
|
325
|
-
return {
|
|
326
|
-
reason: 'invalid_proposal'
|
|
327
|
-
};
|
|
328
|
-
}
|
|
329
348
|
if (proposal.indexWithinCheckpoint === 0) {
|
|
330
349
|
// If this is the first block in a new checkpoint, increment the checkpoint number
|
|
331
|
-
if (!(proposal.blockHeader.getSlot() >
|
|
350
|
+
if (!(proposal.blockHeader.getSlot() > parentBlock.header.getSlot())) {
|
|
332
351
|
this.log.warn(`Slot should be greater than parent block slot for first block in checkpoint`, proposalInfo);
|
|
333
352
|
return {
|
|
334
353
|
reason: 'invalid_proposal'
|
|
@@ -345,7 +364,7 @@ export class BlockProposalHandler {
|
|
|
345
364
|
reason: 'invalid_proposal'
|
|
346
365
|
};
|
|
347
366
|
}
|
|
348
|
-
if (proposal.blockHeader.getSlot() !==
|
|
367
|
+
if (proposal.blockHeader.getSlot() !== parentBlock.header.getSlot()) {
|
|
349
368
|
this.log.warn(`Slot should be equal to parent block slot for non-first block in checkpoint`, proposalInfo);
|
|
350
369
|
return {
|
|
351
370
|
reason: 'invalid_proposal'
|
|
@@ -445,8 +464,37 @@ export class BlockProposalHandler {
|
|
|
445
464
|
const nextSlotTimestampSeconds = Number(getTimestampForSlot(SlotNumber(slot + 1), config));
|
|
446
465
|
return new Date(nextSlotTimestampSeconds * 1000);
|
|
447
466
|
}
|
|
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;
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
}
|
|
448
494
|
getReexecuteFailureReason(err) {
|
|
449
|
-
if (err instanceof
|
|
495
|
+
if (err instanceof ReExInitialStateMismatchError) {
|
|
496
|
+
return 'initial_state_mismatch';
|
|
497
|
+
} else if (err instanceof ReExStateMismatchError) {
|
|
450
498
|
return 'state_mismatch';
|
|
451
499
|
} else if (err instanceof ReExFailedTxsError) {
|
|
452
500
|
return 'failed_txs';
|
|
@@ -479,30 +527,41 @@ export class BlockProposalHandler {
|
|
|
479
527
|
// Fork before the block to be built
|
|
480
528
|
const parentBlockNumber = BlockNumber(blockNumber - 1);
|
|
481
529
|
await this.worldState.syncImmediate(parentBlockNumber);
|
|
482
|
-
const fork = _ts_add_disposable_resource(env, await this.worldState.fork(parentBlockNumber),
|
|
483
|
-
//
|
|
530
|
+
const fork = _ts_add_disposable_resource(env, await this.worldState.fork(parentBlockNumber), true);
|
|
531
|
+
// Verify the fork's archive root matches the proposal's expected last archive.
|
|
532
|
+
// If they don't match, our world state synced to a different chain and reexecution would fail.
|
|
533
|
+
const forkArchiveRoot = new Fr((await fork.getTreeInfo(MerkleTreeId.ARCHIVE)).root);
|
|
534
|
+
if (!forkArchiveRoot.equals(proposal.blockHeader.lastArchive.root)) {
|
|
535
|
+
throw new ReExInitialStateMismatchError(proposal.blockHeader.lastArchive.root, forkArchiveRoot);
|
|
536
|
+
}
|
|
537
|
+
// Build checkpoint constants from proposal (excludes blockNumber which is per-block)
|
|
484
538
|
const constants = {
|
|
485
539
|
chainId: new Fr(config.l1ChainId),
|
|
486
540
|
version: new Fr(config.rollupVersion),
|
|
487
541
|
slotNumber: slot,
|
|
542
|
+
timestamp: blockHeader.globalVariables.timestamp,
|
|
488
543
|
coinbase: blockHeader.globalVariables.coinbase,
|
|
489
544
|
feeRecipient: blockHeader.globalVariables.feeRecipient,
|
|
490
545
|
gasFees: blockHeader.globalVariables.gasFees
|
|
491
546
|
};
|
|
492
547
|
// Create checkpoint builder with prior blocks
|
|
493
|
-
const checkpointBuilder = await this.checkpointsBuilder.openCheckpoint(checkpointNumber, constants, l1ToL2Messages, previousCheckpointOutHashes, fork, priorBlocks, this.log.getBindings());
|
|
548
|
+
const checkpointBuilder = await this.checkpointsBuilder.openCheckpoint(checkpointNumber, constants, 0n, l1ToL2Messages, previousCheckpointOutHashes, fork, priorBlocks, this.log.getBindings());
|
|
494
549
|
// Build the new block
|
|
495
550
|
const deadline = this.getReexecutionDeadline(slot, config);
|
|
551
|
+
const maxBlockGas = this.config.validateMaxL2BlockGas !== undefined || this.config.validateMaxDABlockGas !== undefined ? new Gas(this.config.validateMaxDABlockGas ?? Infinity, this.config.validateMaxL2BlockGas ?? Infinity) : undefined;
|
|
496
552
|
const result = await checkpointBuilder.buildBlock(txs, blockNumber, blockHeader.globalVariables.timestamp, {
|
|
497
553
|
deadline,
|
|
498
|
-
expectedEndState: blockHeader.state
|
|
554
|
+
expectedEndState: blockHeader.state,
|
|
555
|
+
maxTransactions: this.config.validateMaxTxsPerBlock,
|
|
556
|
+
maxBlockGas
|
|
499
557
|
});
|
|
500
558
|
const { block, failedTxs } = result;
|
|
501
559
|
const numFailedTxs = failedTxs.length;
|
|
502
|
-
this.log.verbose(`
|
|
560
|
+
this.log.verbose(`Block proposal ${blockNumber} at slot ${slot} transaction re-execution complete`, {
|
|
503
561
|
numFailedTxs,
|
|
504
562
|
numProposalTxs: txHashes.length,
|
|
505
563
|
numProcessedTxs: block.body.txEffects.length,
|
|
564
|
+
blockNumber,
|
|
506
565
|
slot
|
|
507
566
|
});
|
|
508
567
|
if (numFailedTxs > 0) {
|
|
@@ -540,7 +599,8 @@ export class BlockProposalHandler {
|
|
|
540
599
|
env.error = e;
|
|
541
600
|
env.hasError = true;
|
|
542
601
|
} finally{
|
|
543
|
-
_ts_dispose_resources(env);
|
|
602
|
+
const result = _ts_dispose_resources(env);
|
|
603
|
+
if (result) await result;
|
|
544
604
|
}
|
|
545
605
|
}
|
|
546
606
|
}
|
|
@@ -9,6 +9,7 @@ import { Checkpoint } from '@aztec/stdlib/checkpoint';
|
|
|
9
9
|
import type { ContractDataSource } from '@aztec/stdlib/contract';
|
|
10
10
|
import type { L1RollupConstants } from '@aztec/stdlib/epoch-helpers';
|
|
11
11
|
import { type BuildBlockInCheckpointResult, type FullNodeBlockBuilderConfig, type ICheckpointBlockBuilder, type ICheckpointsBuilder, type MerkleTreeWriteOperations, type PublicProcessorLimits, type WorldStateSynchronizer } from '@aztec/stdlib/interfaces/server';
|
|
12
|
+
import { type DebugLogStore } from '@aztec/stdlib/logs';
|
|
12
13
|
import { type CheckpointGlobalVariables, GlobalVariables, StateReference, Tx } from '@aztec/stdlib/tx';
|
|
13
14
|
import { type TelemetryClient } from '@aztec/telemetry-client';
|
|
14
15
|
export type { BuildBlockInCheckpointResult } from '@aztec/stdlib/interfaces/server';
|
|
@@ -23,11 +24,13 @@ export declare class CheckpointBuilder implements ICheckpointBlockBuilder {
|
|
|
23
24
|
private contractDataSource;
|
|
24
25
|
private dateProvider;
|
|
25
26
|
private telemetryClient;
|
|
27
|
+
private debugLogStore;
|
|
26
28
|
private log;
|
|
27
|
-
constructor(checkpointBuilder: LightweightCheckpointBuilder, fork: MerkleTreeWriteOperations, config: FullNodeBlockBuilderConfig, contractDataSource: ContractDataSource, dateProvider: DateProvider, telemetryClient: TelemetryClient, bindings?: LoggerBindings);
|
|
29
|
+
constructor(checkpointBuilder: LightweightCheckpointBuilder, fork: MerkleTreeWriteOperations, config: FullNodeBlockBuilderConfig, contractDataSource: ContractDataSource, dateProvider: DateProvider, telemetryClient: TelemetryClient, bindings?: LoggerBindings, debugLogStore?: DebugLogStore);
|
|
28
30
|
getConstantData(): CheckpointGlobalVariables;
|
|
29
31
|
/**
|
|
30
32
|
* Builds a single block within this checkpoint.
|
|
33
|
+
* Automatically caps gas and blob field limits based on checkpoint-level budgets and prior blocks.
|
|
31
34
|
*/
|
|
32
35
|
buildBlock(pendingTxs: Iterable<Tx> | AsyncIterable<Tx>, blockNumber: BlockNumber, timestamp: bigint, opts?: PublicProcessorLimits & {
|
|
33
36
|
expectedEndState?: StateReference;
|
|
@@ -36,6 +39,12 @@ export declare class CheckpointBuilder implements ICheckpointBlockBuilder {
|
|
|
36
39
|
completeCheckpoint(): Promise<Checkpoint>;
|
|
37
40
|
/** Gets the checkpoint currently in progress. */
|
|
38
41
|
getCheckpoint(): Promise<Checkpoint>;
|
|
42
|
+
/**
|
|
43
|
+
* Caps per-block gas and blob field limits by remaining checkpoint-level budgets.
|
|
44
|
+
* Computes remaining L2 gas (mana), DA gas, and blob fields from blocks already added to the checkpoint,
|
|
45
|
+
* then returns opts with maxBlockGas and maxBlobFields capped accordingly.
|
|
46
|
+
*/
|
|
47
|
+
protected capLimitsByCheckpointBudgets(opts: PublicProcessorLimits): Pick<PublicProcessorLimits, 'maxBlockGas' | 'maxBlobFields' | 'maxTransactions'>;
|
|
39
48
|
protected makeBlockBuilderDeps(globalVariables: GlobalVariables, fork: MerkleTreeWriteOperations): Promise<{
|
|
40
49
|
processor: PublicProcessor;
|
|
41
50
|
validator: import("@aztec/stdlib/interfaces/server").PublicProcessorValidator;
|
|
@@ -48,19 +57,20 @@ export declare class FullNodeCheckpointsBuilder implements ICheckpointsBuilder {
|
|
|
48
57
|
private contractDataSource;
|
|
49
58
|
private dateProvider;
|
|
50
59
|
private telemetryClient;
|
|
60
|
+
private debugLogStore;
|
|
51
61
|
private log;
|
|
52
|
-
constructor(config: FullNodeBlockBuilderConfig & Pick<L1RollupConstants, 'l1GenesisTime' | 'slotDuration'>, worldState: WorldStateSynchronizer, contractDataSource: ContractDataSource, dateProvider: DateProvider, telemetryClient?: TelemetryClient);
|
|
62
|
+
constructor(config: FullNodeBlockBuilderConfig & Pick<L1RollupConstants, 'l1GenesisTime' | 'slotDuration'>, worldState: WorldStateSynchronizer, contractDataSource: ContractDataSource, dateProvider: DateProvider, telemetryClient?: TelemetryClient, debugLogStore?: DebugLogStore);
|
|
53
63
|
getConfig(): FullNodeBlockBuilderConfig;
|
|
54
64
|
updateConfig(config: Partial<FullNodeBlockBuilderConfig>): void;
|
|
55
65
|
/**
|
|
56
66
|
* Starts a new checkpoint and returns a CheckpointBuilder to build blocks within it.
|
|
57
67
|
*/
|
|
58
|
-
startCheckpoint(checkpointNumber: CheckpointNumber, constants: CheckpointGlobalVariables, l1ToL2Messages: Fr[], previousCheckpointOutHashes: Fr[], fork: MerkleTreeWriteOperations, bindings?: LoggerBindings): Promise<CheckpointBuilder>;
|
|
68
|
+
startCheckpoint(checkpointNumber: CheckpointNumber, constants: CheckpointGlobalVariables, feeAssetPriceModifier: bigint, l1ToL2Messages: Fr[], previousCheckpointOutHashes: Fr[], fork: MerkleTreeWriteOperations, bindings?: LoggerBindings): Promise<CheckpointBuilder>;
|
|
59
69
|
/**
|
|
60
70
|
* Opens a checkpoint, either starting fresh or resuming from existing blocks.
|
|
61
71
|
*/
|
|
62
|
-
openCheckpoint(checkpointNumber: CheckpointNumber, constants: CheckpointGlobalVariables, l1ToL2Messages: Fr[], previousCheckpointOutHashes: Fr[], fork: MerkleTreeWriteOperations, existingBlocks?: L2Block[], bindings?: LoggerBindings): Promise<CheckpointBuilder>;
|
|
72
|
+
openCheckpoint(checkpointNumber: CheckpointNumber, constants: CheckpointGlobalVariables, feeAssetPriceModifier: bigint, l1ToL2Messages: Fr[], previousCheckpointOutHashes: Fr[], fork: MerkleTreeWriteOperations, existingBlocks?: L2Block[], bindings?: LoggerBindings): Promise<CheckpointBuilder>;
|
|
63
73
|
/** Returns a fork of the world state at the given block number. */
|
|
64
74
|
getFork(blockNumber: BlockNumber): Promise<MerkleTreeWriteOperations>;
|
|
65
75
|
}
|
|
66
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
76
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2hlY2twb2ludF9idWlsZGVyLmQudHMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvY2hlY2twb2ludF9idWlsZGVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUVBLE9BQU8sRUFBRSxXQUFXLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSxpQ0FBaUMsQ0FBQztBQUVoRixPQUFPLEVBQUUsRUFBRSxFQUFFLE1BQU0sZ0NBQWdDLENBQUM7QUFDcEQsT0FBTyxFQUFlLEtBQUssY0FBYyxFQUFnQixNQUFNLHVCQUF1QixDQUFDO0FBRXZGLE9BQU8sRUFBRSxZQUFZLEVBQVcsTUFBTSx5QkFBeUIsQ0FBQztBQUVoRSxPQUFPLEVBQUUsNEJBQTRCLEVBQUUsTUFBTSw0QkFBNEIsQ0FBQztBQUMxRSxPQUFPLEVBR0wsZUFBZSxFQUVoQixNQUFNLHlCQUF5QixDQUFDO0FBQ2pDLE9BQU8sRUFBRSxPQUFPLEVBQUUsTUFBTSxxQkFBcUIsQ0FBQztBQUM5QyxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sMEJBQTBCLENBQUM7QUFDdEQsT0FBTyxLQUFLLEVBQUUsa0JBQWtCLEVBQUUsTUFBTSx3QkFBd0IsQ0FBQztBQUNqRSxPQUFPLEtBQUssRUFBRSxpQkFBaUIsRUFBRSxNQUFNLDZCQUE2QixDQUFDO0FBRXJFLE9BQU8sRUFDTCxLQUFLLDRCQUE0QixFQUNqQyxLQUFLLDBCQUEwQixFQUUvQixLQUFLLHVCQUF1QixFQUM1QixLQUFLLG1CQUFtQixFQUN4QixLQUFLLHlCQUF5QixFQUU5QixLQUFLLHFCQUFxQixFQUMxQixLQUFLLHNCQUFzQixFQUM1QixNQUFNLGlDQUFpQyxDQUFDO0FBQ3pDLE9BQU8sRUFBRSxLQUFLLGFBQWEsRUFBcUIsTUFBTSxvQkFBb0IsQ0FBQztBQUUzRSxPQUFPLEVBQUUsS0FBSyx5QkFBeUIsRUFBRSxlQUFlLEVBQUUsY0FBYyxFQUFFLEVBQUUsRUFBRSxNQUFNLGtCQUFrQixDQUFDO0FBQ3ZHLE9BQU8sRUFBRSxLQUFL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
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"checkpoint_builder.d.ts","sourceRoot":"","sources":["../src/checkpoint_builder.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"checkpoint_builder.d.ts","sourceRoot":"","sources":["../src/checkpoint_builder.ts"],"names":[],"mappings":"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"}
|