@aztec/validator-client 0.0.1-commit.1bea0213 → 0.0.1-commit.217f559981
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 +21 -18
- package/dest/block_proposal_handler.d.ts +4 -5
- package/dest/block_proposal_handler.d.ts.map +1 -1
- package/dest/block_proposal_handler.js +23 -36
- package/dest/checkpoint_builder.d.ts +10 -11
- package/dest/checkpoint_builder.d.ts.map +1 -1
- package/dest/checkpoint_builder.js +42 -27
- package/dest/config.d.ts +1 -1
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +7 -4
- 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 +3 -3
- 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 +2 -2
- package/dest/metrics.d.ts +4 -3
- package/dest/metrics.d.ts.map +1 -1
- package/dest/metrics.js +34 -5
- package/dest/tx_validator/tx_validator_factory.d.ts +4 -3
- package/dest/tx_validator/tx_validator_factory.d.ts.map +1 -1
- package/dest/tx_validator/tx_validator_factory.js +16 -16
- package/dest/validator.d.ts +38 -14
- package/dest/validator.d.ts.map +1 -1
- package/dest/validator.js +182 -51
- package/package.json +19 -17
- package/src/block_proposal_handler.ts +34 -53
- package/src/checkpoint_builder.ts +62 -25
- package/src/config.ts +7 -4
- package/src/duties/validation_service.ts +9 -2
- package/src/key_store/ha_key_store.ts +2 -2
- package/src/metrics.ts +45 -6
- package/src/tx_validator/tx_validator_factory.ts +51 -32
- package/src/validator.ts +236 -63
|
@@ -1,25 +1,19 @@
|
|
|
1
1
|
import { INITIAL_L2_BLOCK_NUM } from '@aztec/constants';
|
|
2
2
|
import type { EpochCache } from '@aztec/epoch-cache';
|
|
3
3
|
import { BlockNumber, CheckpointNumber, SlotNumber } from '@aztec/foundation/branded-types';
|
|
4
|
-
import { chunkBy } from '@aztec/foundation/collection';
|
|
5
4
|
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
6
5
|
import { TimeoutError } from '@aztec/foundation/error';
|
|
7
6
|
import { createLogger } from '@aztec/foundation/log';
|
|
8
7
|
import { retryUntil } from '@aztec/foundation/retry';
|
|
9
8
|
import { DateProvider, Timer } from '@aztec/foundation/timer';
|
|
10
9
|
import type { P2P, PeerId } from '@aztec/p2p';
|
|
11
|
-
import { TxProvider } from '@aztec/p2p';
|
|
12
10
|
import { BlockProposalValidator } from '@aztec/p2p/msg_validators';
|
|
13
|
-
import type { L2Block, L2BlockSink, L2BlockSource } from '@aztec/stdlib/block';
|
|
11
|
+
import type { BlockData, L2Block, L2BlockSink, L2BlockSource } from '@aztec/stdlib/block';
|
|
14
12
|
import { getEpochAtSlot, getTimestampForSlot } from '@aztec/stdlib/epoch-helpers';
|
|
15
|
-
import type { ValidatorClientFullConfig, WorldStateSynchronizer } from '@aztec/stdlib/interfaces/server';
|
|
16
|
-
import {
|
|
17
|
-
type L1ToL2MessageSource,
|
|
18
|
-
computeCheckpointOutHash,
|
|
19
|
-
computeInHashFromL1ToL2Messages,
|
|
20
|
-
} from '@aztec/stdlib/messaging';
|
|
13
|
+
import type { ITxProvider, ValidatorClientFullConfig, WorldStateSynchronizer } from '@aztec/stdlib/interfaces/server';
|
|
14
|
+
import { type L1ToL2MessageSource, computeInHashFromL1ToL2Messages } from '@aztec/stdlib/messaging';
|
|
21
15
|
import type { BlockProposal } from '@aztec/stdlib/p2p';
|
|
22
|
-
import {
|
|
16
|
+
import type { CheckpointGlobalVariables, FailedTx, Tx } from '@aztec/stdlib/tx';
|
|
23
17
|
import {
|
|
24
18
|
ReExFailedTxsError,
|
|
25
19
|
ReExStateMismatchError,
|
|
@@ -78,7 +72,7 @@ export class BlockProposalHandler {
|
|
|
78
72
|
private worldState: WorldStateSynchronizer,
|
|
79
73
|
private blockSource: L2BlockSource & L2BlockSink,
|
|
80
74
|
private l1ToL2MessageSource: L1ToL2MessageSource,
|
|
81
|
-
private txProvider:
|
|
75
|
+
private txProvider: ITxProvider,
|
|
82
76
|
private blockProposalValidator: BlockProposalValidator,
|
|
83
77
|
private epochCache: EpochCache,
|
|
84
78
|
private config: ValidatorClientFullConfig,
|
|
@@ -154,16 +148,16 @@ export class BlockProposalHandler {
|
|
|
154
148
|
}
|
|
155
149
|
|
|
156
150
|
// Check that the parent proposal is a block we know, otherwise reexecution would fail
|
|
157
|
-
const
|
|
158
|
-
if (
|
|
151
|
+
const parentBlock = await this.getParentBlock(proposal);
|
|
152
|
+
if (parentBlock === undefined) {
|
|
159
153
|
this.log.warn(`Parent block for proposal not found, skipping processing`, proposalInfo);
|
|
160
154
|
return { isValid: false, reason: 'parent_block_not_found' };
|
|
161
155
|
}
|
|
162
156
|
|
|
163
|
-
// Check that the parent block's slot is
|
|
164
|
-
if (
|
|
165
|
-
this.log.warn(`Parent block slot is greater than
|
|
166
|
-
parentBlockSlot:
|
|
157
|
+
// Check that the parent block's slot is not greater than the proposal's slot.
|
|
158
|
+
if (parentBlock !== 'genesis' && parentBlock.header.getSlot() > slotNumber) {
|
|
159
|
+
this.log.warn(`Parent block slot is greater than proposal slot, skipping processing`, {
|
|
160
|
+
parentBlockSlot: parentBlock.header.getSlot().toString(),
|
|
167
161
|
proposalSlot: slotNumber.toString(),
|
|
168
162
|
...proposalInfo,
|
|
169
163
|
});
|
|
@@ -172,9 +166,9 @@ export class BlockProposalHandler {
|
|
|
172
166
|
|
|
173
167
|
// Compute the block number based on the parent block
|
|
174
168
|
const blockNumber =
|
|
175
|
-
|
|
169
|
+
parentBlock === 'genesis'
|
|
176
170
|
? BlockNumber(INITIAL_L2_BLOCK_NUM)
|
|
177
|
-
: BlockNumber(
|
|
171
|
+
: BlockNumber(parentBlock.header.getBlockNumber() + 1);
|
|
178
172
|
|
|
179
173
|
// Check that this block number does not exist already
|
|
180
174
|
const existingBlock = await this.blockSource.getBlockHeader(blockNumber);
|
|
@@ -191,7 +185,7 @@ export class BlockProposalHandler {
|
|
|
191
185
|
});
|
|
192
186
|
|
|
193
187
|
// Compute the checkpoint number for this block and validate checkpoint consistency
|
|
194
|
-
const checkpointResult =
|
|
188
|
+
const checkpointResult = this.computeCheckpointNumber(proposal, parentBlock, proposalInfo);
|
|
195
189
|
if (checkpointResult.reason) {
|
|
196
190
|
return { isValid: false, blockNumber, reason: checkpointResult.reason };
|
|
197
191
|
}
|
|
@@ -219,17 +213,11 @@ export class BlockProposalHandler {
|
|
|
219
213
|
// Try re-executing the transactions in the proposal if needed
|
|
220
214
|
let reexecutionResult;
|
|
221
215
|
if (shouldReexecute) {
|
|
222
|
-
//
|
|
223
|
-
// TODO(leila/mbps): There can be a more efficient way to get the previous checkpoint out
|
|
224
|
-
// hashes without having to fetch all the blocks.
|
|
216
|
+
// Collect the out hashes of all the checkpoints before this one in the same epoch
|
|
225
217
|
const epoch = getEpochAtSlot(slotNumber, this.epochCache.getL1Constants());
|
|
226
|
-
const
|
|
227
|
-
.filter(
|
|
228
|
-
.
|
|
229
|
-
const blocksByCheckpoint = chunkBy(checkpointedBlocks, b => b.checkpointNumber);
|
|
230
|
-
const previousCheckpointOutHashes = blocksByCheckpoint.map(checkpointBlocks =>
|
|
231
|
-
computeCheckpointOutHash(checkpointBlocks.map(b => b.block.body.txEffects.map(tx => tx.l2ToL1Msgs))),
|
|
232
|
-
);
|
|
218
|
+
const previousCheckpointOutHashes = (await this.blockSource.getCheckpointsDataForEpoch(epoch))
|
|
219
|
+
.filter(c => c.checkpointNumber < checkpointNumber)
|
|
220
|
+
.map(c => c.checkpointOutHash);
|
|
233
221
|
|
|
234
222
|
try {
|
|
235
223
|
this.log.verbose(`Re-executing transactions in the proposal`, proposalInfo);
|
|
@@ -261,7 +249,7 @@ export class BlockProposalHandler {
|
|
|
261
249
|
return { isValid: true, blockNumber, reexecutionResult };
|
|
262
250
|
}
|
|
263
251
|
|
|
264
|
-
private async getParentBlock(proposal: BlockProposal): Promise<'genesis' |
|
|
252
|
+
private async getParentBlock(proposal: BlockProposal): Promise<'genesis' | BlockData | undefined> {
|
|
265
253
|
const parentArchive = proposal.blockHeader.lastArchive.root;
|
|
266
254
|
const slot = proposal.slotNumber;
|
|
267
255
|
const config = this.checkpointsBuilder.getConfig();
|
|
@@ -277,12 +265,11 @@ export class BlockProposalHandler {
|
|
|
277
265
|
|
|
278
266
|
try {
|
|
279
267
|
return (
|
|
280
|
-
(await this.blockSource.
|
|
268
|
+
(await this.blockSource.getBlockDataByArchive(parentArchive)) ??
|
|
281
269
|
(timeoutDurationMs <= 0
|
|
282
270
|
? undefined
|
|
283
271
|
: await retryUntil(
|
|
284
|
-
() =>
|
|
285
|
-
this.blockSource.syncImmediate().then(() => this.blockSource.getBlockHeaderByArchive(parentArchive)),
|
|
272
|
+
() => this.blockSource.syncImmediate().then(() => this.blockSource.getBlockDataByArchive(parentArchive)),
|
|
286
273
|
'force archiver sync',
|
|
287
274
|
timeoutDurationMs / 1000,
|
|
288
275
|
0.5,
|
|
@@ -298,12 +285,12 @@ export class BlockProposalHandler {
|
|
|
298
285
|
}
|
|
299
286
|
}
|
|
300
287
|
|
|
301
|
-
private
|
|
288
|
+
private computeCheckpointNumber(
|
|
302
289
|
proposal: BlockProposal,
|
|
303
|
-
|
|
290
|
+
parentBlock: 'genesis' | BlockData,
|
|
304
291
|
proposalInfo: object,
|
|
305
|
-
):
|
|
306
|
-
if (
|
|
292
|
+
): CheckpointComputationResult {
|
|
293
|
+
if (parentBlock === 'genesis') {
|
|
307
294
|
// First block is in checkpoint 1
|
|
308
295
|
if (proposal.indexWithinCheckpoint !== 0) {
|
|
309
296
|
this.log.warn(`First block proposal has non-zero indexWithinCheckpoint`, proposalInfo);
|
|
@@ -312,19 +299,9 @@ export class BlockProposalHandler {
|
|
|
312
299
|
return { checkpointNumber: CheckpointNumber.INITIAL };
|
|
313
300
|
}
|
|
314
301
|
|
|
315
|
-
// Get the parent block to find its checkpoint number
|
|
316
|
-
// TODO(palla/mbps): The block header should include the checkpoint number to avoid this lookup,
|
|
317
|
-
// or at least the L2BlockSource should return a different struct that includes it.
|
|
318
|
-
const parentBlockNumber = parentBlockHeader.getBlockNumber();
|
|
319
|
-
const parentBlock = await this.blockSource.getL2Block(parentBlockNumber);
|
|
320
|
-
if (!parentBlock) {
|
|
321
|
-
this.log.warn(`Parent block ${parentBlockNumber} not found in archiver`, proposalInfo);
|
|
322
|
-
return { reason: 'invalid_proposal' };
|
|
323
|
-
}
|
|
324
|
-
|
|
325
302
|
if (proposal.indexWithinCheckpoint === 0) {
|
|
326
303
|
// If this is the first block in a new checkpoint, increment the checkpoint number
|
|
327
|
-
if (!(proposal.blockHeader.getSlot() >
|
|
304
|
+
if (!(proposal.blockHeader.getSlot() > parentBlock.header.getSlot())) {
|
|
328
305
|
this.log.warn(`Slot should be greater than parent block slot for first block in checkpoint`, proposalInfo);
|
|
329
306
|
return { reason: 'invalid_proposal' };
|
|
330
307
|
}
|
|
@@ -336,7 +313,7 @@ export class BlockProposalHandler {
|
|
|
336
313
|
this.log.warn(`Non-sequential indexWithinCheckpoint`, proposalInfo);
|
|
337
314
|
return { reason: 'invalid_proposal' };
|
|
338
315
|
}
|
|
339
|
-
if (proposal.blockHeader.getSlot() !==
|
|
316
|
+
if (proposal.blockHeader.getSlot() !== parentBlock.header.getSlot()) {
|
|
340
317
|
this.log.warn(`Slot should be equal to parent block slot for non-first block in checkpoint`, proposalInfo);
|
|
341
318
|
return { reason: 'invalid_proposal' };
|
|
342
319
|
}
|
|
@@ -357,7 +334,7 @@ export class BlockProposalHandler {
|
|
|
357
334
|
*/
|
|
358
335
|
private validateNonFirstBlockInCheckpoint(
|
|
359
336
|
proposal: BlockProposal,
|
|
360
|
-
parentBlock:
|
|
337
|
+
parentBlock: BlockData,
|
|
361
338
|
proposalInfo: object,
|
|
362
339
|
): CheckpointComputationResult | undefined {
|
|
363
340
|
const proposalGlobals = proposal.blockHeader.globalVariables;
|
|
@@ -475,13 +452,15 @@ export class BlockProposalHandler {
|
|
|
475
452
|
|
|
476
453
|
// Fork before the block to be built
|
|
477
454
|
const parentBlockNumber = BlockNumber(blockNumber - 1);
|
|
478
|
-
|
|
455
|
+
await this.worldState.syncImmediate(parentBlockNumber);
|
|
456
|
+
await using fork = await this.worldState.fork(parentBlockNumber);
|
|
479
457
|
|
|
480
|
-
// Build checkpoint constants from proposal (excludes blockNumber
|
|
458
|
+
// Build checkpoint constants from proposal (excludes blockNumber which is per-block)
|
|
481
459
|
const constants: CheckpointGlobalVariables = {
|
|
482
460
|
chainId: new Fr(config.l1ChainId),
|
|
483
461
|
version: new Fr(config.rollupVersion),
|
|
484
462
|
slotNumber: slot,
|
|
463
|
+
timestamp: blockHeader.globalVariables.timestamp,
|
|
485
464
|
coinbase: blockHeader.globalVariables.coinbase,
|
|
486
465
|
feeRecipient: blockHeader.globalVariables.feeRecipient,
|
|
487
466
|
gasFees: blockHeader.globalVariables.gasFees,
|
|
@@ -491,10 +470,12 @@ export class BlockProposalHandler {
|
|
|
491
470
|
const checkpointBuilder = await this.checkpointsBuilder.openCheckpoint(
|
|
492
471
|
checkpointNumber,
|
|
493
472
|
constants,
|
|
473
|
+
0n, // only takes effect in the following checkpoint.
|
|
494
474
|
l1ToL2Messages,
|
|
495
475
|
previousCheckpointOutHashes,
|
|
496
476
|
fork,
|
|
497
477
|
priorBlocks,
|
|
478
|
+
this.log.getBindings(),
|
|
498
479
|
);
|
|
499
480
|
|
|
500
481
|
// Build the new block
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { BlockNumber, CheckpointNumber } from '@aztec/foundation/branded-types';
|
|
2
2
|
import { merge, pick } from '@aztec/foundation/collection';
|
|
3
3
|
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
4
|
-
import { createLogger } from '@aztec/foundation/log';
|
|
4
|
+
import { type Logger, type LoggerBindings, createLogger } from '@aztec/foundation/log';
|
|
5
5
|
import { bufferToHex } from '@aztec/foundation/string';
|
|
6
|
-
import { DateProvider,
|
|
6
|
+
import { DateProvider, elapsed } from '@aztec/foundation/timer';
|
|
7
7
|
import { getDefaultAllowedSetupFunctions } from '@aztec/p2p/msg_validators';
|
|
8
8
|
import { LightweightCheckpointBuilder } from '@aztec/prover-client/light';
|
|
9
9
|
import {
|
|
@@ -24,6 +24,7 @@ import {
|
|
|
24
24
|
type ICheckpointBlockBuilder,
|
|
25
25
|
type ICheckpointsBuilder,
|
|
26
26
|
type MerkleTreeWriteOperations,
|
|
27
|
+
NoValidTxsError,
|
|
27
28
|
type PublicProcessorLimits,
|
|
28
29
|
type WorldStateSynchronizer,
|
|
29
30
|
} from '@aztec/stdlib/interfaces/server';
|
|
@@ -36,18 +37,13 @@ import { createValidatorForBlockBuilding } from './tx_validator/tx_validator_fac
|
|
|
36
37
|
// Re-export for backward compatibility
|
|
37
38
|
export type { BuildBlockInCheckpointResult } from '@aztec/stdlib/interfaces/server';
|
|
38
39
|
|
|
39
|
-
const log = createLogger('checkpoint-builder');
|
|
40
|
-
|
|
41
|
-
/** Result of building a block within a checkpoint. Extends the base interface with timer. */
|
|
42
|
-
export interface BuildBlockInCheckpointResultWithTimer extends BuildBlockInCheckpointResult {
|
|
43
|
-
blockBuildingTimer: Timer;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
40
|
/**
|
|
47
41
|
* Builder for a single checkpoint. Handles building blocks within the checkpoint
|
|
48
42
|
* and completing it.
|
|
49
43
|
*/
|
|
50
44
|
export class CheckpointBuilder implements ICheckpointBlockBuilder {
|
|
45
|
+
private log: Logger;
|
|
46
|
+
|
|
51
47
|
constructor(
|
|
52
48
|
private checkpointBuilder: LightweightCheckpointBuilder,
|
|
53
49
|
private fork: MerkleTreeWriteOperations,
|
|
@@ -55,7 +51,13 @@ export class CheckpointBuilder implements ICheckpointBlockBuilder {
|
|
|
55
51
|
private contractDataSource: ContractDataSource,
|
|
56
52
|
private dateProvider: DateProvider,
|
|
57
53
|
private telemetryClient: TelemetryClient,
|
|
58
|
-
|
|
54
|
+
bindings?: LoggerBindings,
|
|
55
|
+
) {
|
|
56
|
+
this.log = createLogger('checkpoint-builder', {
|
|
57
|
+
...bindings,
|
|
58
|
+
instanceId: `checkpoint-${checkpointBuilder.checkpointNumber}`,
|
|
59
|
+
});
|
|
60
|
+
}
|
|
59
61
|
|
|
60
62
|
getConstantData(): CheckpointGlobalVariables {
|
|
61
63
|
return this.checkpointBuilder.constants;
|
|
@@ -68,12 +70,11 @@ export class CheckpointBuilder implements ICheckpointBlockBuilder {
|
|
|
68
70
|
pendingTxs: Iterable<Tx> | AsyncIterable<Tx>,
|
|
69
71
|
blockNumber: BlockNumber,
|
|
70
72
|
timestamp: bigint,
|
|
71
|
-
opts: PublicProcessorLimits & { expectedEndState?: StateReference },
|
|
72
|
-
): Promise<
|
|
73
|
-
const blockBuildingTimer = new Timer();
|
|
73
|
+
opts: PublicProcessorLimits & { expectedEndState?: StateReference } = {},
|
|
74
|
+
): Promise<BuildBlockInCheckpointResult> {
|
|
74
75
|
const slot = this.checkpointBuilder.constants.slotNumber;
|
|
75
76
|
|
|
76
|
-
log.verbose(`Building block ${blockNumber} for slot ${slot} within checkpoint`, {
|
|
77
|
+
this.log.verbose(`Building block ${blockNumber} for slot ${slot} within checkpoint`, {
|
|
77
78
|
slot,
|
|
78
79
|
blockNumber,
|
|
79
80
|
...opts,
|
|
@@ -97,6 +98,12 @@ export class CheckpointBuilder implements ICheckpointBlockBuilder {
|
|
|
97
98
|
processor.process(pendingTxs, opts, validator),
|
|
98
99
|
);
|
|
99
100
|
|
|
101
|
+
// Throw if we didn't collect a single valid tx and we're not allowed to build empty blocks
|
|
102
|
+
// (only the first block in a checkpoint can be empty)
|
|
103
|
+
if (processedTxs.length === 0 && this.checkpointBuilder.getBlockCount() > 0) {
|
|
104
|
+
throw new NoValidTxsError(failedTxs);
|
|
105
|
+
}
|
|
106
|
+
|
|
100
107
|
// Add block to checkpoint
|
|
101
108
|
const block = await this.checkpointBuilder.addBlock(globalVariables, processedTxs, {
|
|
102
109
|
expectedEndState: opts.expectedEndState,
|
|
@@ -105,25 +112,28 @@ export class CheckpointBuilder implements ICheckpointBlockBuilder {
|
|
|
105
112
|
// How much public gas was processed
|
|
106
113
|
const publicGas = processedTxs.reduce((acc, tx) => acc.add(tx.gasUsed.publicGas), Gas.empty());
|
|
107
114
|
|
|
108
|
-
|
|
115
|
+
this.log.debug('Built block within checkpoint', {
|
|
116
|
+
header: block.header.toInspect(),
|
|
117
|
+
processedTxs: processedTxs.map(tx => tx.hash.toString()),
|
|
118
|
+
failedTxs: failedTxs.map(tx => tx.tx.txHash.toString()),
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
return {
|
|
109
122
|
block,
|
|
110
123
|
publicGas,
|
|
111
124
|
publicProcessorDuration,
|
|
112
125
|
numTxs: processedTxs.length,
|
|
113
126
|
failedTxs,
|
|
114
|
-
blockBuildingTimer,
|
|
115
127
|
usedTxs,
|
|
116
128
|
usedTxBlobFields,
|
|
117
129
|
};
|
|
118
|
-
log.debug('Built block within checkpoint', res.block.header);
|
|
119
|
-
return res;
|
|
120
130
|
}
|
|
121
131
|
|
|
122
132
|
/** Completes the checkpoint and returns it. */
|
|
123
133
|
async completeCheckpoint(): Promise<Checkpoint> {
|
|
124
134
|
const checkpoint = await this.checkpointBuilder.completeCheckpoint();
|
|
125
135
|
|
|
126
|
-
log.verbose(`Completed checkpoint ${checkpoint.number}`, {
|
|
136
|
+
this.log.verbose(`Completed checkpoint ${checkpoint.number}`, {
|
|
127
137
|
checkpointNumber: checkpoint.number,
|
|
128
138
|
numBlocks: checkpoint.blocks.length,
|
|
129
139
|
archiveRoot: checkpoint.archive.root.toString(),
|
|
@@ -139,14 +149,16 @@ export class CheckpointBuilder implements ICheckpointBlockBuilder {
|
|
|
139
149
|
|
|
140
150
|
protected async makeBlockBuilderDeps(globalVariables: GlobalVariables, fork: MerkleTreeWriteOperations) {
|
|
141
151
|
const txPublicSetupAllowList = this.config.txPublicSetupAllowList ?? (await getDefaultAllowedSetupFunctions());
|
|
142
|
-
const contractsDB = new PublicContractsDB(this.contractDataSource);
|
|
152
|
+
const contractsDB = new PublicContractsDB(this.contractDataSource, this.log.getBindings());
|
|
143
153
|
const guardedFork = new GuardedMerkleTreeOperations(fork);
|
|
144
154
|
|
|
155
|
+
const bindings = this.log.getBindings();
|
|
145
156
|
const publicTxSimulator = createPublicTxSimulatorForBlockBuilding(
|
|
146
157
|
guardedFork,
|
|
147
158
|
contractsDB,
|
|
148
159
|
globalVariables,
|
|
149
160
|
this.telemetryClient,
|
|
161
|
+
bindings,
|
|
150
162
|
);
|
|
151
163
|
|
|
152
164
|
const processor = new PublicProcessor(
|
|
@@ -156,7 +168,7 @@ export class CheckpointBuilder implements ICheckpointBlockBuilder {
|
|
|
156
168
|
publicTxSimulator,
|
|
157
169
|
this.dateProvider,
|
|
158
170
|
this.telemetryClient,
|
|
159
|
-
|
|
171
|
+
createLogger('simulator:public-processor', bindings),
|
|
160
172
|
this.config,
|
|
161
173
|
);
|
|
162
174
|
|
|
@@ -165,6 +177,7 @@ export class CheckpointBuilder implements ICheckpointBlockBuilder {
|
|
|
165
177
|
this.contractDataSource,
|
|
166
178
|
globalVariables,
|
|
167
179
|
txPublicSetupAllowList,
|
|
180
|
+
this.log.getBindings(),
|
|
168
181
|
);
|
|
169
182
|
|
|
170
183
|
return {
|
|
@@ -176,13 +189,17 @@ export class CheckpointBuilder implements ICheckpointBlockBuilder {
|
|
|
176
189
|
|
|
177
190
|
/** Factory for creating checkpoint builders. */
|
|
178
191
|
export class FullNodeCheckpointsBuilder implements ICheckpointsBuilder {
|
|
192
|
+
private log: Logger;
|
|
193
|
+
|
|
179
194
|
constructor(
|
|
180
195
|
private config: FullNodeBlockBuilderConfig & Pick<L1RollupConstants, 'l1GenesisTime' | 'slotDuration'>,
|
|
181
196
|
private worldState: WorldStateSynchronizer,
|
|
182
197
|
private contractDataSource: ContractDataSource,
|
|
183
198
|
private dateProvider: DateProvider,
|
|
184
199
|
private telemetryClient: TelemetryClient = getTelemetryClient(),
|
|
185
|
-
) {
|
|
200
|
+
) {
|
|
201
|
+
this.log = createLogger('checkpoint-builder');
|
|
202
|
+
}
|
|
186
203
|
|
|
187
204
|
public getConfig(): FullNodeBlockBuilderConfig {
|
|
188
205
|
return this.config;
|
|
@@ -198,19 +215,22 @@ export class FullNodeCheckpointsBuilder implements ICheckpointsBuilder {
|
|
|
198
215
|
async startCheckpoint(
|
|
199
216
|
checkpointNumber: CheckpointNumber,
|
|
200
217
|
constants: CheckpointGlobalVariables,
|
|
218
|
+
feeAssetPriceModifier: bigint,
|
|
201
219
|
l1ToL2Messages: Fr[],
|
|
202
220
|
previousCheckpointOutHashes: Fr[],
|
|
203
221
|
fork: MerkleTreeWriteOperations,
|
|
222
|
+
bindings?: LoggerBindings,
|
|
204
223
|
): Promise<CheckpointBuilder> {
|
|
205
224
|
const stateReference = await fork.getStateReference();
|
|
206
225
|
const archiveTree = await fork.getTreeInfo(MerkleTreeId.ARCHIVE);
|
|
207
226
|
|
|
208
|
-
log.verbose(`Building new checkpoint ${checkpointNumber}`, {
|
|
227
|
+
this.log.verbose(`Building new checkpoint ${checkpointNumber}`, {
|
|
209
228
|
checkpointNumber,
|
|
210
229
|
msgCount: l1ToL2Messages.length,
|
|
211
230
|
initialStateReference: stateReference.toInspect(),
|
|
212
231
|
initialArchiveRoot: bufferToHex(archiveTree.root),
|
|
213
232
|
constants,
|
|
233
|
+
feeAssetPriceModifier,
|
|
214
234
|
});
|
|
215
235
|
|
|
216
236
|
const lightweightBuilder = await LightweightCheckpointBuilder.startNewCheckpoint(
|
|
@@ -219,6 +239,8 @@ export class FullNodeCheckpointsBuilder implements ICheckpointsBuilder {
|
|
|
219
239
|
l1ToL2Messages,
|
|
220
240
|
previousCheckpointOutHashes,
|
|
221
241
|
fork,
|
|
242
|
+
bindings,
|
|
243
|
+
feeAssetPriceModifier,
|
|
222
244
|
);
|
|
223
245
|
|
|
224
246
|
return new CheckpointBuilder(
|
|
@@ -228,6 +250,7 @@ export class FullNodeCheckpointsBuilder implements ICheckpointsBuilder {
|
|
|
228
250
|
this.contractDataSource,
|
|
229
251
|
this.dateProvider,
|
|
230
252
|
this.telemetryClient,
|
|
253
|
+
bindings,
|
|
231
254
|
);
|
|
232
255
|
}
|
|
233
256
|
|
|
@@ -237,34 +260,47 @@ export class FullNodeCheckpointsBuilder implements ICheckpointsBuilder {
|
|
|
237
260
|
async openCheckpoint(
|
|
238
261
|
checkpointNumber: CheckpointNumber,
|
|
239
262
|
constants: CheckpointGlobalVariables,
|
|
263
|
+
feeAssetPriceModifier: bigint,
|
|
240
264
|
l1ToL2Messages: Fr[],
|
|
241
265
|
previousCheckpointOutHashes: Fr[],
|
|
242
266
|
fork: MerkleTreeWriteOperations,
|
|
243
267
|
existingBlocks: L2Block[] = [],
|
|
268
|
+
bindings?: LoggerBindings,
|
|
244
269
|
): Promise<CheckpointBuilder> {
|
|
245
270
|
const stateReference = await fork.getStateReference();
|
|
246
271
|
const archiveTree = await fork.getTreeInfo(MerkleTreeId.ARCHIVE);
|
|
247
272
|
|
|
248
273
|
if (existingBlocks.length === 0) {
|
|
249
|
-
return this.startCheckpoint(
|
|
274
|
+
return this.startCheckpoint(
|
|
275
|
+
checkpointNumber,
|
|
276
|
+
constants,
|
|
277
|
+
feeAssetPriceModifier,
|
|
278
|
+
l1ToL2Messages,
|
|
279
|
+
previousCheckpointOutHashes,
|
|
280
|
+
fork,
|
|
281
|
+
bindings,
|
|
282
|
+
);
|
|
250
283
|
}
|
|
251
284
|
|
|
252
|
-
log.verbose(`Resuming checkpoint ${checkpointNumber} with ${existingBlocks.length} existing blocks`, {
|
|
285
|
+
this.log.verbose(`Resuming checkpoint ${checkpointNumber} with ${existingBlocks.length} existing blocks`, {
|
|
253
286
|
checkpointNumber,
|
|
254
287
|
msgCount: l1ToL2Messages.length,
|
|
255
288
|
existingBlockCount: existingBlocks.length,
|
|
256
289
|
initialStateReference: stateReference.toInspect(),
|
|
257
290
|
initialArchiveRoot: bufferToHex(archiveTree.root),
|
|
258
291
|
constants,
|
|
292
|
+
feeAssetPriceModifier,
|
|
259
293
|
});
|
|
260
294
|
|
|
261
295
|
const lightweightBuilder = await LightweightCheckpointBuilder.resumeCheckpoint(
|
|
262
296
|
checkpointNumber,
|
|
263
297
|
constants,
|
|
298
|
+
feeAssetPriceModifier,
|
|
264
299
|
l1ToL2Messages,
|
|
265
300
|
previousCheckpointOutHashes,
|
|
266
301
|
fork,
|
|
267
302
|
existingBlocks,
|
|
303
|
+
bindings,
|
|
268
304
|
);
|
|
269
305
|
|
|
270
306
|
return new CheckpointBuilder(
|
|
@@ -274,6 +310,7 @@ export class FullNodeCheckpointsBuilder implements ICheckpointsBuilder {
|
|
|
274
310
|
this.contractDataSource,
|
|
275
311
|
this.dateProvider,
|
|
276
312
|
this.telemetryClient,
|
|
313
|
+
bindings,
|
|
277
314
|
);
|
|
278
315
|
}
|
|
279
316
|
|
package/src/config.ts
CHANGED
|
@@ -6,8 +6,8 @@ import {
|
|
|
6
6
|
secretValueConfigHelper,
|
|
7
7
|
} from '@aztec/foundation/config';
|
|
8
8
|
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
9
|
+
import { validatorHASignerConfigMappings } from '@aztec/stdlib/ha-signing';
|
|
9
10
|
import type { ValidatorClientConfig } from '@aztec/stdlib/interfaces/server';
|
|
10
|
-
import { validatorHASignerConfigMappings } from '@aztec/validator-ha-signer/config';
|
|
11
11
|
|
|
12
12
|
export type { ValidatorClientConfig };
|
|
13
13
|
|
|
@@ -65,15 +65,18 @@ export const validatorClientConfigMappings: ConfigMappingsType<ValidatorClientCo
|
|
|
65
65
|
'Whether to run in fisherman mode: validates all proposals and attestations but does not broadcast attestations or participate in consensus.',
|
|
66
66
|
...booleanConfigHelper(false),
|
|
67
67
|
},
|
|
68
|
-
// TODO(palla/mbps): Change default to false once checkpoint validation is stable
|
|
69
68
|
skipCheckpointProposalValidation: {
|
|
70
|
-
description: 'Skip checkpoint proposal validation and always attest (default:
|
|
71
|
-
defaultValue:
|
|
69
|
+
description: 'Skip checkpoint proposal validation and always attest (default: false)',
|
|
70
|
+
defaultValue: false,
|
|
72
71
|
},
|
|
73
72
|
skipPushProposedBlocksToArchiver: {
|
|
74
73
|
description: 'Skip pushing re-executed blocks to archiver (default: false)',
|
|
75
74
|
defaultValue: false,
|
|
76
75
|
},
|
|
76
|
+
attestToEquivocatedProposals: {
|
|
77
|
+
description: 'Agree to attest to equivocated checkpoint proposals (for testing purposes only)',
|
|
78
|
+
...booleanConfigHelper(false),
|
|
79
|
+
},
|
|
77
80
|
...validatorHASignerConfigMappings,
|
|
78
81
|
};
|
|
79
82
|
|
|
@@ -95,6 +95,7 @@ export class ValidationService {
|
|
|
95
95
|
public createCheckpointProposal(
|
|
96
96
|
checkpointHeader: CheckpointHeader,
|
|
97
97
|
archive: Fr,
|
|
98
|
+
feeAssetPriceModifier: bigint,
|
|
98
99
|
lastBlockInfo: CreateCheckpointProposalLastBlockData | undefined,
|
|
99
100
|
proposerAttesterAddress: EthAddress | undefined,
|
|
100
101
|
options: CheckpointProposalOptions,
|
|
@@ -119,7 +120,13 @@ export class ValidationService {
|
|
|
119
120
|
txs: options.publishFullTxs ? lastBlockInfo.txs : undefined,
|
|
120
121
|
};
|
|
121
122
|
|
|
122
|
-
return CheckpointProposal.createProposalFromSigner(
|
|
123
|
+
return CheckpointProposal.createProposalFromSigner(
|
|
124
|
+
checkpointHeader,
|
|
125
|
+
archive,
|
|
126
|
+
feeAssetPriceModifier,
|
|
127
|
+
lastBlock,
|
|
128
|
+
payloadSigner,
|
|
129
|
+
);
|
|
123
130
|
}
|
|
124
131
|
|
|
125
132
|
/**
|
|
@@ -137,7 +144,7 @@ export class ValidationService {
|
|
|
137
144
|
attestors: EthAddress[],
|
|
138
145
|
): Promise<CheckpointAttestation[]> {
|
|
139
146
|
// Create the attestation payload from the checkpoint proposal
|
|
140
|
-
const payload = new ConsensusPayload(proposal.checkpointHeader, proposal.archive);
|
|
147
|
+
const payload = new ConsensusPayload(proposal.checkpointHeader, proposal.archive, proposal.feeAssetPriceModifier);
|
|
141
148
|
const buf = Buffer32.fromBuffer(
|
|
142
149
|
keccak256(payload.getPayloadToSign(SignatureDomainSeparator.checkpointAttestation)),
|
|
143
150
|
);
|
|
@@ -256,8 +256,8 @@ export class HAKeyStore implements ExtendedValidatorKeyStore {
|
|
|
256
256
|
/**
|
|
257
257
|
* Start the high-availability key store
|
|
258
258
|
*/
|
|
259
|
-
public start()
|
|
260
|
-
|
|
259
|
+
public async start() {
|
|
260
|
+
await this.haSigner.start();
|
|
261
261
|
}
|
|
262
262
|
|
|
263
263
|
/**
|
package/src/metrics.ts
CHANGED
|
@@ -6,8 +6,11 @@ import {
|
|
|
6
6
|
Metrics,
|
|
7
7
|
type TelemetryClient,
|
|
8
8
|
type UpDownCounter,
|
|
9
|
+
createUpDownCounterWithDefault,
|
|
9
10
|
} from '@aztec/telemetry-client';
|
|
10
11
|
|
|
12
|
+
import type { BlockProposalValidationFailureReason } from './block_proposal_handler.js';
|
|
13
|
+
|
|
11
14
|
export class ValidatorMetrics {
|
|
12
15
|
private failedReexecutionCounter: UpDownCounter;
|
|
13
16
|
private successfulAttestationsCount: UpDownCounter;
|
|
@@ -21,16 +24,44 @@ export class ValidatorMetrics {
|
|
|
21
24
|
constructor(telemetryClient: TelemetryClient) {
|
|
22
25
|
const meter = telemetryClient.getMeter('Validator');
|
|
23
26
|
|
|
24
|
-
this.failedReexecutionCounter = meter
|
|
27
|
+
this.failedReexecutionCounter = createUpDownCounterWithDefault(meter, Metrics.VALIDATOR_FAILED_REEXECUTION_COUNT, {
|
|
28
|
+
[Attributes.STATUS]: ['failed'],
|
|
29
|
+
});
|
|
25
30
|
|
|
26
|
-
this.successfulAttestationsCount =
|
|
31
|
+
this.successfulAttestationsCount = createUpDownCounterWithDefault(
|
|
32
|
+
meter,
|
|
33
|
+
Metrics.VALIDATOR_ATTESTATION_SUCCESS_COUNT,
|
|
34
|
+
);
|
|
27
35
|
|
|
28
|
-
this.failedAttestationsBadProposalCount =
|
|
36
|
+
this.failedAttestationsBadProposalCount = createUpDownCounterWithDefault(
|
|
37
|
+
meter,
|
|
29
38
|
Metrics.VALIDATOR_ATTESTATION_FAILED_BAD_PROPOSAL_COUNT,
|
|
39
|
+
{
|
|
40
|
+
[Attributes.ERROR_TYPE]: [
|
|
41
|
+
'invalid_proposal',
|
|
42
|
+
'state_mismatch',
|
|
43
|
+
'failed_txs',
|
|
44
|
+
'in_hash_mismatch',
|
|
45
|
+
'parent_block_wrong_slot',
|
|
46
|
+
],
|
|
47
|
+
[Attributes.IS_COMMITTEE_MEMBER]: [true, false],
|
|
48
|
+
},
|
|
30
49
|
);
|
|
31
50
|
|
|
32
|
-
this.failedAttestationsNodeIssueCount =
|
|
51
|
+
this.failedAttestationsNodeIssueCount = createUpDownCounterWithDefault(
|
|
52
|
+
meter,
|
|
33
53
|
Metrics.VALIDATOR_ATTESTATION_FAILED_NODE_ISSUE_COUNT,
|
|
54
|
+
{
|
|
55
|
+
[Attributes.ERROR_TYPE]: [
|
|
56
|
+
'parent_block_not_found',
|
|
57
|
+
'global_variables_mismatch',
|
|
58
|
+
'block_number_already_exists',
|
|
59
|
+
'txs_not_available',
|
|
60
|
+
'timeout',
|
|
61
|
+
'unknown_error',
|
|
62
|
+
],
|
|
63
|
+
[Attributes.IS_COMMITTEE_MEMBER]: [true, false],
|
|
64
|
+
},
|
|
34
65
|
);
|
|
35
66
|
|
|
36
67
|
this.reexMana = meter.createHistogram(Metrics.VALIDATOR_RE_EXECUTION_MANA);
|
|
@@ -58,14 +89,22 @@ export class ValidatorMetrics {
|
|
|
58
89
|
this.successfulAttestationsCount.add(num);
|
|
59
90
|
}
|
|
60
91
|
|
|
61
|
-
public incFailedAttestationsBadProposal(
|
|
92
|
+
public incFailedAttestationsBadProposal(
|
|
93
|
+
num: number,
|
|
94
|
+
reason: BlockProposalValidationFailureReason,
|
|
95
|
+
inCommittee: boolean,
|
|
96
|
+
) {
|
|
62
97
|
this.failedAttestationsBadProposalCount.add(num, {
|
|
63
98
|
[Attributes.ERROR_TYPE]: reason,
|
|
64
99
|
[Attributes.IS_COMMITTEE_MEMBER]: inCommittee,
|
|
65
100
|
});
|
|
66
101
|
}
|
|
67
102
|
|
|
68
|
-
public incFailedAttestationsNodeIssue(
|
|
103
|
+
public incFailedAttestationsNodeIssue(
|
|
104
|
+
num: number,
|
|
105
|
+
reason: BlockProposalValidationFailureReason,
|
|
106
|
+
inCommittee: boolean,
|
|
107
|
+
) {
|
|
69
108
|
this.failedAttestationsNodeIssueCount.add(num, {
|
|
70
109
|
[Attributes.ERROR_TYPE]: reason,
|
|
71
110
|
[Attributes.IS_COMMITTEE_MEMBER]: inCommittee,
|