@aztec/validator-client 0.0.1-commit.e2b2873ed → 0.0.1-commit.e304674f1
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 +41 -2
- package/dest/checkpoint_builder.d.ts +21 -8
- package/dest/checkpoint_builder.d.ts.map +1 -1
- package/dest/checkpoint_builder.js +124 -46
- package/dest/config.d.ts +1 -1
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +22 -6
- package/dest/duties/validation_service.d.ts +3 -4
- package/dest/duties/validation_service.d.ts.map +1 -1
- package/dest/duties/validation_service.js +11 -22
- package/dest/factory.d.ts +7 -4
- package/dest/factory.d.ts.map +1 -1
- package/dest/factory.js +6 -5
- package/dest/index.d.ts +2 -3
- package/dest/index.d.ts.map +1 -1
- package/dest/index.js +1 -2
- package/dest/key_store/ha_key_store.js +1 -1
- package/dest/metrics.d.ts +10 -2
- package/dest/metrics.d.ts.map +1 -1
- package/dest/metrics.js +12 -0
- package/dest/proposal_handler.d.ts +107 -0
- package/dest/proposal_handler.d.ts.map +1 -0
- package/dest/proposal_handler.js +966 -0
- package/dest/validator.d.ts +18 -15
- package/dest/validator.d.ts.map +1 -1
- package/dest/validator.js +80 -191
- package/package.json +19 -19
- package/src/checkpoint_builder.ts +142 -39
- package/src/config.ts +22 -6
- package/src/duties/validation_service.ts +18 -24
- package/src/factory.ts +9 -3
- package/src/index.ts +1 -2
- package/src/key_store/ha_key_store.ts +1 -1
- package/src/metrics.ts +19 -1
- package/src/proposal_handler.ts +1033 -0
- package/src/validator.ts +103 -210
- package/dest/block_proposal_handler.d.ts +0 -63
- package/dest/block_proposal_handler.d.ts.map +0 -1
- package/dest/block_proposal_handler.js +0 -546
- 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/block_proposal_handler.ts +0 -555
- 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/src/validator.ts
CHANGED
|
@@ -9,11 +9,9 @@ import {
|
|
|
9
9
|
SlotNumber,
|
|
10
10
|
} from '@aztec/foundation/branded-types';
|
|
11
11
|
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
12
|
-
import { TimeoutError } from '@aztec/foundation/error';
|
|
13
12
|
import type { EthAddress } from '@aztec/foundation/eth-address';
|
|
14
13
|
import type { Signature } from '@aztec/foundation/eth-signature';
|
|
15
14
|
import { type LogData, type Logger, createLogger } from '@aztec/foundation/log';
|
|
16
|
-
import { retryUntil } from '@aztec/foundation/retry';
|
|
17
15
|
import { RunningPromise } from '@aztec/foundation/running-promise';
|
|
18
16
|
import { sleep } from '@aztec/foundation/sleep';
|
|
19
17
|
import { DateProvider } from '@aztec/foundation/timer';
|
|
@@ -22,16 +20,15 @@ import type { DuplicateAttestationInfo, DuplicateProposalInfo, P2P, PeerId } fro
|
|
|
22
20
|
import { AuthRequest, AuthResponse, BlockProposalValidator, ReqRespSubProtocol } from '@aztec/p2p';
|
|
23
21
|
import { OffenseType, WANT_TO_SLASH_EVENT, type Watcher, type WatcherEmitter } from '@aztec/slasher';
|
|
24
22
|
import type { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
25
|
-
import type { CommitteeAttestationsAndSigners,
|
|
23
|
+
import type { CommitteeAttestationsAndSigners, L2BlockSink, L2BlockSource } from '@aztec/stdlib/block';
|
|
26
24
|
import { getEpochAtSlot } from '@aztec/stdlib/epoch-helpers';
|
|
27
25
|
import type {
|
|
28
|
-
CreateCheckpointProposalLastBlockData,
|
|
29
26
|
ITxProvider,
|
|
30
27
|
Validator,
|
|
31
28
|
ValidatorClientFullConfig,
|
|
32
29
|
WorldStateSynchronizer,
|
|
33
30
|
} from '@aztec/stdlib/interfaces/server';
|
|
34
|
-
import {
|
|
31
|
+
import type { L1ToL2MessageSource } from '@aztec/stdlib/messaging';
|
|
35
32
|
import {
|
|
36
33
|
type BlockProposal,
|
|
37
34
|
type BlockProposalOptions,
|
|
@@ -41,22 +38,27 @@ import {
|
|
|
41
38
|
type CheckpointProposalOptions,
|
|
42
39
|
} from '@aztec/stdlib/p2p';
|
|
43
40
|
import type { CheckpointHeader } from '@aztec/stdlib/rollup';
|
|
44
|
-
import type { BlockHeader,
|
|
41
|
+
import type { BlockHeader, Tx } from '@aztec/stdlib/tx';
|
|
45
42
|
import { AttestationTimeoutError } from '@aztec/stdlib/validators';
|
|
46
43
|
import { type TelemetryClient, type Tracer, getTelemetryClient } from '@aztec/telemetry-client';
|
|
47
|
-
import {
|
|
48
|
-
|
|
44
|
+
import {
|
|
45
|
+
createHASigner,
|
|
46
|
+
createLocalSignerWithProtection,
|
|
47
|
+
createSignerFromSharedDb,
|
|
48
|
+
} from '@aztec/validator-ha-signer/factory';
|
|
49
|
+
import { DutyType, type SigningContext, type SlashingProtectionDatabase } from '@aztec/validator-ha-signer/types';
|
|
50
|
+
import type { ValidatorHASigner } from '@aztec/validator-ha-signer/validator-ha-signer';
|
|
49
51
|
|
|
50
52
|
import { EventEmitter } from 'events';
|
|
51
53
|
import type { TypedDataDefinition } from 'viem';
|
|
52
54
|
|
|
53
|
-
import { BlockProposalHandler, type BlockProposalValidationFailureReason } from './block_proposal_handler.js';
|
|
54
55
|
import type { FullNodeCheckpointsBuilder } from './checkpoint_builder.js';
|
|
55
56
|
import { ValidationService } from './duties/validation_service.js';
|
|
56
57
|
import { HAKeyStore } from './key_store/ha_key_store.js';
|
|
57
58
|
import type { ExtendedValidatorKeyStore } from './key_store/interface.js';
|
|
58
59
|
import { NodeKeystoreAdapter } from './key_store/node_keystore_adapter.js';
|
|
59
60
|
import { ValidatorMetrics } from './metrics.js';
|
|
61
|
+
import { type BlockProposalValidationFailureReason, ProposalHandler } from './proposal_handler.js';
|
|
60
62
|
|
|
61
63
|
// We maintain a set of proposers who have proposed invalid blocks.
|
|
62
64
|
// Just cap the set to avoid unbounded growth.
|
|
@@ -76,7 +78,6 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
76
78
|
private validationService: ValidationService;
|
|
77
79
|
private metrics: ValidatorMetrics;
|
|
78
80
|
private log: Logger;
|
|
79
|
-
|
|
80
81
|
// Whether it has already registered handlers on the p2p client
|
|
81
82
|
private hasRegisteredHandlers = false;
|
|
82
83
|
|
|
@@ -88,6 +89,8 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
88
89
|
|
|
89
90
|
private lastEpochForCommitteeUpdateLoop: EpochNumber | undefined;
|
|
90
91
|
private epochCacheUpdateLoop: RunningPromise;
|
|
92
|
+
/** Tracks the last epoch in which each attester successfully submitted at least one attestation. */
|
|
93
|
+
private lastAttestedEpochByAttester: Map<string, EpochNumber> = new Map();
|
|
91
94
|
|
|
92
95
|
private proposersOfInvalidBlocks: Set<string> = new Set();
|
|
93
96
|
|
|
@@ -98,13 +101,14 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
98
101
|
private keyStore: ExtendedValidatorKeyStore,
|
|
99
102
|
private epochCache: EpochCache,
|
|
100
103
|
private p2pClient: P2P,
|
|
101
|
-
private
|
|
104
|
+
private proposalHandler: ProposalHandler,
|
|
102
105
|
private blockSource: L2BlockSource,
|
|
103
106
|
private checkpointsBuilder: FullNodeCheckpointsBuilder,
|
|
104
107
|
private worldState: WorldStateSynchronizer,
|
|
105
108
|
private l1ToL2MessageSource: L1ToL2MessageSource,
|
|
106
109
|
private config: ValidatorClientFullConfig,
|
|
107
110
|
private blobClient: BlobClientInterface,
|
|
111
|
+
private slashingProtectionSigner: ValidatorHASigner,
|
|
108
112
|
private dateProvider: DateProvider = new DateProvider(),
|
|
109
113
|
telemetry: TelemetryClient = getTelemetryClient(),
|
|
110
114
|
log = createLogger('validator'),
|
|
@@ -158,6 +162,7 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
158
162
|
this.log.trace(`No committee found for slot`);
|
|
159
163
|
return;
|
|
160
164
|
}
|
|
165
|
+
this.metrics.setCurrentEpoch(epoch);
|
|
161
166
|
if (epoch !== this.lastEpochForCommitteeUpdateLoop) {
|
|
162
167
|
const me = this.getValidatorAddresses();
|
|
163
168
|
const committeeSet = new Set(committee.map(v => v.toString()));
|
|
@@ -191,12 +196,14 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
191
196
|
blobClient: BlobClientInterface,
|
|
192
197
|
dateProvider: DateProvider = new DateProvider(),
|
|
193
198
|
telemetry: TelemetryClient = getTelemetryClient(),
|
|
199
|
+
slashingProtectionDb?: SlashingProtectionDatabase,
|
|
194
200
|
) {
|
|
195
201
|
const metrics = new ValidatorMetrics(telemetry);
|
|
196
202
|
const blockProposalValidator = new BlockProposalValidator(epochCache, {
|
|
197
203
|
txsPermitted: !config.disableTransactions,
|
|
204
|
+
maxTxsPerBlock: config.validateMaxTxsPerBlock,
|
|
198
205
|
});
|
|
199
|
-
const
|
|
206
|
+
const proposalHandler = new ProposalHandler(
|
|
200
207
|
checkpointsBuilder,
|
|
201
208
|
worldState,
|
|
202
209
|
blockSource,
|
|
@@ -205,33 +212,53 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
205
212
|
blockProposalValidator,
|
|
206
213
|
epochCache,
|
|
207
214
|
config,
|
|
215
|
+
blobClient,
|
|
208
216
|
metrics,
|
|
209
217
|
dateProvider,
|
|
210
218
|
telemetry,
|
|
211
219
|
);
|
|
212
220
|
|
|
213
|
-
|
|
214
|
-
|
|
221
|
+
const nodeKeystoreAdapter = NodeKeystoreAdapter.fromKeyStoreManager(keyStoreManager);
|
|
222
|
+
let slashingProtectionSigner: ValidatorHASigner;
|
|
223
|
+
if (slashingProtectionDb) {
|
|
224
|
+
// Shared database mode: use a pre-existing database (e.g. for testing HA setups).
|
|
225
|
+
({ signer: slashingProtectionSigner } = createSignerFromSharedDb(slashingProtectionDb, config, {
|
|
226
|
+
telemetryClient: telemetry,
|
|
227
|
+
dateProvider,
|
|
228
|
+
}));
|
|
229
|
+
} else if (config.haSigningEnabled) {
|
|
230
|
+
// Multi-node HA mode: use PostgreSQL-backed distributed locking.
|
|
215
231
|
// If maxStuckDutiesAgeMs is not explicitly set, compute it from Aztec slot duration
|
|
216
232
|
const haConfig = {
|
|
217
233
|
...config,
|
|
218
234
|
maxStuckDutiesAgeMs: config.maxStuckDutiesAgeMs ?? epochCache.getL1Constants().slotDuration * 2 * 1000,
|
|
219
235
|
};
|
|
220
|
-
|
|
221
|
-
|
|
236
|
+
({ signer: slashingProtectionSigner } = await createHASigner(haConfig, {
|
|
237
|
+
telemetryClient: telemetry,
|
|
238
|
+
dateProvider,
|
|
239
|
+
}));
|
|
240
|
+
} else {
|
|
241
|
+
// Single-node mode: use LMDB-backed local signing protection.
|
|
242
|
+
// This prevents double-signing if the node crashes and restarts mid-proposal.
|
|
243
|
+
({ signer: slashingProtectionSigner } = await createLocalSignerWithProtection(config, {
|
|
244
|
+
telemetryClient: telemetry,
|
|
245
|
+
dateProvider,
|
|
246
|
+
}));
|
|
222
247
|
}
|
|
248
|
+
const validatorKeyStore: ExtendedValidatorKeyStore = new HAKeyStore(nodeKeystoreAdapter, slashingProtectionSigner);
|
|
223
249
|
|
|
224
250
|
const validator = new ValidatorClient(
|
|
225
251
|
validatorKeyStore,
|
|
226
252
|
epochCache,
|
|
227
253
|
p2pClient,
|
|
228
|
-
|
|
254
|
+
proposalHandler,
|
|
229
255
|
blockSource,
|
|
230
256
|
checkpointsBuilder,
|
|
231
257
|
worldState,
|
|
232
258
|
l1ToL2MessageSource,
|
|
233
259
|
config,
|
|
234
260
|
blobClient,
|
|
261
|
+
slashingProtectionSigner,
|
|
235
262
|
dateProvider,
|
|
236
263
|
telemetry,
|
|
237
264
|
);
|
|
@@ -245,8 +272,8 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
245
272
|
.filter(addr => !this.config.disabledValidators.some(disabled => disabled.equals(addr)));
|
|
246
273
|
}
|
|
247
274
|
|
|
248
|
-
public
|
|
249
|
-
return this.
|
|
275
|
+
public getProposalHandler() {
|
|
276
|
+
return this.proposalHandler;
|
|
250
277
|
}
|
|
251
278
|
|
|
252
279
|
public signWithAddress(addr: EthAddress, msg: TypedDataDefinition, context: SigningContext) {
|
|
@@ -269,6 +296,12 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
269
296
|
this.config = { ...this.config, ...config };
|
|
270
297
|
}
|
|
271
298
|
|
|
299
|
+
public reloadKeystore(newManager: KeystoreManager): void {
|
|
300
|
+
const newAdapter = NodeKeystoreAdapter.fromKeyStoreManager(newManager);
|
|
301
|
+
this.keyStore = new HAKeyStore(newAdapter, this.slashingProtectionSigner);
|
|
302
|
+
this.validationService = new ValidationService(this.keyStore, this.log.createChild('validation-service'));
|
|
303
|
+
}
|
|
304
|
+
|
|
272
305
|
public async start() {
|
|
273
306
|
if (this.epochCacheUpdateLoop.isRunning()) {
|
|
274
307
|
this.log.warn(`Validator client already started`);
|
|
@@ -313,7 +346,7 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
313
346
|
checkpoint: CheckpointProposalCore,
|
|
314
347
|
proposalSender: PeerId,
|
|
315
348
|
): Promise<CheckpointAttestation[] | undefined> => this.attestToCheckpointProposal(checkpoint, proposalSender);
|
|
316
|
-
this.p2pClient.
|
|
349
|
+
this.p2pClient.registerValidatorCheckpointProposalHandler(checkpointHandler);
|
|
317
350
|
|
|
318
351
|
// Duplicate proposal handler - triggers slashing for equivocation
|
|
319
352
|
this.p2pClient.registerDuplicateProposalCallback((info: DuplicateProposalInfo) => {
|
|
@@ -352,13 +385,12 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
352
385
|
return false;
|
|
353
386
|
}
|
|
354
387
|
|
|
355
|
-
//
|
|
388
|
+
// Log self-proposals from HA peers (same validator key on different nodes)
|
|
356
389
|
if (this.getValidatorAddresses().some(addr => addr.equals(proposer))) {
|
|
357
|
-
this.log.
|
|
390
|
+
this.log.verbose(`Processing block proposal from HA peer for slot ${slotNumber}`, {
|
|
358
391
|
proposer: proposer.toString(),
|
|
359
392
|
slotNumber,
|
|
360
393
|
});
|
|
361
|
-
return false;
|
|
362
394
|
}
|
|
363
395
|
|
|
364
396
|
// Check if we're in the committee (for metrics purposes)
|
|
@@ -374,25 +406,25 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
374
406
|
|
|
375
407
|
// Reexecute txs if we are part of the committee, or if slashing is enabled, or if we are configured to always reexecute.
|
|
376
408
|
// In fisherman mode, we always reexecute to validate proposals.
|
|
377
|
-
const {
|
|
378
|
-
this.config;
|
|
409
|
+
const { slashBroadcastedInvalidBlockPenalty, alwaysReexecuteBlockProposals, fishermanMode } = this.config;
|
|
379
410
|
const shouldReexecute =
|
|
380
411
|
fishermanMode ||
|
|
381
|
-
|
|
382
|
-
|
|
412
|
+
slashBroadcastedInvalidBlockPenalty > 0n ||
|
|
413
|
+
partOfCommittee ||
|
|
383
414
|
alwaysReexecuteBlockProposals ||
|
|
384
415
|
this.blobClient.canUpload();
|
|
385
416
|
|
|
386
|
-
const validationResult = await this.
|
|
417
|
+
const validationResult = await this.proposalHandler.handleBlockProposal(
|
|
387
418
|
proposal,
|
|
388
419
|
proposalSender,
|
|
389
420
|
!!shouldReexecute && !escapeHatchOpen,
|
|
390
421
|
);
|
|
391
422
|
|
|
392
423
|
if (!validationResult.isValid) {
|
|
393
|
-
this.log.warn(`Block proposal validation failed: ${validationResult.reason}`, proposalInfo);
|
|
394
|
-
|
|
395
424
|
const reason = validationResult.reason || 'unknown';
|
|
425
|
+
|
|
426
|
+
this.log.warn(`Block proposal validation failed: ${reason}`, proposalInfo);
|
|
427
|
+
|
|
396
428
|
// Classify failure reason: bad proposal vs node issue
|
|
397
429
|
const badProposalReasons: BlockProposalValidationFailureReason[] = [
|
|
398
430
|
'invalid_proposal',
|
|
@@ -447,62 +479,50 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
447
479
|
proposal: CheckpointProposalCore,
|
|
448
480
|
_proposalSender: PeerId,
|
|
449
481
|
): Promise<CheckpointAttestation[] | undefined> {
|
|
450
|
-
const
|
|
482
|
+
const proposalSlotNumber = proposal.slotNumber;
|
|
451
483
|
const proposer = proposal.getSender();
|
|
452
484
|
|
|
453
485
|
// If escape hatch is open for this slot's epoch, do not attest.
|
|
454
|
-
if (await this.epochCache.isEscapeHatchOpenAtSlot(
|
|
455
|
-
this.log.warn(`Escape hatch open for slot ${
|
|
456
|
-
return undefined;
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
// Reject proposals with invalid signatures
|
|
460
|
-
if (!proposer) {
|
|
461
|
-
this.log.warn(`Received checkpoint proposal with invalid signature for slot ${slotNumber}`);
|
|
486
|
+
if (await this.epochCache.isEscapeHatchOpenAtSlot(proposalSlotNumber)) {
|
|
487
|
+
this.log.warn(`Escape hatch open for slot ${proposalSlotNumber}, skipping checkpoint attestation handling`);
|
|
462
488
|
return undefined;
|
|
463
489
|
}
|
|
464
490
|
|
|
465
491
|
// Ignore proposals from ourselves (may happen in HA setups)
|
|
466
|
-
if (this.getValidatorAddresses().some(addr => addr.equals(proposer))) {
|
|
467
|
-
this.log.
|
|
492
|
+
if (proposer && this.getValidatorAddresses().some(addr => addr.equals(proposer))) {
|
|
493
|
+
this.log.debug(`Ignoring block proposal from self for slot ${proposalSlotNumber}`, {
|
|
468
494
|
proposer: proposer.toString(),
|
|
469
|
-
|
|
495
|
+
proposalSlotNumber,
|
|
470
496
|
});
|
|
471
497
|
return undefined;
|
|
472
498
|
}
|
|
473
499
|
|
|
474
|
-
// Check that I have any address in
|
|
475
|
-
const inCommittee = await this.epochCache.filterInCommittee(
|
|
500
|
+
// Check that I have any address in the committee where this checkpoint will land before attesting
|
|
501
|
+
const inCommittee = await this.epochCache.filterInCommittee(proposalSlotNumber, this.getValidatorAddresses());
|
|
476
502
|
const partOfCommittee = inCommittee.length > 0;
|
|
477
503
|
|
|
478
504
|
const proposalInfo = {
|
|
479
|
-
|
|
505
|
+
proposalSlotNumber,
|
|
480
506
|
archive: proposal.archive.toString(),
|
|
481
|
-
proposer: proposer
|
|
482
|
-
txCount: proposal.txHashes.length,
|
|
507
|
+
proposer: proposer?.toString(),
|
|
483
508
|
};
|
|
484
|
-
this.log.info(`Received checkpoint proposal for slot ${
|
|
509
|
+
this.log.info(`Received checkpoint proposal for slot ${proposalSlotNumber}`, {
|
|
485
510
|
...proposalInfo,
|
|
486
|
-
txHashes: proposal.txHashes.map(t => t.toString()),
|
|
487
511
|
fishermanMode: this.config.fishermanMode || false,
|
|
488
512
|
});
|
|
489
513
|
|
|
490
|
-
// Validate the checkpoint proposal before attesting (unless skipCheckpointProposalValidation is set)
|
|
514
|
+
// Validate the checkpoint proposal before attesting (unless skipCheckpointProposalValidation is set).
|
|
515
|
+
// Uses the cached result from the all-nodes callback if available (avoids double validation).
|
|
491
516
|
if (this.config.skipCheckpointProposalValidation) {
|
|
492
|
-
this.log.warn(`Skipping checkpoint proposal validation for slot ${
|
|
517
|
+
this.log.warn(`Skipping checkpoint proposal validation for slot ${proposalSlotNumber}`, proposalInfo);
|
|
493
518
|
} else {
|
|
494
|
-
const validationResult = await this.
|
|
519
|
+
const validationResult = await this.proposalHandler.handleCheckpointProposal(proposal, proposalInfo);
|
|
495
520
|
if (!validationResult.isValid) {
|
|
496
521
|
this.log.warn(`Checkpoint proposal validation failed: ${validationResult.reason}`, proposalInfo);
|
|
497
522
|
return undefined;
|
|
498
523
|
}
|
|
499
524
|
}
|
|
500
525
|
|
|
501
|
-
// Upload blobs to filestore if we can (fire and forget)
|
|
502
|
-
if (this.blobClient.canUpload()) {
|
|
503
|
-
void this.uploadBlobsForCheckpoint(proposal, proposalInfo);
|
|
504
|
-
}
|
|
505
|
-
|
|
506
526
|
// Check that I have any address in current committee before attesting
|
|
507
527
|
// In fisherman mode, we still create attestations for validation even if not in committee
|
|
508
528
|
if (!partOfCommittee && !this.config.fishermanMode) {
|
|
@@ -511,14 +531,28 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
511
531
|
}
|
|
512
532
|
|
|
513
533
|
// Provided all of the above checks pass, we can attest to the proposal
|
|
514
|
-
this.log.info(
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
534
|
+
this.log.info(
|
|
535
|
+
`${partOfCommittee ? 'Attesting to' : 'Validated'} checkpoint proposal for slot ${proposalSlotNumber}`,
|
|
536
|
+
{
|
|
537
|
+
...proposalInfo,
|
|
538
|
+
inCommittee: partOfCommittee,
|
|
539
|
+
fishermanMode: this.config.fishermanMode || false,
|
|
540
|
+
},
|
|
541
|
+
);
|
|
519
542
|
|
|
520
543
|
this.metrics.incSuccessfulAttestations(inCommittee.length);
|
|
521
544
|
|
|
545
|
+
// Track epoch participation per attester: count each (attester, epoch) pair at most once
|
|
546
|
+
const proposalEpoch = getEpochAtSlot(proposalSlotNumber, this.epochCache.getL1Constants());
|
|
547
|
+
for (const attester of inCommittee) {
|
|
548
|
+
const key = attester.toString();
|
|
549
|
+
const lastEpoch = this.lastAttestedEpochByAttester.get(key);
|
|
550
|
+
if (lastEpoch === undefined || proposalEpoch > lastEpoch) {
|
|
551
|
+
this.lastAttestedEpochByAttester.set(key, proposalEpoch);
|
|
552
|
+
this.metrics.incAttestedEpochCount(attester);
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
|
|
522
556
|
// Determine which validators should attest
|
|
523
557
|
let attestors: EthAddress[];
|
|
524
558
|
if (partOfCommittee) {
|
|
@@ -537,7 +571,7 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
537
571
|
|
|
538
572
|
if (this.config.fishermanMode) {
|
|
539
573
|
// bail out early and don't save attestations to the pool in fisherman mode
|
|
540
|
-
this.log.info(`Creating checkpoint attestations for slot ${
|
|
574
|
+
this.log.info(`Creating checkpoint attestations for slot ${proposalSlotNumber}`, {
|
|
541
575
|
...proposalInfo,
|
|
542
576
|
attestors: attestors.map(a => a.toString()),
|
|
543
577
|
});
|
|
@@ -586,153 +620,10 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
586
620
|
return attestations;
|
|
587
621
|
}
|
|
588
622
|
|
|
589
|
-
/**
|
|
590
|
-
* Validates a checkpoint proposal by building the full checkpoint and comparing it with the proposal.
|
|
591
|
-
* @returns Validation result with isValid flag and reason if invalid.
|
|
592
|
-
*/
|
|
593
|
-
private async validateCheckpointProposal(
|
|
594
|
-
proposal: CheckpointProposalCore,
|
|
595
|
-
proposalInfo: LogData,
|
|
596
|
-
): Promise<{ isValid: true } | { isValid: false; reason: string }> {
|
|
597
|
-
const slot = proposal.slotNumber;
|
|
598
|
-
const timeoutSeconds = 10; // TODO(palla/mbps): This should map to the timetable settings
|
|
599
|
-
|
|
600
|
-
// Wait for last block to sync by archive
|
|
601
|
-
let lastBlockHeader: BlockHeader | undefined;
|
|
602
|
-
try {
|
|
603
|
-
lastBlockHeader = await retryUntil(
|
|
604
|
-
async () => {
|
|
605
|
-
await this.blockSource.syncImmediate();
|
|
606
|
-
return this.blockSource.getBlockHeaderByArchive(proposal.archive);
|
|
607
|
-
},
|
|
608
|
-
`waiting for block with archive ${proposal.archive.toString()} for slot ${slot}`,
|
|
609
|
-
timeoutSeconds,
|
|
610
|
-
0.5,
|
|
611
|
-
);
|
|
612
|
-
} catch (err) {
|
|
613
|
-
if (err instanceof TimeoutError) {
|
|
614
|
-
this.log.warn(`Timed out waiting for block with archive matching checkpoint proposal`, proposalInfo);
|
|
615
|
-
return { isValid: false, reason: 'last_block_not_found' };
|
|
616
|
-
}
|
|
617
|
-
this.log.error(`Error fetching last block for checkpoint proposal`, err, proposalInfo);
|
|
618
|
-
return { isValid: false, reason: 'block_fetch_error' };
|
|
619
|
-
}
|
|
620
|
-
|
|
621
|
-
if (!lastBlockHeader) {
|
|
622
|
-
this.log.warn(`Last block not found for checkpoint proposal`, proposalInfo);
|
|
623
|
-
return { isValid: false, reason: 'last_block_not_found' };
|
|
624
|
-
}
|
|
625
|
-
|
|
626
|
-
// Get all full blocks for the slot and checkpoint
|
|
627
|
-
const blocks = await this.blockSource.getBlocksForSlot(slot);
|
|
628
|
-
if (blocks.length === 0) {
|
|
629
|
-
this.log.warn(`No blocks found for slot ${slot}`, proposalInfo);
|
|
630
|
-
return { isValid: false, reason: 'no_blocks_for_slot' };
|
|
631
|
-
}
|
|
632
|
-
|
|
633
|
-
this.log.debug(`Found ${blocks.length} blocks for slot ${slot}`, {
|
|
634
|
-
...proposalInfo,
|
|
635
|
-
blockNumbers: blocks.map(b => b.number),
|
|
636
|
-
});
|
|
637
|
-
|
|
638
|
-
// Get checkpoint constants from first block
|
|
639
|
-
const firstBlock = blocks[0];
|
|
640
|
-
const constants = this.extractCheckpointConstants(firstBlock);
|
|
641
|
-
const checkpointNumber = firstBlock.checkpointNumber;
|
|
642
|
-
|
|
643
|
-
// Get L1-to-L2 messages for this checkpoint
|
|
644
|
-
const l1ToL2Messages = await this.l1ToL2MessageSource.getL1ToL2Messages(checkpointNumber);
|
|
645
|
-
|
|
646
|
-
// Compute the previous checkpoint out hashes for the epoch.
|
|
647
|
-
// TODO: There can be a more efficient way to get the previous checkpoint out hashes without having to fetch the
|
|
648
|
-
// actual checkpoints and the blocks/txs in them.
|
|
649
|
-
const epoch = getEpochAtSlot(slot, this.epochCache.getL1Constants());
|
|
650
|
-
const previousCheckpoints = (await this.blockSource.getCheckpointsForEpoch(epoch))
|
|
651
|
-
.filter(b => b.number < checkpointNumber)
|
|
652
|
-
.sort((a, b) => a.number - b.number);
|
|
653
|
-
const previousCheckpointOutHashes = previousCheckpoints.map(c => c.getCheckpointOutHash());
|
|
654
|
-
|
|
655
|
-
// Fork world state at the block before the first block
|
|
656
|
-
const parentBlockNumber = BlockNumber(firstBlock.number - 1);
|
|
657
|
-
const fork = await this.worldState.fork(parentBlockNumber);
|
|
658
|
-
|
|
659
|
-
try {
|
|
660
|
-
// Create checkpoint builder with all existing blocks
|
|
661
|
-
const checkpointBuilder = await this.checkpointsBuilder.openCheckpoint(
|
|
662
|
-
checkpointNumber,
|
|
663
|
-
constants,
|
|
664
|
-
l1ToL2Messages,
|
|
665
|
-
previousCheckpointOutHashes,
|
|
666
|
-
fork,
|
|
667
|
-
blocks,
|
|
668
|
-
this.log.getBindings(),
|
|
669
|
-
);
|
|
670
|
-
|
|
671
|
-
// Complete the checkpoint to get computed values
|
|
672
|
-
const computedCheckpoint = await checkpointBuilder.completeCheckpoint();
|
|
673
|
-
|
|
674
|
-
// Compare checkpoint header with proposal
|
|
675
|
-
if (!computedCheckpoint.header.equals(proposal.checkpointHeader)) {
|
|
676
|
-
this.log.warn(`Checkpoint header mismatch`, {
|
|
677
|
-
...proposalInfo,
|
|
678
|
-
computed: computedCheckpoint.header.toInspect(),
|
|
679
|
-
proposal: proposal.checkpointHeader.toInspect(),
|
|
680
|
-
});
|
|
681
|
-
return { isValid: false, reason: 'checkpoint_header_mismatch' };
|
|
682
|
-
}
|
|
683
|
-
|
|
684
|
-
// Compare archive root with proposal
|
|
685
|
-
if (!computedCheckpoint.archive.root.equals(proposal.archive)) {
|
|
686
|
-
this.log.warn(`Archive root mismatch`, {
|
|
687
|
-
...proposalInfo,
|
|
688
|
-
computed: computedCheckpoint.archive.root.toString(),
|
|
689
|
-
proposal: proposal.archive.toString(),
|
|
690
|
-
});
|
|
691
|
-
return { isValid: false, reason: 'archive_mismatch' };
|
|
692
|
-
}
|
|
693
|
-
|
|
694
|
-
// Check that the accumulated epoch out hash matches the value in the proposal.
|
|
695
|
-
// The epoch out hash is the accumulated hash of all checkpoint out hashes in the epoch.
|
|
696
|
-
const checkpointOutHash = computedCheckpoint.getCheckpointOutHash();
|
|
697
|
-
const computedEpochOutHash = accumulateCheckpointOutHashes([...previousCheckpointOutHashes, checkpointOutHash]);
|
|
698
|
-
const proposalEpochOutHash = proposal.checkpointHeader.epochOutHash;
|
|
699
|
-
if (!computedEpochOutHash.equals(proposalEpochOutHash)) {
|
|
700
|
-
this.log.warn(`Epoch out hash mismatch`, {
|
|
701
|
-
proposalEpochOutHash: proposalEpochOutHash.toString(),
|
|
702
|
-
computedEpochOutHash: computedEpochOutHash.toString(),
|
|
703
|
-
checkpointOutHash: checkpointOutHash.toString(),
|
|
704
|
-
previousCheckpointOutHashes: previousCheckpointOutHashes.map(h => h.toString()),
|
|
705
|
-
...proposalInfo,
|
|
706
|
-
});
|
|
707
|
-
return { isValid: false, reason: 'out_hash_mismatch' };
|
|
708
|
-
}
|
|
709
|
-
|
|
710
|
-
this.log.verbose(`Checkpoint proposal validation successful for slot ${slot}`, proposalInfo);
|
|
711
|
-
return { isValid: true };
|
|
712
|
-
} finally {
|
|
713
|
-
await fork.close();
|
|
714
|
-
}
|
|
715
|
-
}
|
|
716
|
-
|
|
717
|
-
/**
|
|
718
|
-
* Extract checkpoint global variables from a block.
|
|
719
|
-
*/
|
|
720
|
-
private extractCheckpointConstants(block: L2Block): CheckpointGlobalVariables {
|
|
721
|
-
const gv = block.header.globalVariables;
|
|
722
|
-
return {
|
|
723
|
-
chainId: gv.chainId,
|
|
724
|
-
version: gv.version,
|
|
725
|
-
slotNumber: gv.slotNumber,
|
|
726
|
-
coinbase: gv.coinbase,
|
|
727
|
-
feeRecipient: gv.feeRecipient,
|
|
728
|
-
gasFees: gv.gasFees,
|
|
729
|
-
};
|
|
730
|
-
}
|
|
731
|
-
|
|
732
623
|
/**
|
|
733
624
|
* Uploads blobs for a checkpoint to the filestore (fire and forget).
|
|
734
625
|
*/
|
|
735
|
-
|
|
626
|
+
protected async uploadBlobsForCheckpoint(proposal: CheckpointProposalCore, proposalInfo: LogData): Promise<void> {
|
|
736
627
|
try {
|
|
737
628
|
const lastBlockHeader = await this.blockSource.getBlockHeaderByArchive(proposal.archive);
|
|
738
629
|
if (!lastBlockHeader) {
|
|
@@ -747,7 +638,7 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
747
638
|
}
|
|
748
639
|
|
|
749
640
|
const blobFields = blocks.flatMap(b => b.toBlobFields());
|
|
750
|
-
const blobs: Blob[] = getBlobsPerL1Block(blobFields);
|
|
641
|
+
const blobs: Blob[] = await getBlobsPerL1Block(blobFields);
|
|
751
642
|
await this.blobClient.sendBlobsToFilestore(blobs);
|
|
752
643
|
this.log.debug(`Uploaded ${blobs.length} blobs to filestore for checkpoint at slot ${proposal.slotNumber}`, {
|
|
753
644
|
...proposalInfo,
|
|
@@ -876,7 +767,8 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
876
767
|
async createCheckpointProposal(
|
|
877
768
|
checkpointHeader: CheckpointHeader,
|
|
878
769
|
archive: Fr,
|
|
879
|
-
|
|
770
|
+
feeAssetPriceModifier: bigint,
|
|
771
|
+
lastBlockProposal: BlockProposal | undefined,
|
|
880
772
|
proposerAddress: EthAddress | undefined,
|
|
881
773
|
options: CheckpointProposalOptions = {},
|
|
882
774
|
): Promise<CheckpointProposal> {
|
|
@@ -897,7 +789,8 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
897
789
|
const newProposal = await this.validationService.createCheckpointProposal(
|
|
898
790
|
checkpointHeader,
|
|
899
791
|
archive,
|
|
900
|
-
|
|
792
|
+
feeAssetPriceModifier,
|
|
793
|
+
lastBlockProposal,
|
|
901
794
|
proposerAddress,
|
|
902
795
|
options,
|
|
903
796
|
);
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
import type { EpochCache } from '@aztec/epoch-cache';
|
|
2
|
-
import { BlockNumber, CheckpointNumber } from '@aztec/foundation/branded-types';
|
|
3
|
-
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
4
|
-
import { DateProvider } from '@aztec/foundation/timer';
|
|
5
|
-
import type { P2P, PeerId } from '@aztec/p2p';
|
|
6
|
-
import { BlockProposalValidator } from '@aztec/p2p/msg_validators';
|
|
7
|
-
import type { L2Block, L2BlockSink, L2BlockSource } from '@aztec/stdlib/block';
|
|
8
|
-
import type { ITxProvider, ValidatorClientFullConfig, WorldStateSynchronizer } from '@aztec/stdlib/interfaces/server';
|
|
9
|
-
import { type L1ToL2MessageSource } from '@aztec/stdlib/messaging';
|
|
10
|
-
import type { BlockProposal } from '@aztec/stdlib/p2p';
|
|
11
|
-
import { type FailedTx, type Tx } from '@aztec/stdlib/tx';
|
|
12
|
-
import { type TelemetryClient, type Tracer } from '@aztec/telemetry-client';
|
|
13
|
-
import type { FullNodeCheckpointsBuilder } from './checkpoint_builder.js';
|
|
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';
|
|
16
|
-
type ReexecuteTransactionsResult = {
|
|
17
|
-
block: L2Block;
|
|
18
|
-
failedTxs: FailedTx[];
|
|
19
|
-
reexecutionTimeMs: number;
|
|
20
|
-
totalManaUsed: number;
|
|
21
|
-
};
|
|
22
|
-
export type BlockProposalValidationSuccessResult = {
|
|
23
|
-
isValid: true;
|
|
24
|
-
blockNumber: BlockNumber;
|
|
25
|
-
reexecutionResult?: ReexecuteTransactionsResult;
|
|
26
|
-
};
|
|
27
|
-
export type BlockProposalValidationFailureResult = {
|
|
28
|
-
isValid: false;
|
|
29
|
-
reason: BlockProposalValidationFailureReason;
|
|
30
|
-
blockNumber?: BlockNumber;
|
|
31
|
-
reexecutionResult?: ReexecuteTransactionsResult;
|
|
32
|
-
};
|
|
33
|
-
export type BlockProposalValidationResult = BlockProposalValidationSuccessResult | BlockProposalValidationFailureResult;
|
|
34
|
-
export declare class BlockProposalHandler {
|
|
35
|
-
private checkpointsBuilder;
|
|
36
|
-
private worldState;
|
|
37
|
-
private blockSource;
|
|
38
|
-
private l1ToL2MessageSource;
|
|
39
|
-
private txProvider;
|
|
40
|
-
private blockProposalValidator;
|
|
41
|
-
private epochCache;
|
|
42
|
-
private config;
|
|
43
|
-
private metrics?;
|
|
44
|
-
private dateProvider;
|
|
45
|
-
private log;
|
|
46
|
-
readonly tracer: Tracer;
|
|
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
|
-
registerForReexecution(p2pClient: P2P): BlockProposalHandler;
|
|
49
|
-
handleBlockProposal(proposal: BlockProposal, proposalSender: PeerId, shouldReexecute: boolean): Promise<BlockProposalValidationResult>;
|
|
50
|
-
private getParentBlock;
|
|
51
|
-
private computeCheckpointNumber;
|
|
52
|
-
/**
|
|
53
|
-
* Validates that a non-first block in a checkpoint has consistent global variables with its parent.
|
|
54
|
-
* For blocks with indexWithinCheckpoint > 0, all global variables except blockNumber must match the parent.
|
|
55
|
-
* @returns A failure result if validation fails, undefined if validation passes
|
|
56
|
-
*/
|
|
57
|
-
private validateNonFirstBlockInCheckpoint;
|
|
58
|
-
private getReexecutionDeadline;
|
|
59
|
-
private getReexecuteFailureReason;
|
|
60
|
-
reexecuteTransactions(proposal: BlockProposal, blockNumber: BlockNumber, checkpointNumber: CheckpointNumber, txs: Tx[], l1ToL2Messages: Fr[], previousCheckpointOutHashes: Fr[]): Promise<ReexecuteTransactionsResult>;
|
|
61
|
-
}
|
|
62
|
-
export {};
|
|
63
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYmxvY2tfcHJvcG9zYWxfaGFuZGxlci5kLnRzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL2Jsb2NrX3Byb3Bvc2FsX2hhbmRsZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQ0EsT0FBTyxLQUFLLEVBQUUsVUFBVSxFQUFFLE1BQU0sb0JBQW9CLENBQUM7QUFDckQsT0FBTyxFQUFFLFdBQVcsRUFBRSxnQkFBZ0IsRUFBYyxNQUFNLGlDQUFpQyxDQUFDO0FBRTVGLE9BQU8sRUFBRSxFQUFFLEVBQUUsTUFBTSxnQ0FBZ0MsQ0FBQztBQUlwRCxPQUFPLEVBQUUsWUFBWSxFQUFTLE1BQU0seUJBQXlCLENBQUM7QUFDOUQsT0FBTyxLQUFLLEVBQUUsR0FBRyxFQUFFLE1BQU0sRUFBRSxNQUFNLFlBQVksQ0FBQztBQUM5QyxPQUFPLEVBQUUsc0JBQXNCLEVBQUUsTUFBTSwyQkFBMkIsQ0FBQztBQUNuRSxPQUFPLEtBQUssRUFBRSxPQUFPLEVBQUUsV0FBVyxFQUFFLGFBQWEsRUFBRSxNQUFNLHFCQUFxQixDQUFDO0FBRS9FLE9BQU8sS0FBSyxFQUFFLFdBQVcsRUFBRSx5QkFBeUIsRUFBRSxzQkFBc0IsRUFBRSxNQUFNLGlDQUFpQyxDQUFDO0FBQ3RILE9BQU8sRUFDTCxLQUFLLG1CQUFtQixFQUd6QixNQUFNLHlCQUF5QixDQUFDO0FBQ2pDLE9BQU8sS0FBSyxFQUFFLGFBQWEsRUFBRSxNQUFNLG1CQUFtQixDQUFDO0FBQ3ZELE9BQU8sRUFBK0MsS0FBSyxRQUFRLEVBQUUsS0FBSyxFQUFFLEVBQUUsTUFBTSxrQkFBa0IsQ0FBQztBQU92RyxPQUFPLEVBQUUsS0FBSyxlQUFlLEVBQUUsS0FBSyxNQUFNLEVBQXNCLE1BQU0seUJBQXlCLENBQUM7QUFFaEcsT0FBTyxLQUFLLEVBQUUsMEJBQTBCLEVBQUUsTUFBTSx5QkFBeUIsQ0FBQztBQUMxRSxPQUFPLEtBQUssRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLGNBQWMsQ0FBQztBQUVyRCxNQUFN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
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"block_proposal_handler.d.ts","sourceRoot":"","sources":["../src/block_proposal_handler.ts"],"names":[],"mappings":"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"}
|