@aztec/validator-client 0.0.1-commit.8c0b8ff → 0.0.1-commit.8cb2d04d8
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 +0 -2
- package/dest/checkpoint_builder.js +1 -1
- package/dest/config.d.ts +1 -1
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +6 -10
- package/dest/duties/validation_service.d.ts +7 -9
- package/dest/duties/validation_service.d.ts.map +1 -1
- package/dest/duties/validation_service.js +13 -25
- package/dest/factory.d.ts +1 -1
- package/dest/factory.d.ts.map +1 -1
- package/dest/factory.js +1 -1
- package/dest/metrics.d.ts +5 -1
- package/dest/metrics.d.ts.map +1 -1
- package/dest/metrics.js +12 -0
- package/dest/proposal_handler.d.ts +20 -6
- package/dest/proposal_handler.d.ts.map +1 -1
- package/dest/proposal_handler.js +230 -108
- package/dest/validator.d.ts +19 -11
- package/dest/validator.d.ts.map +1 -1
- package/dest/validator.js +90 -54
- package/package.json +19 -19
- package/src/checkpoint_builder.ts +1 -1
- package/src/config.ts +6 -10
- package/src/duties/validation_service.ts +16 -29
- package/src/factory.ts +1 -0
- package/src/metrics.ts +18 -0
- package/src/proposal_handler.ts +252 -113
- package/src/validator.ts +118 -68
package/src/validator.ts
CHANGED
|
@@ -1,16 +1,11 @@
|
|
|
1
1
|
import type { BlobClientInterface } from '@aztec/blob-client/client';
|
|
2
|
+
import { type Blob, getBlobsPerL1Block } from '@aztec/blob-lib';
|
|
2
3
|
import type { EpochCache } from '@aztec/epoch-cache';
|
|
3
|
-
import {
|
|
4
|
-
BlockNumber,
|
|
5
|
-
CheckpointNumber,
|
|
6
|
-
EpochNumber,
|
|
7
|
-
IndexWithinCheckpoint,
|
|
8
|
-
SlotNumber,
|
|
9
|
-
} from '@aztec/foundation/branded-types';
|
|
4
|
+
import { CheckpointNumber, EpochNumber, IndexWithinCheckpoint, SlotNumber } from '@aztec/foundation/branded-types';
|
|
10
5
|
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
11
6
|
import type { EthAddress } from '@aztec/foundation/eth-address';
|
|
12
7
|
import type { Signature } from '@aztec/foundation/eth-signature';
|
|
13
|
-
import { type Logger, createLogger } from '@aztec/foundation/log';
|
|
8
|
+
import { type LogData, type Logger, createLogger } from '@aztec/foundation/log';
|
|
14
9
|
import { RunningPromise } from '@aztec/foundation/running-promise';
|
|
15
10
|
import { sleep } from '@aztec/foundation/sleep';
|
|
16
11
|
import { DateProvider } from '@aztec/foundation/timer';
|
|
@@ -22,7 +17,6 @@ import type { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
|
22
17
|
import type { CommitteeAttestationsAndSigners, L2BlockSink, L2BlockSource } from '@aztec/stdlib/block';
|
|
23
18
|
import { getEpochAtSlot } from '@aztec/stdlib/epoch-helpers';
|
|
24
19
|
import type {
|
|
25
|
-
CreateCheckpointProposalLastBlockData,
|
|
26
20
|
ITxProvider,
|
|
27
21
|
Validator,
|
|
28
22
|
ValidatorClientFullConfig,
|
|
@@ -41,7 +35,11 @@ import type { CheckpointHeader } from '@aztec/stdlib/rollup';
|
|
|
41
35
|
import type { BlockHeader, Tx } from '@aztec/stdlib/tx';
|
|
42
36
|
import { AttestationTimeoutError } from '@aztec/stdlib/validators';
|
|
43
37
|
import { type TelemetryClient, type Tracer, getTelemetryClient } from '@aztec/telemetry-client';
|
|
44
|
-
import {
|
|
38
|
+
import {
|
|
39
|
+
createHASigner,
|
|
40
|
+
createLocalSignerWithProtection,
|
|
41
|
+
createSignerFromSharedDb,
|
|
42
|
+
} from '@aztec/validator-ha-signer/factory';
|
|
45
43
|
import { DutyType, type SigningContext, type SlashingProtectionDatabase } from '@aztec/validator-ha-signer/types';
|
|
46
44
|
import type { ValidatorHASigner } from '@aztec/validator-ha-signer/validator-ha-signer';
|
|
47
45
|
|
|
@@ -98,9 +96,13 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
98
96
|
private epochCache: EpochCache,
|
|
99
97
|
private p2pClient: P2P,
|
|
100
98
|
private proposalHandler: ProposalHandler,
|
|
99
|
+
private blockSource: L2BlockSource,
|
|
100
|
+
private checkpointsBuilder: FullNodeCheckpointsBuilder,
|
|
101
|
+
private worldState: WorldStateSynchronizer,
|
|
102
|
+
private l1ToL2MessageSource: L1ToL2MessageSource,
|
|
101
103
|
private config: ValidatorClientFullConfig,
|
|
102
104
|
private blobClient: BlobClientInterface,
|
|
103
|
-
private
|
|
105
|
+
private slashingProtectionSigner: ValidatorHASigner,
|
|
104
106
|
private dateProvider: DateProvider = new DateProvider(),
|
|
105
107
|
telemetry: TelemetryClient = getTelemetryClient(),
|
|
106
108
|
log = createLogger('validator'),
|
|
@@ -208,35 +210,50 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
208
210
|
metrics,
|
|
209
211
|
dateProvider,
|
|
210
212
|
telemetry,
|
|
213
|
+
undefined,
|
|
211
214
|
);
|
|
212
215
|
|
|
213
216
|
const nodeKeystoreAdapter = NodeKeystoreAdapter.fromKeyStoreManager(keyStoreManager);
|
|
214
|
-
let
|
|
215
|
-
let haSigner: ValidatorHASigner | undefined;
|
|
217
|
+
let slashingProtectionSigner: ValidatorHASigner;
|
|
216
218
|
if (slashingProtectionDb) {
|
|
217
219
|
// Shared database mode: use a pre-existing database (e.g. for testing HA setups).
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
220
|
+
({ signer: slashingProtectionSigner } = createSignerFromSharedDb(slashingProtectionDb, config, {
|
|
221
|
+
telemetryClient: telemetry,
|
|
222
|
+
dateProvider,
|
|
223
|
+
}));
|
|
221
224
|
} else if (config.haSigningEnabled) {
|
|
225
|
+
// Multi-node HA mode: use PostgreSQL-backed distributed locking.
|
|
222
226
|
// If maxStuckDutiesAgeMs is not explicitly set, compute it from Aztec slot duration
|
|
223
227
|
const haConfig = {
|
|
224
228
|
...config,
|
|
225
229
|
maxStuckDutiesAgeMs: config.maxStuckDutiesAgeMs ?? epochCache.getL1Constants().slotDuration * 2 * 1000,
|
|
226
230
|
};
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
231
|
+
({ signer: slashingProtectionSigner } = await createHASigner(haConfig, {
|
|
232
|
+
telemetryClient: telemetry,
|
|
233
|
+
dateProvider,
|
|
234
|
+
}));
|
|
235
|
+
} else {
|
|
236
|
+
// Single-node mode: use LMDB-backed local signing protection.
|
|
237
|
+
// This prevents double-signing if the node crashes and restarts mid-proposal.
|
|
238
|
+
({ signer: slashingProtectionSigner } = await createLocalSignerWithProtection(config, {
|
|
239
|
+
telemetryClient: telemetry,
|
|
240
|
+
dateProvider,
|
|
241
|
+
}));
|
|
230
242
|
}
|
|
243
|
+
const validatorKeyStore: ExtendedValidatorKeyStore = new HAKeyStore(nodeKeystoreAdapter, slashingProtectionSigner);
|
|
231
244
|
|
|
232
245
|
const validator = new ValidatorClient(
|
|
233
246
|
validatorKeyStore,
|
|
234
247
|
epochCache,
|
|
235
248
|
p2pClient,
|
|
236
249
|
proposalHandler,
|
|
250
|
+
blockSource,
|
|
251
|
+
checkpointsBuilder,
|
|
252
|
+
worldState,
|
|
253
|
+
l1ToL2MessageSource,
|
|
237
254
|
config,
|
|
238
255
|
blobClient,
|
|
239
|
-
|
|
256
|
+
slashingProtectionSigner,
|
|
240
257
|
dateProvider,
|
|
241
258
|
telemetry,
|
|
242
259
|
);
|
|
@@ -275,24 +292,8 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
275
292
|
}
|
|
276
293
|
|
|
277
294
|
public reloadKeystore(newManager: KeystoreManager): void {
|
|
278
|
-
if (this.config.haSigningEnabled && !this.haSigner) {
|
|
279
|
-
this.log.warn(
|
|
280
|
-
'HA signing is enabled in config but was not initialized at startup. ' +
|
|
281
|
-
'Restart the node to enable HA signing.',
|
|
282
|
-
);
|
|
283
|
-
} else if (!this.config.haSigningEnabled && this.haSigner) {
|
|
284
|
-
this.log.warn(
|
|
285
|
-
'HA signing was disabled via config update but the HA signer is still active. ' +
|
|
286
|
-
'Restart the node to fully disable HA signing.',
|
|
287
|
-
);
|
|
288
|
-
}
|
|
289
|
-
|
|
290
295
|
const newAdapter = NodeKeystoreAdapter.fromKeyStoreManager(newManager);
|
|
291
|
-
|
|
292
|
-
this.keyStore = new HAKeyStore(newAdapter, this.haSigner);
|
|
293
|
-
} else {
|
|
294
|
-
this.keyStore = newAdapter;
|
|
295
|
-
}
|
|
296
|
+
this.keyStore = new HAKeyStore(newAdapter, this.slashingProtectionSigner);
|
|
296
297
|
this.validationService = new ValidationService(this.keyStore, this.log.createChild('validation-service'));
|
|
297
298
|
}
|
|
298
299
|
|
|
@@ -340,7 +341,7 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
340
341
|
checkpoint: CheckpointProposalCore,
|
|
341
342
|
proposalSender: PeerId,
|
|
342
343
|
): Promise<CheckpointAttestation[] | undefined> => this.attestToCheckpointProposal(checkpoint, proposalSender);
|
|
343
|
-
this.p2pClient.
|
|
344
|
+
this.p2pClient.registerValidatorCheckpointProposalHandler(checkpointHandler);
|
|
344
345
|
|
|
345
346
|
// Duplicate proposal handler - triggers slashing for equivocation
|
|
346
347
|
this.p2pClient.registerDuplicateProposalCallback((info: DuplicateProposalInfo) => {
|
|
@@ -400,12 +401,11 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
400
401
|
|
|
401
402
|
// Reexecute txs if we are part of the committee, or if slashing is enabled, or if we are configured to always reexecute.
|
|
402
403
|
// In fisherman mode, we always reexecute to validate proposals.
|
|
403
|
-
const {
|
|
404
|
-
this.config;
|
|
404
|
+
const { slashBroadcastedInvalidBlockPenalty, alwaysReexecuteBlockProposals, fishermanMode } = this.config;
|
|
405
405
|
const shouldReexecute =
|
|
406
406
|
fishermanMode ||
|
|
407
|
-
|
|
408
|
-
|
|
407
|
+
slashBroadcastedInvalidBlockPenalty > 0n ||
|
|
408
|
+
partOfCommittee ||
|
|
409
409
|
alwaysReexecuteBlockProposals ||
|
|
410
410
|
this.blobClient.canUpload();
|
|
411
411
|
|
|
@@ -474,47 +474,51 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
474
474
|
proposal: CheckpointProposalCore,
|
|
475
475
|
_proposalSender: PeerId,
|
|
476
476
|
): Promise<CheckpointAttestation[] | undefined> {
|
|
477
|
-
const
|
|
477
|
+
const proposalSlotNumber = proposal.slotNumber;
|
|
478
478
|
const proposer = proposal.getSender();
|
|
479
479
|
|
|
480
480
|
// If escape hatch is open for this slot's epoch, do not attest.
|
|
481
|
-
if (await this.epochCache.isEscapeHatchOpenAtSlot(
|
|
482
|
-
this.log.warn(`Escape hatch open for slot ${
|
|
481
|
+
if (await this.epochCache.isEscapeHatchOpenAtSlot(proposalSlotNumber)) {
|
|
482
|
+
this.log.warn(`Escape hatch open for slot ${proposalSlotNumber}, skipping checkpoint attestation handling`);
|
|
483
483
|
return undefined;
|
|
484
484
|
}
|
|
485
485
|
|
|
486
486
|
// Ignore proposals from ourselves (may happen in HA setups)
|
|
487
487
|
if (proposer && this.getValidatorAddresses().some(addr => addr.equals(proposer))) {
|
|
488
|
-
this.log.debug(`Ignoring block proposal from self for slot ${
|
|
488
|
+
this.log.debug(`Ignoring block proposal from self for slot ${proposalSlotNumber}`, {
|
|
489
489
|
proposer: proposer.toString(),
|
|
490
|
-
|
|
490
|
+
proposalSlotNumber,
|
|
491
491
|
});
|
|
492
492
|
return undefined;
|
|
493
493
|
}
|
|
494
494
|
|
|
495
495
|
// Check that I have any address in the committee where this checkpoint will land before attesting
|
|
496
|
-
const inCommittee = await this.epochCache.filterInCommittee(
|
|
496
|
+
const inCommittee = await this.epochCache.filterInCommittee(proposalSlotNumber, this.getValidatorAddresses());
|
|
497
497
|
const partOfCommittee = inCommittee.length > 0;
|
|
498
498
|
|
|
499
499
|
const proposalInfo = {
|
|
500
|
-
|
|
500
|
+
proposalSlotNumber,
|
|
501
501
|
archive: proposal.archive.toString(),
|
|
502
502
|
proposer: proposer?.toString(),
|
|
503
503
|
};
|
|
504
|
-
this.log.info(`Received checkpoint proposal for slot ${
|
|
504
|
+
this.log.info(`Received checkpoint proposal for slot ${proposalSlotNumber}`, {
|
|
505
505
|
...proposalInfo,
|
|
506
506
|
fishermanMode: this.config.fishermanMode || false,
|
|
507
507
|
});
|
|
508
508
|
|
|
509
|
-
// Validate the checkpoint proposal
|
|
509
|
+
// Validate the checkpoint proposal before attesting (unless skipCheckpointProposalValidation is set).
|
|
510
|
+
// Uses the cached result from the all-nodes callback if available (avoids double validation).
|
|
511
|
+
let checkpointNumber: CheckpointNumber;
|
|
510
512
|
if (this.config.skipCheckpointProposalValidation) {
|
|
511
|
-
this.log.warn(`Skipping checkpoint proposal validation for slot ${
|
|
513
|
+
this.log.warn(`Skipping checkpoint proposal validation for slot ${proposalSlotNumber}`, proposalInfo);
|
|
514
|
+
checkpointNumber = CheckpointNumber(0);
|
|
512
515
|
} else {
|
|
513
516
|
const validationResult = await this.proposalHandler.handleCheckpointProposal(proposal, proposalInfo);
|
|
514
517
|
if (!validationResult.isValid) {
|
|
515
518
|
this.log.warn(`Checkpoint proposal validation failed: ${validationResult.reason}`, proposalInfo);
|
|
516
519
|
return undefined;
|
|
517
520
|
}
|
|
521
|
+
checkpointNumber = validationResult.checkpointNumber;
|
|
518
522
|
}
|
|
519
523
|
|
|
520
524
|
// Check that I have any address in current committee before attesting
|
|
@@ -525,16 +529,19 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
525
529
|
}
|
|
526
530
|
|
|
527
531
|
// Provided all of the above checks pass, we can attest to the proposal
|
|
528
|
-
this.log.info(
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
532
|
+
this.log.info(
|
|
533
|
+
`${partOfCommittee ? 'Attesting to' : 'Validated'} checkpoint proposal for slot ${proposalSlotNumber}`,
|
|
534
|
+
{
|
|
535
|
+
...proposalInfo,
|
|
536
|
+
inCommittee: partOfCommittee,
|
|
537
|
+
fishermanMode: this.config.fishermanMode || false,
|
|
538
|
+
},
|
|
539
|
+
);
|
|
533
540
|
|
|
534
541
|
this.metrics.incSuccessfulAttestations(inCommittee.length);
|
|
535
542
|
|
|
536
543
|
// Track epoch participation per attester: count each (attester, epoch) pair at most once
|
|
537
|
-
const proposalEpoch = getEpochAtSlot(
|
|
544
|
+
const proposalEpoch = getEpochAtSlot(proposalSlotNumber, this.epochCache.getL1Constants());
|
|
538
545
|
for (const attester of inCommittee) {
|
|
539
546
|
const key = attester.toString();
|
|
540
547
|
const lastEpoch = this.lastAttestedEpochByAttester.get(key);
|
|
@@ -562,14 +569,14 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
562
569
|
|
|
563
570
|
if (this.config.fishermanMode) {
|
|
564
571
|
// bail out early and don't save attestations to the pool in fisherman mode
|
|
565
|
-
this.log.info(`Creating checkpoint attestations for slot ${
|
|
572
|
+
this.log.info(`Creating checkpoint attestations for slot ${proposalSlotNumber}`, {
|
|
566
573
|
...proposalInfo,
|
|
567
574
|
attestors: attestors.map(a => a.toString()),
|
|
568
575
|
});
|
|
569
576
|
return undefined;
|
|
570
577
|
}
|
|
571
578
|
|
|
572
|
-
return await this.createCheckpointAttestationsFromProposal(proposal, attestors);
|
|
579
|
+
return await this.createCheckpointAttestationsFromProposal(proposal, attestors, checkpointNumber);
|
|
573
580
|
}
|
|
574
581
|
|
|
575
582
|
/**
|
|
@@ -596,13 +603,14 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
596
603
|
private async createCheckpointAttestationsFromProposal(
|
|
597
604
|
proposal: CheckpointProposalCore,
|
|
598
605
|
attestors: EthAddress[] = [],
|
|
606
|
+
checkpointNumber: CheckpointNumber,
|
|
599
607
|
): Promise<CheckpointAttestation[] | undefined> {
|
|
600
608
|
// Equivocation check: must happen right before signing to minimize the race window
|
|
601
609
|
if (!this.shouldAttestToSlot(proposal.slotNumber)) {
|
|
602
610
|
return undefined;
|
|
603
611
|
}
|
|
604
612
|
|
|
605
|
-
const attestations = await this.validationService.attestToCheckpointProposal(proposal, attestors);
|
|
613
|
+
const attestations = await this.validationService.attestToCheckpointProposal(proposal, attestors, checkpointNumber);
|
|
606
614
|
|
|
607
615
|
// Track the proposal we attested to (to prevent equivocation)
|
|
608
616
|
this.lastAttestedProposal = proposal;
|
|
@@ -611,6 +619,35 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
611
619
|
return attestations;
|
|
612
620
|
}
|
|
613
621
|
|
|
622
|
+
/**
|
|
623
|
+
* Uploads blobs for a checkpoint to the filestore (fire and forget).
|
|
624
|
+
*/
|
|
625
|
+
protected async uploadBlobsForCheckpoint(proposal: CheckpointProposalCore, proposalInfo: LogData): Promise<void> {
|
|
626
|
+
try {
|
|
627
|
+
const lastBlockHeader = await this.blockSource.getBlockHeaderByArchive(proposal.archive);
|
|
628
|
+
if (!lastBlockHeader) {
|
|
629
|
+
this.log.warn(`Failed to get last block header for blob upload`, proposalInfo);
|
|
630
|
+
return;
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
const blocks = await this.blockSource.getBlocksForSlot(proposal.slotNumber);
|
|
634
|
+
if (blocks.length === 0) {
|
|
635
|
+
this.log.warn(`No blocks found for blob upload`, proposalInfo);
|
|
636
|
+
return;
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
const blobFields = blocks.flatMap(b => b.toBlobFields());
|
|
640
|
+
const blobs: Blob[] = await getBlobsPerL1Block(blobFields);
|
|
641
|
+
await this.blobClient.sendBlobsToFilestore(blobs);
|
|
642
|
+
this.log.debug(`Uploaded ${blobs.length} blobs to filestore for checkpoint at slot ${proposal.slotNumber}`, {
|
|
643
|
+
...proposalInfo,
|
|
644
|
+
numBlobs: blobs.length,
|
|
645
|
+
});
|
|
646
|
+
} catch (err) {
|
|
647
|
+
this.log.warn(`Failed to upload blobs for checkpoint: ${err}`, proposalInfo);
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
|
|
614
651
|
private slashInvalidBlock(proposal: BlockProposal) {
|
|
615
652
|
const proposer = proposal.getSender();
|
|
616
653
|
|
|
@@ -686,6 +723,7 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
686
723
|
|
|
687
724
|
async createBlockProposal(
|
|
688
725
|
blockHeader: BlockHeader,
|
|
726
|
+
checkpointNumber: CheckpointNumber,
|
|
689
727
|
indexWithinCheckpoint: IndexWithinCheckpoint,
|
|
690
728
|
inHash: Fr,
|
|
691
729
|
archive: Fr,
|
|
@@ -712,6 +750,7 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
712
750
|
);
|
|
713
751
|
const newProposal = await this.validationService.createBlockProposal(
|
|
714
752
|
blockHeader,
|
|
753
|
+
checkpointNumber,
|
|
715
754
|
indexWithinCheckpoint,
|
|
716
755
|
inHash,
|
|
717
756
|
archive,
|
|
@@ -729,8 +768,9 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
729
768
|
async createCheckpointProposal(
|
|
730
769
|
checkpointHeader: CheckpointHeader,
|
|
731
770
|
archive: Fr,
|
|
771
|
+
checkpointNumber: CheckpointNumber,
|
|
732
772
|
feeAssetPriceModifier: bigint,
|
|
733
|
-
|
|
773
|
+
lastBlockProposal: BlockProposal | undefined,
|
|
734
774
|
proposerAddress: EthAddress | undefined,
|
|
735
775
|
options: CheckpointProposalOptions = {},
|
|
736
776
|
): Promise<CheckpointProposal> {
|
|
@@ -751,8 +791,9 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
751
791
|
const newProposal = await this.validationService.createCheckpointProposal(
|
|
752
792
|
checkpointHeader,
|
|
753
793
|
archive,
|
|
794
|
+
checkpointNumber,
|
|
754
795
|
feeAssetPriceModifier,
|
|
755
|
-
|
|
796
|
+
lastBlockProposal,
|
|
756
797
|
proposerAddress,
|
|
757
798
|
options,
|
|
758
799
|
);
|
|
@@ -768,16 +809,24 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
768
809
|
attestationsAndSigners: CommitteeAttestationsAndSigners,
|
|
769
810
|
proposer: EthAddress,
|
|
770
811
|
slot: SlotNumber,
|
|
771
|
-
|
|
812
|
+
checkpointNumber: CheckpointNumber,
|
|
772
813
|
): Promise<Signature> {
|
|
773
|
-
return await this.validationService.signAttestationsAndSigners(
|
|
814
|
+
return await this.validationService.signAttestationsAndSigners(
|
|
815
|
+
attestationsAndSigners,
|
|
816
|
+
proposer,
|
|
817
|
+
slot,
|
|
818
|
+
checkpointNumber,
|
|
819
|
+
);
|
|
774
820
|
}
|
|
775
821
|
|
|
776
|
-
async collectOwnAttestations(
|
|
822
|
+
async collectOwnAttestations(
|
|
823
|
+
proposal: CheckpointProposal,
|
|
824
|
+
checkpointNumber: CheckpointNumber,
|
|
825
|
+
): Promise<CheckpointAttestation[]> {
|
|
777
826
|
const slot = proposal.slotNumber;
|
|
778
827
|
const inCommittee = await this.epochCache.filterInCommittee(slot, this.getValidatorAddresses());
|
|
779
828
|
this.log.debug(`Collecting ${inCommittee.length} self-attestations for slot ${slot}`, { inCommittee });
|
|
780
|
-
const attestations = await this.createCheckpointAttestationsFromProposal(proposal, inCommittee);
|
|
829
|
+
const attestations = await this.createCheckpointAttestationsFromProposal(proposal, inCommittee, checkpointNumber);
|
|
781
830
|
|
|
782
831
|
if (!attestations) {
|
|
783
832
|
return [];
|
|
@@ -796,6 +845,7 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
796
845
|
proposal: CheckpointProposal,
|
|
797
846
|
required: number,
|
|
798
847
|
deadline: Date,
|
|
848
|
+
checkpointNumber: CheckpointNumber,
|
|
799
849
|
): Promise<CheckpointAttestation[]> {
|
|
800
850
|
// Wait and poll the p2pClient's attestation pool for this checkpoint until we have enough attestations
|
|
801
851
|
const slot = proposal.slotNumber;
|
|
@@ -808,7 +858,7 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
808
858
|
throw new AttestationTimeoutError(0, required, slot);
|
|
809
859
|
}
|
|
810
860
|
|
|
811
|
-
await this.collectOwnAttestations(proposal);
|
|
861
|
+
await this.collectOwnAttestations(proposal, checkpointNumber);
|
|
812
862
|
|
|
813
863
|
const proposalId = proposal.archive.toString();
|
|
814
864
|
const myAddresses = this.getValidatorAddresses();
|