@aztec/validator-client 0.0.1-commit.e588bc7e5 → 0.0.1-commit.e5a3663dd
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/dest/config.d.ts +1 -1
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +11 -5
- package/dest/duties/validation_service.d.ts +11 -12
- package/dest/duties/validation_service.d.ts.map +1 -1
- package/dest/duties/validation_service.js +26 -38
- package/dest/factory.d.ts +1 -1
- package/dest/factory.d.ts.map +1 -1
- package/dest/factory.js +7 -2
- 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 +3 -2
- package/dest/proposal_handler.d.ts.map +1 -1
- package/dest/proposal_handler.js +37 -14
- package/dest/validator.d.ts +9 -8
- package/dest/validator.d.ts.map +1 -1
- package/dest/validator.js +32 -17
- package/package.json +19 -19
- package/src/config.ts +11 -4
- package/src/duties/validation_service.ts +45 -46
- package/src/factory.ts +6 -0
- package/src/metrics.ts +18 -0
- package/src/proposal_handler.ts +44 -19
- package/src/validator.ts +54 -20
package/src/proposal_handler.ts
CHANGED
|
@@ -76,7 +76,9 @@ export type BlockProposalValidationFailureResult = {
|
|
|
76
76
|
|
|
77
77
|
export type BlockProposalValidationResult = BlockProposalValidationSuccessResult | BlockProposalValidationFailureResult;
|
|
78
78
|
|
|
79
|
-
export type CheckpointProposalValidationResult =
|
|
79
|
+
export type CheckpointProposalValidationResult =
|
|
80
|
+
| { isValid: true; checkpointNumber: CheckpointNumber }
|
|
81
|
+
| { isValid: false; reason: string };
|
|
80
82
|
|
|
81
83
|
type CheckpointComputationResult =
|
|
82
84
|
| { checkpointNumber: CheckpointNumber; reason?: undefined }
|
|
@@ -94,7 +96,7 @@ export class ProposalHandler {
|
|
|
94
96
|
};
|
|
95
97
|
|
|
96
98
|
/** Archiver reference for setting proposed checkpoints (pipelining). Set via register(). */
|
|
97
|
-
private archiver?: Pick<Archiver, '
|
|
99
|
+
private archiver?: Pick<Archiver, 'addProposedCheckpoint' | 'getL1Constants'>;
|
|
98
100
|
|
|
99
101
|
/** Returns current validator addresses for own-proposal detection. Set via register(). */
|
|
100
102
|
private getOwnValidatorAddresses?: () => string[];
|
|
@@ -130,7 +132,7 @@ export class ProposalHandler {
|
|
|
130
132
|
register(
|
|
131
133
|
p2pClient: P2P,
|
|
132
134
|
shouldReexecute: boolean,
|
|
133
|
-
archiver?: Pick<Archiver, '
|
|
135
|
+
archiver?: Pick<Archiver, 'addProposedCheckpoint' | 'getL1Constants'>,
|
|
134
136
|
getOwnValidatorAddresses?: () => string[],
|
|
135
137
|
): ProposalHandler {
|
|
136
138
|
this.archiver = archiver;
|
|
@@ -175,6 +177,7 @@ export class ProposalHandler {
|
|
|
175
177
|
_sender: PeerId,
|
|
176
178
|
): Promise<CheckpointAttestation[] | undefined> => {
|
|
177
179
|
try {
|
|
180
|
+
const pipeliningTimer = new Timer();
|
|
178
181
|
const proposalInfo: LogData = {
|
|
179
182
|
slot: proposal.slotNumber,
|
|
180
183
|
archive: proposal.archive.toString(),
|
|
@@ -196,7 +199,10 @@ export class ProposalHandler {
|
|
|
196
199
|
|
|
197
200
|
const result = await this.handleCheckpointProposal(proposal, proposalInfo);
|
|
198
201
|
if (result.isValid && this.archiver && this.epochCache.isProposerPipeliningEnabled()) {
|
|
199
|
-
await this.setProposedCheckpointFromValidation(proposal);
|
|
202
|
+
const set = await this.setProposedCheckpointFromValidation(proposal);
|
|
203
|
+
if (set) {
|
|
204
|
+
this.metrics?.recordCheckpointProposalToPipelinedStateDuration(pipeliningTimer.ms());
|
|
205
|
+
}
|
|
200
206
|
}
|
|
201
207
|
} catch (err) {
|
|
202
208
|
this.log.warn(`Error handling checkpoint proposal for slot ${proposal.slotNumber}`, { err });
|
|
@@ -374,7 +380,6 @@ export class ProposalHandler {
|
|
|
374
380
|
|
|
375
381
|
private async getParentBlock(proposal: BlockProposal): Promise<'genesis' | BlockData | undefined> {
|
|
376
382
|
const parentArchive = proposal.blockHeader.lastArchive.root;
|
|
377
|
-
const slot = proposal.slotNumber;
|
|
378
383
|
const config = this.checkpointsBuilder.getConfig();
|
|
379
384
|
const { genesisArchiveRoot } = await this.blockSource.getGenesisValues();
|
|
380
385
|
|
|
@@ -382,7 +387,7 @@ export class ProposalHandler {
|
|
|
382
387
|
return 'genesis';
|
|
383
388
|
}
|
|
384
389
|
|
|
385
|
-
const deadline = this.getReexecutionDeadline(
|
|
390
|
+
const deadline = this.getReexecutionDeadline(proposal.slotNumber, config);
|
|
386
391
|
const currentTime = this.dateProvider.now();
|
|
387
392
|
const timeoutDurationMs = deadline.getTime() - currentTime;
|
|
388
393
|
|
|
@@ -531,8 +536,14 @@ export class ProposalHandler {
|
|
|
531
536
|
return undefined;
|
|
532
537
|
}
|
|
533
538
|
|
|
534
|
-
private getReexecutionDeadline(
|
|
535
|
-
|
|
539
|
+
private getReexecutionDeadline(
|
|
540
|
+
slotNumber: SlotNumber,
|
|
541
|
+
config: { l1GenesisTime: bigint; slotDuration: number },
|
|
542
|
+
): Date {
|
|
543
|
+
// Under proposer pipelining, the proposal slot may be ahead of wall clock time.
|
|
544
|
+
// Reexecution budgets should still be bounded by the current slot we are in now.
|
|
545
|
+
const wallclockSlot = slotNumber - this.epochCache.pipeliningOffset();
|
|
546
|
+
const nextSlotTimestampSeconds = Number(getTimestampForSlot(SlotNumber(wallclockSlot + 1), config));
|
|
536
547
|
return new Date(nextSlotTimestampSeconds * 1000);
|
|
537
548
|
}
|
|
538
549
|
|
|
@@ -545,8 +556,9 @@ export class ProposalHandler {
|
|
|
545
556
|
}
|
|
546
557
|
|
|
547
558
|
// Make a quick check before triggering an archiver sync
|
|
559
|
+
// If we are pipelining and have a pending checkpoint number stored, we will allow the block proposal to be for a slot further
|
|
548
560
|
const syncedSlot = await this.blockSource.getSyncedL2SlotNumber();
|
|
549
|
-
if (syncedSlot !== undefined && syncedSlot + 1 >= slot) {
|
|
561
|
+
if (syncedSlot !== undefined && syncedSlot + 1 + this.epochCache.pipeliningOffset() >= slot) {
|
|
550
562
|
return true;
|
|
551
563
|
}
|
|
552
564
|
|
|
@@ -555,8 +567,8 @@ export class ProposalHandler {
|
|
|
555
567
|
return await retryUntil(
|
|
556
568
|
async () => {
|
|
557
569
|
await this.blockSource.syncImmediate();
|
|
558
|
-
const
|
|
559
|
-
return
|
|
570
|
+
const updatedSyncedSlot = await this.blockSource.getSyncedL2SlotNumber();
|
|
571
|
+
return updatedSyncedSlot !== undefined && updatedSyncedSlot + 1 >= slot;
|
|
560
572
|
},
|
|
561
573
|
'wait for block source sync',
|
|
562
574
|
timeoutMs / 1000,
|
|
@@ -815,6 +827,16 @@ export class ProposalHandler {
|
|
|
815
827
|
return { isValid: false, reason: 'last_block_archive_mismatch' };
|
|
816
828
|
}
|
|
817
829
|
|
|
830
|
+
const maxBlocksPerCheckpoint = this.config.maxBlocksPerCheckpoint;
|
|
831
|
+
if (maxBlocksPerCheckpoint !== undefined && blocks.length > maxBlocksPerCheckpoint) {
|
|
832
|
+
this.log.warn(`Checkpoint proposal exceeds maxBlocksPerCheckpoint`, {
|
|
833
|
+
...proposalInfo,
|
|
834
|
+
blocksInProposal: blocks.length,
|
|
835
|
+
maxBlocksPerCheckpoint,
|
|
836
|
+
});
|
|
837
|
+
return { isValid: false, reason: 'too_many_blocks_in_checkpoint' };
|
|
838
|
+
}
|
|
839
|
+
|
|
818
840
|
this.log.debug(`Found ${blocks.length} blocks for slot ${slot}`, {
|
|
819
841
|
...proposalInfo,
|
|
820
842
|
blockNumbers: blocks.map(b => b.number),
|
|
@@ -904,7 +926,7 @@ export class ProposalHandler {
|
|
|
904
926
|
}
|
|
905
927
|
|
|
906
928
|
this.log.verbose(`Checkpoint proposal validation successful for slot ${slot}`, proposalInfo);
|
|
907
|
-
return { isValid: true };
|
|
929
|
+
return { isValid: true, checkpointNumber };
|
|
908
930
|
}
|
|
909
931
|
|
|
910
932
|
/** Extracts checkpoint global variables from a block. */
|
|
@@ -961,19 +983,19 @@ export class ProposalHandler {
|
|
|
961
983
|
* Used after successful validation of a foreign proposal.
|
|
962
984
|
* Does not retry since we already waited for the block during validation.
|
|
963
985
|
*/
|
|
964
|
-
private async setProposedCheckpointFromValidation(proposal: CheckpointProposalCore): Promise<
|
|
986
|
+
private async setProposedCheckpointFromValidation(proposal: CheckpointProposalCore): Promise<boolean> {
|
|
965
987
|
if (!this.archiver) {
|
|
966
|
-
return;
|
|
988
|
+
return false;
|
|
967
989
|
}
|
|
968
990
|
const blockData = await this.blockSource.getBlockDataByArchive(proposal.archive);
|
|
969
991
|
if (!blockData) {
|
|
970
992
|
this.log.debug(`Block data not found for checkpoint proposal archive, cannot set proposed checkpoint`, {
|
|
971
993
|
archive: proposal.archive.toString(),
|
|
972
994
|
});
|
|
973
|
-
return;
|
|
995
|
+
return false;
|
|
974
996
|
}
|
|
975
997
|
|
|
976
|
-
await this.archiver.
|
|
998
|
+
await this.archiver.addProposedCheckpoint({
|
|
977
999
|
header: proposal.checkpointHeader,
|
|
978
1000
|
checkpointNumber: blockData.checkpointNumber,
|
|
979
1001
|
startBlock: BlockNumber(blockData.header.getBlockNumber() - blockData.indexWithinCheckpoint),
|
|
@@ -981,6 +1003,7 @@ export class ProposalHandler {
|
|
|
981
1003
|
totalManaUsed: proposal.checkpointHeader.totalManaUsed.toBigInt(),
|
|
982
1004
|
feeAssetPriceModifier: proposal.feeAssetPriceModifier,
|
|
983
1005
|
});
|
|
1006
|
+
return true;
|
|
984
1007
|
}
|
|
985
1008
|
|
|
986
1009
|
/**
|
|
@@ -988,9 +1011,9 @@ export class ProposalHandler {
|
|
|
988
1011
|
* Retries fetching block data since the checkpoint proposal often arrives before the last block
|
|
989
1012
|
* finishes re-execution.
|
|
990
1013
|
*/
|
|
991
|
-
private async setProposedCheckpointFromBlocks(proposal: CheckpointProposalCore): Promise<
|
|
1014
|
+
private async setProposedCheckpointFromBlocks(proposal: CheckpointProposalCore): Promise<boolean> {
|
|
992
1015
|
if (!this.archiver) {
|
|
993
|
-
return;
|
|
1016
|
+
return false;
|
|
994
1017
|
}
|
|
995
1018
|
let blockData = await this.blockSource.getBlockDataByArchive(proposal.archive);
|
|
996
1019
|
|
|
@@ -1010,7 +1033,7 @@ export class ProposalHandler {
|
|
|
1010
1033
|
}
|
|
1011
1034
|
|
|
1012
1035
|
if (blockData) {
|
|
1013
|
-
await this.archiver.
|
|
1036
|
+
await this.archiver.addProposedCheckpoint({
|
|
1014
1037
|
header: proposal.checkpointHeader,
|
|
1015
1038
|
checkpointNumber: blockData.checkpointNumber,
|
|
1016
1039
|
startBlock: BlockNumber(blockData.header.getBlockNumber() - blockData.indexWithinCheckpoint),
|
|
@@ -1018,10 +1041,12 @@ export class ProposalHandler {
|
|
|
1018
1041
|
totalManaUsed: proposal.checkpointHeader.totalManaUsed.toBigInt(),
|
|
1019
1042
|
feeAssetPriceModifier: proposal.feeAssetPriceModifier,
|
|
1020
1043
|
});
|
|
1044
|
+
return true;
|
|
1021
1045
|
} else {
|
|
1022
1046
|
this.log.debug(`Block data not found for own checkpoint proposal archive, cannot set proposed checkpoint`, {
|
|
1023
1047
|
archive: proposal.archive.toString(),
|
|
1024
1048
|
});
|
|
1049
|
+
return false;
|
|
1025
1050
|
}
|
|
1026
1051
|
}
|
|
1027
1052
|
}
|
package/src/validator.ts
CHANGED
|
@@ -1,13 +1,7 @@
|
|
|
1
1
|
import type { BlobClientInterface } from '@aztec/blob-client/client';
|
|
2
2
|
import { type Blob, getBlobsPerL1Block } from '@aztec/blob-lib';
|
|
3
3
|
import type { EpochCache } from '@aztec/epoch-cache';
|
|
4
|
-
import {
|
|
5
|
-
BlockNumber,
|
|
6
|
-
CheckpointNumber,
|
|
7
|
-
EpochNumber,
|
|
8
|
-
IndexWithinCheckpoint,
|
|
9
|
-
SlotNumber,
|
|
10
|
-
} from '@aztec/foundation/branded-types';
|
|
4
|
+
import { CheckpointNumber, EpochNumber, IndexWithinCheckpoint, SlotNumber } from '@aztec/foundation/branded-types';
|
|
11
5
|
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
12
6
|
import type { EthAddress } from '@aztec/foundation/eth-address';
|
|
13
7
|
import type { Signature } from '@aztec/foundation/eth-signature';
|
|
@@ -23,7 +17,6 @@ import type { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
|
23
17
|
import type { CommitteeAttestationsAndSigners, L2BlockSink, L2BlockSource } from '@aztec/stdlib/block';
|
|
24
18
|
import { getEpochAtSlot } from '@aztec/stdlib/epoch-helpers';
|
|
25
19
|
import type {
|
|
26
|
-
CreateCheckpointProposalLastBlockData,
|
|
27
20
|
ITxProvider,
|
|
28
21
|
Validator,
|
|
29
22
|
ValidatorClientFullConfig,
|
|
@@ -37,6 +30,7 @@ import {
|
|
|
37
30
|
CheckpointProposal,
|
|
38
31
|
type CheckpointProposalCore,
|
|
39
32
|
type CheckpointProposalOptions,
|
|
33
|
+
type CoordinationSignatureContext,
|
|
40
34
|
} from '@aztec/stdlib/p2p';
|
|
41
35
|
import type { CheckpointHeader } from '@aztec/stdlib/rollup';
|
|
42
36
|
import type { BlockHeader, Tx } from '@aztec/stdlib/tx';
|
|
@@ -122,7 +116,11 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
122
116
|
this.tracer = telemetry.getTracer('Validator');
|
|
123
117
|
this.metrics = new ValidatorMetrics(telemetry);
|
|
124
118
|
|
|
125
|
-
this.validationService = new ValidationService(
|
|
119
|
+
this.validationService = new ValidationService(
|
|
120
|
+
keyStore,
|
|
121
|
+
this.getSignatureContext(),
|
|
122
|
+
this.log.createChild('validation-service'),
|
|
123
|
+
);
|
|
126
124
|
|
|
127
125
|
// Refresh epoch cache every second to trigger alert if participation in committee changes
|
|
128
126
|
this.epochCacheUpdateLoop = new RunningPromise(this.handleEpochCommitteeUpdate.bind(this), this.log, 1000);
|
|
@@ -203,6 +201,11 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
203
201
|
const blockProposalValidator = new BlockProposalValidator(epochCache, {
|
|
204
202
|
txsPermitted: !config.disableTransactions,
|
|
205
203
|
maxTxsPerBlock: config.validateMaxTxsPerBlock,
|
|
204
|
+
maxBlocksPerCheckpoint: config.maxBlocksPerCheckpoint,
|
|
205
|
+
signatureContext: {
|
|
206
|
+
chainId: config.l1ChainId,
|
|
207
|
+
rollupAddress: config.l1Contracts.rollupAddress,
|
|
208
|
+
},
|
|
206
209
|
});
|
|
207
210
|
const proposalHandler = new ProposalHandler(
|
|
208
211
|
checkpointsBuilder,
|
|
@@ -217,6 +220,7 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
217
220
|
metrics,
|
|
218
221
|
dateProvider,
|
|
219
222
|
telemetry,
|
|
223
|
+
undefined,
|
|
220
224
|
);
|
|
221
225
|
|
|
222
226
|
const nodeKeystoreAdapter = NodeKeystoreAdapter.fromKeyStoreManager(keyStoreManager);
|
|
@@ -281,6 +285,13 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
281
285
|
return this.keyStore.signTypedDataWithAddress(addr, msg, context);
|
|
282
286
|
}
|
|
283
287
|
|
|
288
|
+
private getSignatureContext(): CoordinationSignatureContext {
|
|
289
|
+
return {
|
|
290
|
+
chainId: this.config.l1ChainId,
|
|
291
|
+
rollupAddress: this.config.l1Contracts.rollupAddress,
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
|
|
284
295
|
public getCoinbaseForAttestor(attestor: EthAddress): EthAddress {
|
|
285
296
|
return this.keyStore.getCoinbaseAddress(attestor);
|
|
286
297
|
}
|
|
@@ -300,7 +311,11 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
300
311
|
public reloadKeystore(newManager: KeystoreManager): void {
|
|
301
312
|
const newAdapter = NodeKeystoreAdapter.fromKeyStoreManager(newManager);
|
|
302
313
|
this.keyStore = new HAKeyStore(newAdapter, this.slashingProtectionSigner);
|
|
303
|
-
this.validationService = new ValidationService(
|
|
314
|
+
this.validationService = new ValidationService(
|
|
315
|
+
this.keyStore,
|
|
316
|
+
this.getSignatureContext(),
|
|
317
|
+
this.log.createChild('validation-service'),
|
|
318
|
+
);
|
|
304
319
|
}
|
|
305
320
|
|
|
306
321
|
public async start() {
|
|
@@ -514,14 +529,17 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
514
529
|
|
|
515
530
|
// Validate the checkpoint proposal before attesting (unless skipCheckpointProposalValidation is set).
|
|
516
531
|
// Uses the cached result from the all-nodes callback if available (avoids double validation).
|
|
532
|
+
let checkpointNumber: CheckpointNumber;
|
|
517
533
|
if (this.config.skipCheckpointProposalValidation) {
|
|
518
534
|
this.log.warn(`Skipping checkpoint proposal validation for slot ${proposalSlotNumber}`, proposalInfo);
|
|
535
|
+
checkpointNumber = CheckpointNumber(0);
|
|
519
536
|
} else {
|
|
520
537
|
const validationResult = await this.proposalHandler.handleCheckpointProposal(proposal, proposalInfo);
|
|
521
538
|
if (!validationResult.isValid) {
|
|
522
539
|
this.log.warn(`Checkpoint proposal validation failed: ${validationResult.reason}`, proposalInfo);
|
|
523
540
|
return undefined;
|
|
524
541
|
}
|
|
542
|
+
checkpointNumber = validationResult.checkpointNumber;
|
|
525
543
|
}
|
|
526
544
|
|
|
527
545
|
// Check that I have any address in current committee before attesting
|
|
@@ -579,7 +597,7 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
579
597
|
return undefined;
|
|
580
598
|
}
|
|
581
599
|
|
|
582
|
-
return await this.createCheckpointAttestationsFromProposal(proposal, attestors);
|
|
600
|
+
return await this.createCheckpointAttestationsFromProposal(proposal, attestors, checkpointNumber);
|
|
583
601
|
}
|
|
584
602
|
|
|
585
603
|
/**
|
|
@@ -606,13 +624,14 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
606
624
|
private async createCheckpointAttestationsFromProposal(
|
|
607
625
|
proposal: CheckpointProposalCore,
|
|
608
626
|
attestors: EthAddress[] = [],
|
|
627
|
+
checkpointNumber: CheckpointNumber,
|
|
609
628
|
): Promise<CheckpointAttestation[] | undefined> {
|
|
610
629
|
// Equivocation check: must happen right before signing to minimize the race window
|
|
611
630
|
if (!this.shouldAttestToSlot(proposal.slotNumber)) {
|
|
612
631
|
return undefined;
|
|
613
632
|
}
|
|
614
633
|
|
|
615
|
-
const attestations = await this.validationService.attestToCheckpointProposal(proposal, attestors);
|
|
634
|
+
const attestations = await this.validationService.attestToCheckpointProposal(proposal, attestors, checkpointNumber);
|
|
616
635
|
|
|
617
636
|
// Track the proposal we attested to (to prevent equivocation)
|
|
618
637
|
this.lastAttestedProposal = proposal;
|
|
@@ -725,6 +744,7 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
725
744
|
|
|
726
745
|
async createBlockProposal(
|
|
727
746
|
blockHeader: BlockHeader,
|
|
747
|
+
checkpointNumber: CheckpointNumber,
|
|
728
748
|
indexWithinCheckpoint: IndexWithinCheckpoint,
|
|
729
749
|
inHash: Fr,
|
|
730
750
|
archive: Fr,
|
|
@@ -751,6 +771,7 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
751
771
|
);
|
|
752
772
|
const newProposal = await this.validationService.createBlockProposal(
|
|
753
773
|
blockHeader,
|
|
774
|
+
checkpointNumber,
|
|
754
775
|
indexWithinCheckpoint,
|
|
755
776
|
inHash,
|
|
756
777
|
archive,
|
|
@@ -768,8 +789,9 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
768
789
|
async createCheckpointProposal(
|
|
769
790
|
checkpointHeader: CheckpointHeader,
|
|
770
791
|
archive: Fr,
|
|
792
|
+
checkpointNumber: CheckpointNumber,
|
|
771
793
|
feeAssetPriceModifier: bigint,
|
|
772
|
-
|
|
794
|
+
lastBlockProposal: BlockProposal | undefined,
|
|
773
795
|
proposerAddress: EthAddress | undefined,
|
|
774
796
|
options: CheckpointProposalOptions = {},
|
|
775
797
|
): Promise<CheckpointProposal> {
|
|
@@ -790,8 +812,9 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
790
812
|
const newProposal = await this.validationService.createCheckpointProposal(
|
|
791
813
|
checkpointHeader,
|
|
792
814
|
archive,
|
|
815
|
+
checkpointNumber,
|
|
793
816
|
feeAssetPriceModifier,
|
|
794
|
-
|
|
817
|
+
lastBlockProposal,
|
|
795
818
|
proposerAddress,
|
|
796
819
|
options,
|
|
797
820
|
);
|
|
@@ -807,16 +830,24 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
807
830
|
attestationsAndSigners: CommitteeAttestationsAndSigners,
|
|
808
831
|
proposer: EthAddress,
|
|
809
832
|
slot: SlotNumber,
|
|
810
|
-
|
|
833
|
+
checkpointNumber: CheckpointNumber,
|
|
811
834
|
): Promise<Signature> {
|
|
812
|
-
return await this.validationService.signAttestationsAndSigners(
|
|
835
|
+
return await this.validationService.signAttestationsAndSigners(
|
|
836
|
+
attestationsAndSigners,
|
|
837
|
+
proposer,
|
|
838
|
+
slot,
|
|
839
|
+
checkpointNumber,
|
|
840
|
+
);
|
|
813
841
|
}
|
|
814
842
|
|
|
815
|
-
async collectOwnAttestations(
|
|
843
|
+
async collectOwnAttestations(
|
|
844
|
+
proposal: CheckpointProposal,
|
|
845
|
+
checkpointNumber: CheckpointNumber,
|
|
846
|
+
): Promise<CheckpointAttestation[]> {
|
|
816
847
|
const slot = proposal.slotNumber;
|
|
817
848
|
const inCommittee = await this.epochCache.filterInCommittee(slot, this.getValidatorAddresses());
|
|
818
849
|
this.log.debug(`Collecting ${inCommittee.length} self-attestations for slot ${slot}`, { inCommittee });
|
|
819
|
-
const attestations = await this.createCheckpointAttestationsFromProposal(proposal, inCommittee);
|
|
850
|
+
const attestations = await this.createCheckpointAttestationsFromProposal(proposal, inCommittee, checkpointNumber);
|
|
820
851
|
|
|
821
852
|
if (!attestations) {
|
|
822
853
|
return [];
|
|
@@ -835,6 +866,7 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
835
866
|
proposal: CheckpointProposal,
|
|
836
867
|
required: number,
|
|
837
868
|
deadline: Date,
|
|
869
|
+
checkpointNumber: CheckpointNumber,
|
|
838
870
|
): Promise<CheckpointAttestation[]> {
|
|
839
871
|
// Wait and poll the p2pClient's attestation pool for this checkpoint until we have enough attestations
|
|
840
872
|
const slot = proposal.slotNumber;
|
|
@@ -847,7 +879,7 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
847
879
|
throw new AttestationTimeoutError(0, required, slot);
|
|
848
880
|
}
|
|
849
881
|
|
|
850
|
-
await this.collectOwnAttestations(proposal);
|
|
882
|
+
await this.collectOwnAttestations(proposal, checkpointNumber);
|
|
851
883
|
|
|
852
884
|
const proposalId = proposal.archive.toString();
|
|
853
885
|
const myAddresses = this.getValidatorAddresses();
|
|
@@ -860,7 +892,9 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
860
892
|
attestation => {
|
|
861
893
|
if (!attestation.archive.equals(proposal.archive)) {
|
|
862
894
|
this.log.warn(
|
|
863
|
-
`Received attestation for slot ${slot} with mismatched archive from ${attestation
|
|
895
|
+
`Received attestation for slot ${slot} with mismatched archive from ${attestation
|
|
896
|
+
.getSender()
|
|
897
|
+
?.toString()}`,
|
|
864
898
|
{ attestationArchive: attestation.archive.toString(), proposalArchive: proposal.archive.toString() },
|
|
865
899
|
);
|
|
866
900
|
return false;
|