@aztec/validator-client 0.0.1-commit.f295ac2 → 0.0.1-commit.f504929
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 +22 -19
- package/dest/block_proposal_handler.d.ts +6 -8
- package/dest/block_proposal_handler.d.ts.map +1 -1
- package/dest/block_proposal_handler.js +28 -57
- package/dest/checkpoint_builder.d.ts +15 -13
- package/dest/checkpoint_builder.d.ts.map +1 -1
- package/dest/checkpoint_builder.js +55 -32
- package/dest/config.d.ts +1 -1
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +9 -7
- 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/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 +2 -2
- package/dest/metrics.d.ts +12 -3
- package/dest/metrics.d.ts.map +1 -1
- package/dest/metrics.js +46 -5
- package/dest/validator.d.ts +40 -14
- package/dest/validator.d.ts.map +1 -1
- package/dest/validator.js +196 -52
- package/package.json +19 -17
- package/src/block_proposal_handler.ts +40 -81
- package/src/checkpoint_builder.ts +80 -33
- package/src/config.ts +9 -7
- package/src/duties/validation_service.ts +9 -2
- package/src/factory.ts +1 -0
- package/src/index.ts +0 -1
- package/src/key_store/ha_key_store.ts +2 -2
- package/src/metrics.ts +63 -6
- package/src/validator.ts +253 -65
- 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 -18
- package/dest/tx_validator/tx_validator_factory.d.ts.map +0 -1
- package/dest/tx_validator/tx_validator_factory.js +0 -53
- 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 -133
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aztec/validator-client",
|
|
3
|
-
"version": "0.0.1-commit.
|
|
3
|
+
"version": "0.0.1-commit.f504929",
|
|
4
4
|
"main": "dest/index.js",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -64,28 +64,30 @@
|
|
|
64
64
|
]
|
|
65
65
|
},
|
|
66
66
|
"dependencies": {
|
|
67
|
-
"@aztec/blob-client": "0.0.1-commit.
|
|
68
|
-
"@aztec/blob-lib": "0.0.1-commit.
|
|
69
|
-
"@aztec/constants": "0.0.1-commit.
|
|
70
|
-
"@aztec/epoch-cache": "0.0.1-commit.
|
|
71
|
-
"@aztec/ethereum": "0.0.1-commit.
|
|
72
|
-
"@aztec/foundation": "0.0.1-commit.
|
|
73
|
-
"@aztec/node-keystore": "0.0.1-commit.
|
|
74
|
-
"@aztec/noir-protocol-circuits-types": "0.0.1-commit.
|
|
75
|
-
"@aztec/p2p": "0.0.1-commit.
|
|
76
|
-
"@aztec/protocol-contracts": "0.0.1-commit.
|
|
77
|
-
"@aztec/prover-client": "0.0.1-commit.
|
|
78
|
-
"@aztec/simulator": "0.0.1-commit.
|
|
79
|
-
"@aztec/slasher": "0.0.1-commit.
|
|
80
|
-
"@aztec/stdlib": "0.0.1-commit.
|
|
81
|
-
"@aztec/telemetry-client": "0.0.1-commit.
|
|
82
|
-
"@aztec/validator-ha-signer": "0.0.1-commit.
|
|
67
|
+
"@aztec/blob-client": "0.0.1-commit.f504929",
|
|
68
|
+
"@aztec/blob-lib": "0.0.1-commit.f504929",
|
|
69
|
+
"@aztec/constants": "0.0.1-commit.f504929",
|
|
70
|
+
"@aztec/epoch-cache": "0.0.1-commit.f504929",
|
|
71
|
+
"@aztec/ethereum": "0.0.1-commit.f504929",
|
|
72
|
+
"@aztec/foundation": "0.0.1-commit.f504929",
|
|
73
|
+
"@aztec/node-keystore": "0.0.1-commit.f504929",
|
|
74
|
+
"@aztec/noir-protocol-circuits-types": "0.0.1-commit.f504929",
|
|
75
|
+
"@aztec/p2p": "0.0.1-commit.f504929",
|
|
76
|
+
"@aztec/protocol-contracts": "0.0.1-commit.f504929",
|
|
77
|
+
"@aztec/prover-client": "0.0.1-commit.f504929",
|
|
78
|
+
"@aztec/simulator": "0.0.1-commit.f504929",
|
|
79
|
+
"@aztec/slasher": "0.0.1-commit.f504929",
|
|
80
|
+
"@aztec/stdlib": "0.0.1-commit.f504929",
|
|
81
|
+
"@aztec/telemetry-client": "0.0.1-commit.f504929",
|
|
82
|
+
"@aztec/validator-ha-signer": "0.0.1-commit.f504929",
|
|
83
83
|
"koa": "^2.16.1",
|
|
84
84
|
"koa-router": "^13.1.1",
|
|
85
85
|
"tslib": "^2.4.0",
|
|
86
86
|
"viem": "npm:@aztec/viem@2.38.2"
|
|
87
87
|
},
|
|
88
88
|
"devDependencies": {
|
|
89
|
+
"@aztec/archiver": "0.0.1-commit.f504929",
|
|
90
|
+
"@aztec/world-state": "0.0.1-commit.f504929",
|
|
89
91
|
"@electric-sql/pglite": "^0.3.14",
|
|
90
92
|
"@jest/globals": "^30.0.0",
|
|
91
93
|
"@types/jest": "^30.0.0",
|
|
@@ -7,18 +7,13 @@ import { createLogger } from '@aztec/foundation/log';
|
|
|
7
7
|
import { retryUntil } from '@aztec/foundation/retry';
|
|
8
8
|
import { DateProvider, Timer } from '@aztec/foundation/timer';
|
|
9
9
|
import type { P2P, PeerId } from '@aztec/p2p';
|
|
10
|
-
import { TxProvider } from '@aztec/p2p';
|
|
11
10
|
import { BlockProposalValidator } from '@aztec/p2p/msg_validators';
|
|
12
|
-
import type {
|
|
11
|
+
import type { BlockData, L2Block, L2BlockSink, L2BlockSource } from '@aztec/stdlib/block';
|
|
13
12
|
import { getEpochAtSlot, getTimestampForSlot } from '@aztec/stdlib/epoch-helpers';
|
|
14
|
-
import type { ValidatorClientFullConfig, WorldStateSynchronizer } from '@aztec/stdlib/interfaces/server';
|
|
15
|
-
import {
|
|
16
|
-
type L1ToL2MessageSource,
|
|
17
|
-
computeCheckpointOutHash,
|
|
18
|
-
computeInHashFromL1ToL2Messages,
|
|
19
|
-
} from '@aztec/stdlib/messaging';
|
|
13
|
+
import type { ITxProvider, ValidatorClientFullConfig, WorldStateSynchronizer } from '@aztec/stdlib/interfaces/server';
|
|
14
|
+
import { type L1ToL2MessageSource, computeInHashFromL1ToL2Messages } from '@aztec/stdlib/messaging';
|
|
20
15
|
import type { BlockProposal } from '@aztec/stdlib/p2p';
|
|
21
|
-
import {
|
|
16
|
+
import type { CheckpointGlobalVariables, FailedTx, Tx } from '@aztec/stdlib/tx';
|
|
22
17
|
import {
|
|
23
18
|
ReExFailedTxsError,
|
|
24
19
|
ReExStateMismatchError,
|
|
@@ -44,7 +39,7 @@ export type BlockProposalValidationFailureReason =
|
|
|
44
39
|
| 'unknown_error';
|
|
45
40
|
|
|
46
41
|
type ReexecuteTransactionsResult = {
|
|
47
|
-
block:
|
|
42
|
+
block: L2Block;
|
|
48
43
|
failedTxs: FailedTx[];
|
|
49
44
|
reexecutionTimeMs: number;
|
|
50
45
|
totalManaUsed: number;
|
|
@@ -77,7 +72,7 @@ export class BlockProposalHandler {
|
|
|
77
72
|
private worldState: WorldStateSynchronizer,
|
|
78
73
|
private blockSource: L2BlockSource & L2BlockSink,
|
|
79
74
|
private l1ToL2MessageSource: L1ToL2MessageSource,
|
|
80
|
-
private txProvider:
|
|
75
|
+
private txProvider: ITxProvider,
|
|
81
76
|
private blockProposalValidator: BlockProposalValidator,
|
|
82
77
|
private epochCache: EpochCache,
|
|
83
78
|
private config: ValidatorClientFullConfig,
|
|
@@ -146,23 +141,23 @@ export class BlockProposalHandler {
|
|
|
146
141
|
|
|
147
142
|
// Check that the proposal is from the current proposer, or the next proposer
|
|
148
143
|
// This should have been handled by the p2p layer, but we double check here out of caution
|
|
149
|
-
const
|
|
150
|
-
if (
|
|
144
|
+
const validationResult = await this.blockProposalValidator.validate(proposal);
|
|
145
|
+
if (validationResult.result !== 'accept') {
|
|
151
146
|
this.log.warn(`Proposal is not valid, skipping processing`, proposalInfo);
|
|
152
147
|
return { isValid: false, reason: 'invalid_proposal' };
|
|
153
148
|
}
|
|
154
149
|
|
|
155
150
|
// Check that the parent proposal is a block we know, otherwise reexecution would fail
|
|
156
|
-
const
|
|
157
|
-
if (
|
|
151
|
+
const parentBlock = await this.getParentBlock(proposal);
|
|
152
|
+
if (parentBlock === undefined) {
|
|
158
153
|
this.log.warn(`Parent block for proposal not found, skipping processing`, proposalInfo);
|
|
159
154
|
return { isValid: false, reason: 'parent_block_not_found' };
|
|
160
155
|
}
|
|
161
156
|
|
|
162
|
-
// Check that the parent block's slot is
|
|
163
|
-
if (
|
|
164
|
-
this.log.warn(`Parent block slot is greater than
|
|
165
|
-
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(),
|
|
166
161
|
proposalSlot: slotNumber.toString(),
|
|
167
162
|
...proposalInfo,
|
|
168
163
|
});
|
|
@@ -171,9 +166,9 @@ export class BlockProposalHandler {
|
|
|
171
166
|
|
|
172
167
|
// Compute the block number based on the parent block
|
|
173
168
|
const blockNumber =
|
|
174
|
-
|
|
169
|
+
parentBlock === 'genesis'
|
|
175
170
|
? BlockNumber(INITIAL_L2_BLOCK_NUM)
|
|
176
|
-
: BlockNumber(
|
|
171
|
+
: BlockNumber(parentBlock.header.getBlockNumber() + 1);
|
|
177
172
|
|
|
178
173
|
// Check that this block number does not exist already
|
|
179
174
|
const existingBlock = await this.blockSource.getBlockHeader(blockNumber);
|
|
@@ -190,7 +185,7 @@ export class BlockProposalHandler {
|
|
|
190
185
|
});
|
|
191
186
|
|
|
192
187
|
// Compute the checkpoint number for this block and validate checkpoint consistency
|
|
193
|
-
const checkpointResult =
|
|
188
|
+
const checkpointResult = this.computeCheckpointNumber(proposal, parentBlock, proposalInfo);
|
|
194
189
|
if (checkpointResult.reason) {
|
|
195
190
|
return { isValid: false, blockNumber, reason: checkpointResult.reason };
|
|
196
191
|
}
|
|
@@ -218,17 +213,11 @@ export class BlockProposalHandler {
|
|
|
218
213
|
// Try re-executing the transactions in the proposal if needed
|
|
219
214
|
let reexecutionResult;
|
|
220
215
|
if (shouldReexecute) {
|
|
221
|
-
//
|
|
222
|
-
// TODO(mbps): This assumes one block per checkpoint, which is only true for now.
|
|
223
|
-
// TODO: There can be a more efficient way to get the previous checkpoint out hashes without having to fetch all
|
|
224
|
-
// 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 previousCheckpointOutHashes = previousBlocks.map(b =>
|
|
230
|
-
computeCheckpointOutHash([b.body.txEffects.map(tx => tx.l2ToL1Msgs)]),
|
|
231
|
-
);
|
|
218
|
+
const previousCheckpointOutHashes = (await this.blockSource.getCheckpointsDataForEpoch(epoch))
|
|
219
|
+
.filter(c => c.checkpointNumber < checkpointNumber)
|
|
220
|
+
.map(c => c.checkpointOutHash);
|
|
232
221
|
|
|
233
222
|
try {
|
|
234
223
|
this.log.verbose(`Re-executing transactions in the proposal`, proposalInfo);
|
|
@@ -248,7 +237,6 @@ export class BlockProposalHandler {
|
|
|
248
237
|
}
|
|
249
238
|
|
|
250
239
|
// If we succeeded, push this block into the archiver (unless disabled)
|
|
251
|
-
// TODO(palla/mbps): Change default to false once block sync is stable.
|
|
252
240
|
if (reexecutionResult?.block && this.config.skipPushProposedBlocksToArchiver === false) {
|
|
253
241
|
await this.blockSource.addBlock(reexecutionResult?.block);
|
|
254
242
|
}
|
|
@@ -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.getL2BlockNew(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;
|
|
@@ -436,29 +413,6 @@ export class BlockProposalHandler {
|
|
|
436
413
|
return new Date(nextSlotTimestampSeconds * 1000);
|
|
437
414
|
}
|
|
438
415
|
|
|
439
|
-
/**
|
|
440
|
-
* Gets all prior blocks in the same checkpoint (same slot and checkpoint number) up to but not including upToBlockNumber.
|
|
441
|
-
*/
|
|
442
|
-
private async getBlocksInCheckpoint(
|
|
443
|
-
slot: SlotNumber,
|
|
444
|
-
upToBlockNumber: BlockNumber,
|
|
445
|
-
checkpointNumber: CheckpointNumber,
|
|
446
|
-
): Promise<L2BlockNew[]> {
|
|
447
|
-
const blocks: L2BlockNew[] = [];
|
|
448
|
-
let currentBlockNumber = BlockNumber(upToBlockNumber - 1);
|
|
449
|
-
|
|
450
|
-
while (currentBlockNumber >= INITIAL_L2_BLOCK_NUM) {
|
|
451
|
-
const block = await this.blockSource.getL2BlockNew(currentBlockNumber);
|
|
452
|
-
if (!block || block.header.getSlot() !== slot || block.checkpointNumber !== checkpointNumber) {
|
|
453
|
-
break;
|
|
454
|
-
}
|
|
455
|
-
blocks.unshift(block);
|
|
456
|
-
currentBlockNumber = BlockNumber(currentBlockNumber - 1);
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
return blocks;
|
|
460
|
-
}
|
|
461
|
-
|
|
462
416
|
private getReexecuteFailureReason(err: any) {
|
|
463
417
|
if (err instanceof ReExStateMismatchError) {
|
|
464
418
|
return 'state_mismatch';
|
|
@@ -492,18 +446,21 @@ export class BlockProposalHandler {
|
|
|
492
446
|
const slot = proposal.slotNumber;
|
|
493
447
|
const config = this.checkpointsBuilder.getConfig();
|
|
494
448
|
|
|
495
|
-
// Get prior blocks in this checkpoint (same slot
|
|
496
|
-
const
|
|
449
|
+
// Get prior blocks in this checkpoint (same slot before current block)
|
|
450
|
+
const allBlocksInSlot = await this.blockSource.getBlocksForSlot(slot);
|
|
451
|
+
const priorBlocks = allBlocksInSlot.filter(b => b.number < blockNumber && b.header.getSlot() === slot);
|
|
497
452
|
|
|
498
453
|
// Fork before the block to be built
|
|
499
454
|
const parentBlockNumber = BlockNumber(blockNumber - 1);
|
|
500
|
-
|
|
455
|
+
await this.worldState.syncImmediate(parentBlockNumber);
|
|
456
|
+
await using fork = await this.worldState.fork(parentBlockNumber);
|
|
501
457
|
|
|
502
|
-
// Build checkpoint constants from proposal (excludes blockNumber
|
|
458
|
+
// Build checkpoint constants from proposal (excludes blockNumber which is per-block)
|
|
503
459
|
const constants: CheckpointGlobalVariables = {
|
|
504
460
|
chainId: new Fr(config.l1ChainId),
|
|
505
461
|
version: new Fr(config.rollupVersion),
|
|
506
462
|
slotNumber: slot,
|
|
463
|
+
timestamp: blockHeader.globalVariables.timestamp,
|
|
507
464
|
coinbase: blockHeader.globalVariables.coinbase,
|
|
508
465
|
feeRecipient: blockHeader.globalVariables.feeRecipient,
|
|
509
466
|
gasFees: blockHeader.globalVariables.gasFees,
|
|
@@ -513,10 +470,12 @@ export class BlockProposalHandler {
|
|
|
513
470
|
const checkpointBuilder = await this.checkpointsBuilder.openCheckpoint(
|
|
514
471
|
checkpointNumber,
|
|
515
472
|
constants,
|
|
473
|
+
0n, // only takes effect in the following checkpoint.
|
|
516
474
|
l1ToL2Messages,
|
|
517
475
|
previousCheckpointOutHashes,
|
|
518
476
|
fork,
|
|
519
477
|
priorBlocks,
|
|
478
|
+
this.log.getBindings(),
|
|
520
479
|
);
|
|
521
480
|
|
|
522
481
|
// Build the new block
|
|
@@ -1,10 +1,10 @@
|
|
|
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,
|
|
7
|
-
import { getDefaultAllowedSetupFunctions } from '@aztec/p2p/msg_validators';
|
|
6
|
+
import { DateProvider, elapsed } from '@aztec/foundation/timer';
|
|
7
|
+
import { createTxValidatorForBlockBuilding, getDefaultAllowedSetupFunctions } from '@aztec/p2p/msg_validators';
|
|
8
8
|
import { LightweightCheckpointBuilder } from '@aztec/prover-client/light';
|
|
9
9
|
import {
|
|
10
10
|
GuardedMerkleTreeOperations,
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
PublicProcessor,
|
|
13
13
|
createPublicTxSimulatorForBlockBuilding,
|
|
14
14
|
} from '@aztec/simulator/server';
|
|
15
|
-
import {
|
|
15
|
+
import { L2Block } from '@aztec/stdlib/block';
|
|
16
16
|
import { Checkpoint } from '@aztec/stdlib/checkpoint';
|
|
17
17
|
import type { ContractDataSource } from '@aztec/stdlib/contract';
|
|
18
18
|
import type { L1RollupConstants } from '@aztec/stdlib/epoch-helpers';
|
|
@@ -24,30 +24,25 @@ 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';
|
|
31
|
+
import { type DebugLogStore, NullDebugLogStore } from '@aztec/stdlib/logs';
|
|
30
32
|
import { MerkleTreeId } from '@aztec/stdlib/trees';
|
|
31
33
|
import { type CheckpointGlobalVariables, GlobalVariables, StateReference, Tx } from '@aztec/stdlib/tx';
|
|
32
34
|
import { type TelemetryClient, getTelemetryClient } from '@aztec/telemetry-client';
|
|
33
35
|
|
|
34
|
-
import { createValidatorForBlockBuilding } from './tx_validator/tx_validator_factory.js';
|
|
35
|
-
|
|
36
36
|
// Re-export for backward compatibility
|
|
37
37
|
export type { BuildBlockInCheckpointResult } from '@aztec/stdlib/interfaces/server';
|
|
38
38
|
|
|
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
39
|
/**
|
|
47
40
|
* Builder for a single checkpoint. Handles building blocks within the checkpoint
|
|
48
41
|
* and completing it.
|
|
49
42
|
*/
|
|
50
43
|
export class CheckpointBuilder implements ICheckpointBlockBuilder {
|
|
44
|
+
private log: Logger;
|
|
45
|
+
|
|
51
46
|
constructor(
|
|
52
47
|
private checkpointBuilder: LightweightCheckpointBuilder,
|
|
53
48
|
private fork: MerkleTreeWriteOperations,
|
|
@@ -55,7 +50,14 @@ export class CheckpointBuilder implements ICheckpointBlockBuilder {
|
|
|
55
50
|
private contractDataSource: ContractDataSource,
|
|
56
51
|
private dateProvider: DateProvider,
|
|
57
52
|
private telemetryClient: TelemetryClient,
|
|
58
|
-
|
|
53
|
+
bindings?: LoggerBindings,
|
|
54
|
+
private debugLogStore: DebugLogStore = new NullDebugLogStore(),
|
|
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,33 +98,42 @@ 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
|
-
const block = await this.checkpointBuilder.addBlock(globalVariables, processedTxs, {
|
|
108
|
+
const { block } = await this.checkpointBuilder.addBlock(globalVariables, processedTxs, {
|
|
102
109
|
expectedEndState: opts.expectedEndState,
|
|
103
110
|
});
|
|
104
111
|
|
|
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(),
|
|
@@ -138,15 +148,23 @@ export class CheckpointBuilder implements ICheckpointBlockBuilder {
|
|
|
138
148
|
}
|
|
139
149
|
|
|
140
150
|
protected async makeBlockBuilderDeps(globalVariables: GlobalVariables, fork: MerkleTreeWriteOperations) {
|
|
141
|
-
const txPublicSetupAllowList =
|
|
142
|
-
|
|
151
|
+
const txPublicSetupAllowList = [
|
|
152
|
+
...(await getDefaultAllowedSetupFunctions()),
|
|
153
|
+
...(this.config.txPublicSetupAllowListExtend ?? []),
|
|
154
|
+
];
|
|
155
|
+
const contractsDB = new PublicContractsDB(this.contractDataSource, this.log.getBindings());
|
|
143
156
|
const guardedFork = new GuardedMerkleTreeOperations(fork);
|
|
144
157
|
|
|
158
|
+
const collectDebugLogs = this.debugLogStore.isEnabled;
|
|
159
|
+
|
|
160
|
+
const bindings = this.log.getBindings();
|
|
145
161
|
const publicTxSimulator = createPublicTxSimulatorForBlockBuilding(
|
|
146
162
|
guardedFork,
|
|
147
163
|
contractsDB,
|
|
148
164
|
globalVariables,
|
|
149
165
|
this.telemetryClient,
|
|
166
|
+
bindings,
|
|
167
|
+
collectDebugLogs,
|
|
150
168
|
);
|
|
151
169
|
|
|
152
170
|
const processor = new PublicProcessor(
|
|
@@ -156,15 +174,17 @@ export class CheckpointBuilder implements ICheckpointBlockBuilder {
|
|
|
156
174
|
publicTxSimulator,
|
|
157
175
|
this.dateProvider,
|
|
158
176
|
this.telemetryClient,
|
|
159
|
-
|
|
177
|
+
createLogger('simulator:public-processor', bindings),
|
|
160
178
|
this.config,
|
|
179
|
+
this.debugLogStore,
|
|
161
180
|
);
|
|
162
181
|
|
|
163
|
-
const validator =
|
|
182
|
+
const validator = createTxValidatorForBlockBuilding(
|
|
164
183
|
fork,
|
|
165
184
|
this.contractDataSource,
|
|
166
185
|
globalVariables,
|
|
167
186
|
txPublicSetupAllowList,
|
|
187
|
+
this.log.getBindings(),
|
|
168
188
|
);
|
|
169
189
|
|
|
170
190
|
return {
|
|
@@ -176,13 +196,18 @@ export class CheckpointBuilder implements ICheckpointBlockBuilder {
|
|
|
176
196
|
|
|
177
197
|
/** Factory for creating checkpoint builders. */
|
|
178
198
|
export class FullNodeCheckpointsBuilder implements ICheckpointsBuilder {
|
|
199
|
+
private log: Logger;
|
|
200
|
+
|
|
179
201
|
constructor(
|
|
180
202
|
private config: FullNodeBlockBuilderConfig & Pick<L1RollupConstants, 'l1GenesisTime' | 'slotDuration'>,
|
|
181
203
|
private worldState: WorldStateSynchronizer,
|
|
182
204
|
private contractDataSource: ContractDataSource,
|
|
183
205
|
private dateProvider: DateProvider,
|
|
184
206
|
private telemetryClient: TelemetryClient = getTelemetryClient(),
|
|
185
|
-
|
|
207
|
+
private debugLogStore: DebugLogStore = new NullDebugLogStore(),
|
|
208
|
+
) {
|
|
209
|
+
this.log = createLogger('checkpoint-builder');
|
|
210
|
+
}
|
|
186
211
|
|
|
187
212
|
public getConfig(): FullNodeBlockBuilderConfig {
|
|
188
213
|
return this.config;
|
|
@@ -198,19 +223,22 @@ export class FullNodeCheckpointsBuilder implements ICheckpointsBuilder {
|
|
|
198
223
|
async startCheckpoint(
|
|
199
224
|
checkpointNumber: CheckpointNumber,
|
|
200
225
|
constants: CheckpointGlobalVariables,
|
|
226
|
+
feeAssetPriceModifier: bigint,
|
|
201
227
|
l1ToL2Messages: Fr[],
|
|
202
228
|
previousCheckpointOutHashes: Fr[],
|
|
203
229
|
fork: MerkleTreeWriteOperations,
|
|
230
|
+
bindings?: LoggerBindings,
|
|
204
231
|
): Promise<CheckpointBuilder> {
|
|
205
232
|
const stateReference = await fork.getStateReference();
|
|
206
233
|
const archiveTree = await fork.getTreeInfo(MerkleTreeId.ARCHIVE);
|
|
207
234
|
|
|
208
|
-
log.verbose(`Building new checkpoint ${checkpointNumber}`, {
|
|
235
|
+
this.log.verbose(`Building new checkpoint ${checkpointNumber}`, {
|
|
209
236
|
checkpointNumber,
|
|
210
237
|
msgCount: l1ToL2Messages.length,
|
|
211
238
|
initialStateReference: stateReference.toInspect(),
|
|
212
239
|
initialArchiveRoot: bufferToHex(archiveTree.root),
|
|
213
240
|
constants,
|
|
241
|
+
feeAssetPriceModifier,
|
|
214
242
|
});
|
|
215
243
|
|
|
216
244
|
const lightweightBuilder = await LightweightCheckpointBuilder.startNewCheckpoint(
|
|
@@ -219,6 +247,8 @@ export class FullNodeCheckpointsBuilder implements ICheckpointsBuilder {
|
|
|
219
247
|
l1ToL2Messages,
|
|
220
248
|
previousCheckpointOutHashes,
|
|
221
249
|
fork,
|
|
250
|
+
bindings,
|
|
251
|
+
feeAssetPriceModifier,
|
|
222
252
|
);
|
|
223
253
|
|
|
224
254
|
return new CheckpointBuilder(
|
|
@@ -228,6 +258,8 @@ export class FullNodeCheckpointsBuilder implements ICheckpointsBuilder {
|
|
|
228
258
|
this.contractDataSource,
|
|
229
259
|
this.dateProvider,
|
|
230
260
|
this.telemetryClient,
|
|
261
|
+
bindings,
|
|
262
|
+
this.debugLogStore,
|
|
231
263
|
);
|
|
232
264
|
}
|
|
233
265
|
|
|
@@ -237,34 +269,47 @@ export class FullNodeCheckpointsBuilder implements ICheckpointsBuilder {
|
|
|
237
269
|
async openCheckpoint(
|
|
238
270
|
checkpointNumber: CheckpointNumber,
|
|
239
271
|
constants: CheckpointGlobalVariables,
|
|
272
|
+
feeAssetPriceModifier: bigint,
|
|
240
273
|
l1ToL2Messages: Fr[],
|
|
241
274
|
previousCheckpointOutHashes: Fr[],
|
|
242
275
|
fork: MerkleTreeWriteOperations,
|
|
243
|
-
existingBlocks:
|
|
276
|
+
existingBlocks: L2Block[] = [],
|
|
277
|
+
bindings?: LoggerBindings,
|
|
244
278
|
): Promise<CheckpointBuilder> {
|
|
245
279
|
const stateReference = await fork.getStateReference();
|
|
246
280
|
const archiveTree = await fork.getTreeInfo(MerkleTreeId.ARCHIVE);
|
|
247
281
|
|
|
248
282
|
if (existingBlocks.length === 0) {
|
|
249
|
-
return this.startCheckpoint(
|
|
283
|
+
return this.startCheckpoint(
|
|
284
|
+
checkpointNumber,
|
|
285
|
+
constants,
|
|
286
|
+
feeAssetPriceModifier,
|
|
287
|
+
l1ToL2Messages,
|
|
288
|
+
previousCheckpointOutHashes,
|
|
289
|
+
fork,
|
|
290
|
+
bindings,
|
|
291
|
+
);
|
|
250
292
|
}
|
|
251
293
|
|
|
252
|
-
log.verbose(`Resuming checkpoint ${checkpointNumber} with ${existingBlocks.length} existing blocks`, {
|
|
294
|
+
this.log.verbose(`Resuming checkpoint ${checkpointNumber} with ${existingBlocks.length} existing blocks`, {
|
|
253
295
|
checkpointNumber,
|
|
254
296
|
msgCount: l1ToL2Messages.length,
|
|
255
297
|
existingBlockCount: existingBlocks.length,
|
|
256
298
|
initialStateReference: stateReference.toInspect(),
|
|
257
299
|
initialArchiveRoot: bufferToHex(archiveTree.root),
|
|
258
300
|
constants,
|
|
301
|
+
feeAssetPriceModifier,
|
|
259
302
|
});
|
|
260
303
|
|
|
261
304
|
const lightweightBuilder = await LightweightCheckpointBuilder.resumeCheckpoint(
|
|
262
305
|
checkpointNumber,
|
|
263
306
|
constants,
|
|
307
|
+
feeAssetPriceModifier,
|
|
264
308
|
l1ToL2Messages,
|
|
265
309
|
previousCheckpointOutHashes,
|
|
266
310
|
fork,
|
|
267
311
|
existingBlocks,
|
|
312
|
+
bindings,
|
|
268
313
|
);
|
|
269
314
|
|
|
270
315
|
return new CheckpointBuilder(
|
|
@@ -274,6 +319,8 @@ export class FullNodeCheckpointsBuilder implements ICheckpointsBuilder {
|
|
|
274
319
|
this.contractDataSource,
|
|
275
320
|
this.dateProvider,
|
|
276
321
|
this.telemetryClient,
|
|
322
|
+
bindings,
|
|
323
|
+
this.debugLogStore,
|
|
277
324
|
);
|
|
278
325
|
}
|
|
279
326
|
|
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,17 @@ 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
|
-
// TODO(palla/mbps): Change default to false once block sync is stable
|
|
74
72
|
skipPushProposedBlocksToArchiver: {
|
|
75
|
-
description: 'Skip pushing re-executed blocks to archiver (default:
|
|
76
|
-
defaultValue:
|
|
73
|
+
description: 'Skip pushing re-executed blocks to archiver (default: false)',
|
|
74
|
+
defaultValue: false,
|
|
75
|
+
},
|
|
76
|
+
attestToEquivocatedProposals: {
|
|
77
|
+
description: 'Agree to attest to equivocated checkpoint proposals (for testing purposes only)',
|
|
78
|
+
...booleanConfigHelper(false),
|
|
77
79
|
},
|
|
78
80
|
...validatorHASignerConfigMappings,
|
|
79
81
|
};
|