@aztec/validator-client 0.0.1-commit.181e2d196 → 0.0.1-commit.1a421b1a1
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 +42 -0
- package/dest/block_proposal_handler.d.ts +4 -3
- package/dest/block_proposal_handler.d.ts.map +1 -1
- package/dest/block_proposal_handler.js +104 -30
- package/dest/checkpoint_builder.d.ts +8 -1
- package/dest/checkpoint_builder.d.ts.map +1 -1
- package/dest/checkpoint_builder.js +52 -7
- package/dest/config.d.ts +1 -1
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +22 -1
- package/dest/duties/validation_service.d.ts +1 -1
- package/dest/duties/validation_service.d.ts.map +1 -1
- package/dest/duties/validation_service.js +2 -8
- package/dest/factory.js +1 -1
- package/dest/key_store/ha_key_store.js +1 -1
- package/dest/validator.d.ts +3 -3
- package/dest/validator.d.ts.map +1 -1
- package/dest/validator.js +40 -28
- package/package.json +19 -19
- package/src/block_proposal_handler.ts +126 -37
- package/src/checkpoint_builder.ts +67 -8
- package/src/config.ts +22 -1
- package/src/duties/validation_service.ts +2 -8
- package/src/factory.ts +1 -1
- package/src/key_store/ha_key_store.ts +1 -1
- package/src/validator.ts +39 -32
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import { NUM_CHECKPOINT_END_MARKER_FIELDS, getNumBlockEndBlobFields } from '@aztec/blob-lib/encoding';
|
|
2
|
+
import { BLOBS_PER_CHECKPOINT, FIELDS_PER_BLOB, MAX_PROCESSABLE_DA_GAS_PER_CHECKPOINT } from '@aztec/constants';
|
|
1
3
|
import { BlockNumber, CheckpointNumber } from '@aztec/foundation/branded-types';
|
|
2
|
-
import { merge, pick } from '@aztec/foundation/collection';
|
|
4
|
+
import { merge, pick, sum } from '@aztec/foundation/collection';
|
|
3
5
|
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
4
6
|
import { type Logger, type LoggerBindings, createLogger } from '@aztec/foundation/log';
|
|
5
7
|
import { bufferToHex } from '@aztec/foundation/string';
|
|
@@ -65,6 +67,7 @@ export class CheckpointBuilder implements ICheckpointBlockBuilder {
|
|
|
65
67
|
|
|
66
68
|
/**
|
|
67
69
|
* Builds a single block within this checkpoint.
|
|
70
|
+
* Automatically caps gas and blob field limits based on checkpoint-level budgets and prior blocks.
|
|
68
71
|
*/
|
|
69
72
|
async buildBlock(
|
|
70
73
|
pendingTxs: Iterable<Tx> | AsyncIterable<Tx>,
|
|
@@ -94,8 +97,14 @@ export class CheckpointBuilder implements ICheckpointBlockBuilder {
|
|
|
94
97
|
});
|
|
95
98
|
const { processor, validator } = await this.makeBlockBuilderDeps(globalVariables, this.fork);
|
|
96
99
|
|
|
97
|
-
|
|
98
|
-
|
|
100
|
+
// Cap gas limits amd available blob fields by remaining checkpoint-level budgets
|
|
101
|
+
const cappedOpts: PublicProcessorLimits & { expectedEndState?: StateReference } = {
|
|
102
|
+
...opts,
|
|
103
|
+
...this.capLimitsByCheckpointBudgets(opts),
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
const [publicProcessorDuration, [processedTxs, failedTxs, usedTxs]] = await elapsed(() =>
|
|
107
|
+
processor.process(pendingTxs, cappedOpts, validator),
|
|
99
108
|
);
|
|
100
109
|
|
|
101
110
|
// Throw if we didn't collect a single valid tx and we're not allowed to build empty blocks
|
|
@@ -109,9 +118,6 @@ export class CheckpointBuilder implements ICheckpointBlockBuilder {
|
|
|
109
118
|
expectedEndState: opts.expectedEndState,
|
|
110
119
|
});
|
|
111
120
|
|
|
112
|
-
// How much public gas was processed
|
|
113
|
-
const publicGas = processedTxs.reduce((acc, tx) => acc.add(tx.gasUsed.publicGas), Gas.empty());
|
|
114
|
-
|
|
115
121
|
this.log.debug('Built block within checkpoint', {
|
|
116
122
|
header: block.header.toInspect(),
|
|
117
123
|
processedTxs: processedTxs.map(tx => tx.hash.toString()),
|
|
@@ -120,12 +126,10 @@ export class CheckpointBuilder implements ICheckpointBlockBuilder {
|
|
|
120
126
|
|
|
121
127
|
return {
|
|
122
128
|
block,
|
|
123
|
-
publicGas,
|
|
124
129
|
publicProcessorDuration,
|
|
125
130
|
numTxs: processedTxs.length,
|
|
126
131
|
failedTxs,
|
|
127
132
|
usedTxs,
|
|
128
|
-
usedTxBlobFields,
|
|
129
133
|
};
|
|
130
134
|
}
|
|
131
135
|
|
|
@@ -147,6 +151,61 @@ export class CheckpointBuilder implements ICheckpointBlockBuilder {
|
|
|
147
151
|
return this.checkpointBuilder.clone().completeCheckpoint();
|
|
148
152
|
}
|
|
149
153
|
|
|
154
|
+
/**
|
|
155
|
+
* Caps per-block gas and blob field limits by remaining checkpoint-level budgets.
|
|
156
|
+
* Computes remaining L2 gas (mana), DA gas, and blob fields from blocks already added to the checkpoint,
|
|
157
|
+
* then returns opts with maxBlockGas and maxBlobFields capped accordingly.
|
|
158
|
+
*/
|
|
159
|
+
protected capLimitsByCheckpointBudgets(
|
|
160
|
+
opts: PublicProcessorLimits,
|
|
161
|
+
): Pick<PublicProcessorLimits, 'maxBlockGas' | 'maxBlobFields' | 'maxTransactions'> {
|
|
162
|
+
const existingBlocks = this.checkpointBuilder.getBlocks();
|
|
163
|
+
|
|
164
|
+
// Remaining L2 gas (mana)
|
|
165
|
+
// IMPORTANT: This assumes mana is computed solely based on L2 gas used in transactions.
|
|
166
|
+
// This may change in the future.
|
|
167
|
+
const usedMana = sum(existingBlocks.map(b => b.header.totalManaUsed.toNumber()));
|
|
168
|
+
const remainingMana = this.config.rollupManaLimit - usedMana;
|
|
169
|
+
|
|
170
|
+
// Remaining DA gas
|
|
171
|
+
const usedDAGas = sum(existingBlocks.map(b => b.computeDAGasUsed())) ?? 0;
|
|
172
|
+
const remainingDAGas = MAX_PROCESSABLE_DA_GAS_PER_CHECKPOINT - usedDAGas;
|
|
173
|
+
|
|
174
|
+
// Remaining blob fields (block blob fields include both tx data and block-end overhead)
|
|
175
|
+
const usedBlobFields = sum(existingBlocks.map(b => b.toBlobFields().length));
|
|
176
|
+
const totalBlobCapacity = BLOBS_PER_CHECKPOINT * FIELDS_PER_BLOB - NUM_CHECKPOINT_END_MARKER_FIELDS;
|
|
177
|
+
const isFirstBlock = existingBlocks.length === 0;
|
|
178
|
+
const blockEndOverhead = getNumBlockEndBlobFields(isFirstBlock);
|
|
179
|
+
const maxBlobFieldsForTxs = totalBlobCapacity - usedBlobFields - blockEndOverhead;
|
|
180
|
+
|
|
181
|
+
// Cap L2 gas by remaining checkpoint mana
|
|
182
|
+
const cappedL2Gas = Math.min(opts.maxBlockGas?.l2Gas ?? remainingMana, remainingMana);
|
|
183
|
+
|
|
184
|
+
// Cap DA gas by remaining checkpoint DA gas budget
|
|
185
|
+
const cappedDAGas = Math.min(opts.maxBlockGas?.daGas ?? remainingDAGas, remainingDAGas);
|
|
186
|
+
|
|
187
|
+
// Cap blob fields by remaining checkpoint blob capacity
|
|
188
|
+
const cappedBlobFields =
|
|
189
|
+
opts.maxBlobFields !== undefined ? Math.min(opts.maxBlobFields, maxBlobFieldsForTxs) : maxBlobFieldsForTxs;
|
|
190
|
+
|
|
191
|
+
// Cap transaction count by remaining checkpoint tx budget
|
|
192
|
+
let cappedMaxTransactions: number | undefined;
|
|
193
|
+
if (this.config.maxTxsPerCheckpoint !== undefined) {
|
|
194
|
+
const usedTxs = sum(existingBlocks.map(b => b.body.txEffects.length));
|
|
195
|
+
const remainingTxs = Math.max(0, this.config.maxTxsPerCheckpoint - usedTxs);
|
|
196
|
+
cappedMaxTransactions =
|
|
197
|
+
opts.maxTransactions !== undefined ? Math.min(opts.maxTransactions, remainingTxs) : remainingTxs;
|
|
198
|
+
} else {
|
|
199
|
+
cappedMaxTransactions = opts.maxTransactions;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
return {
|
|
203
|
+
maxBlockGas: new Gas(cappedDAGas, cappedL2Gas),
|
|
204
|
+
maxBlobFields: cappedBlobFields,
|
|
205
|
+
maxTransactions: cappedMaxTransactions,
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
|
|
150
209
|
protected async makeBlockBuilderDeps(globalVariables: GlobalVariables, fork: MerkleTreeWriteOperations) {
|
|
151
210
|
const txPublicSetupAllowList = [
|
|
152
211
|
...(await getDefaultAllowedSetupFunctions()),
|
package/src/config.ts
CHANGED
|
@@ -6,7 +6,7 @@ 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
|
+
import { localSignerConfigMappings, validatorHASignerConfigMappings } from '@aztec/stdlib/ha-signing';
|
|
10
10
|
import type { ValidatorClientConfig } from '@aztec/stdlib/interfaces/server';
|
|
11
11
|
|
|
12
12
|
export type { ValidatorClientConfig };
|
|
@@ -77,6 +77,27 @@ export const validatorClientConfigMappings: ConfigMappingsType<ValidatorClientCo
|
|
|
77
77
|
description: 'Agree to attest to equivocated checkpoint proposals (for testing purposes only)',
|
|
78
78
|
...booleanConfigHelper(false),
|
|
79
79
|
},
|
|
80
|
+
validateMaxL2BlockGas: {
|
|
81
|
+
env: 'VALIDATOR_MAX_L2_BLOCK_GAS',
|
|
82
|
+
description: 'Maximum L2 block gas for validation. Proposals exceeding this limit are rejected.',
|
|
83
|
+
parseEnv: (val: string) => (val ? parseInt(val, 10) : undefined),
|
|
84
|
+
},
|
|
85
|
+
validateMaxDABlockGas: {
|
|
86
|
+
env: 'VALIDATOR_MAX_DA_BLOCK_GAS',
|
|
87
|
+
description: 'Maximum DA block gas for validation. Proposals exceeding this limit are rejected.',
|
|
88
|
+
parseEnv: (val: string) => (val ? parseInt(val, 10) : undefined),
|
|
89
|
+
},
|
|
90
|
+
validateMaxTxsPerBlock: {
|
|
91
|
+
env: 'VALIDATOR_MAX_TX_PER_BLOCK',
|
|
92
|
+
description: 'Maximum transactions per block for validation. Proposals exceeding this limit are rejected.',
|
|
93
|
+
parseEnv: (val: string) => (val ? parseInt(val, 10) : undefined),
|
|
94
|
+
},
|
|
95
|
+
validateMaxTxsPerCheckpoint: {
|
|
96
|
+
env: 'VALIDATOR_MAX_TX_PER_CHECKPOINT',
|
|
97
|
+
description: 'Maximum transactions per checkpoint for validation. Proposals exceeding this limit are rejected.',
|
|
98
|
+
parseEnv: (val: string) => (val ? parseInt(val, 10) : undefined),
|
|
99
|
+
},
|
|
100
|
+
...localSignerConfigMappings,
|
|
80
101
|
...validatorHASignerConfigMappings,
|
|
81
102
|
};
|
|
82
103
|
|
|
@@ -150,16 +150,10 @@ export class ValidationService {
|
|
|
150
150
|
);
|
|
151
151
|
|
|
152
152
|
// TODO(spy/ha): Use checkpointNumber instead of blockNumber once CheckpointHeader includes it.
|
|
153
|
-
//
|
|
153
|
+
// CheckpointProposalCore doesn't have lastBlock info, so use 0 as a proxy.
|
|
154
154
|
// blockNumber is NOT used for the primary key so it's safe to use here.
|
|
155
155
|
// See CheckpointHeader TODO and SigningContext types documentation.
|
|
156
|
-
|
|
157
|
-
try {
|
|
158
|
-
blockNumber = proposal.blockNumber;
|
|
159
|
-
} catch {
|
|
160
|
-
// Checkpoint proposal may not have lastBlock, use 0 as fallback
|
|
161
|
-
blockNumber = BlockNumber(0);
|
|
162
|
-
}
|
|
156
|
+
const blockNumber = BlockNumber(0);
|
|
163
157
|
const context: SigningContext = {
|
|
164
158
|
slot: proposal.slotNumber,
|
|
165
159
|
blockNumber,
|
package/src/factory.ts
CHANGED
|
@@ -29,7 +29,7 @@ export function createBlockProposalHandler(
|
|
|
29
29
|
const metrics = new ValidatorMetrics(deps.telemetry);
|
|
30
30
|
const blockProposalValidator = new BlockProposalValidator(deps.epochCache, {
|
|
31
31
|
txsPermitted: !config.disableTransactions,
|
|
32
|
-
maxTxsPerBlock: config.
|
|
32
|
+
maxTxsPerBlock: config.validateMaxTxsPerBlock,
|
|
33
33
|
});
|
|
34
34
|
return new BlockProposalHandler(
|
|
35
35
|
deps.checkpointsBuilder,
|
|
@@ -240,7 +240,7 @@ export class HAKeyStore implements ExtendedValidatorKeyStore {
|
|
|
240
240
|
}
|
|
241
241
|
|
|
242
242
|
if (error instanceof SlashingProtectionError) {
|
|
243
|
-
this.log.
|
|
243
|
+
this.log.info(`Duty already signed by another node with different payload`, {
|
|
244
244
|
dutyType: context.dutyType,
|
|
245
245
|
slot: context.slot,
|
|
246
246
|
existingMessageHash: error.existingMessageHash,
|
package/src/validator.ts
CHANGED
|
@@ -24,6 +24,7 @@ import { AuthRequest, AuthResponse, BlockProposalValidator, ReqRespSubProtocol }
|
|
|
24
24
|
import { OffenseType, WANT_TO_SLASH_EVENT, type Watcher, type WatcherEmitter } from '@aztec/slasher';
|
|
25
25
|
import type { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
26
26
|
import type { CommitteeAttestationsAndSigners, L2Block, L2BlockSink, L2BlockSource } from '@aztec/stdlib/block';
|
|
27
|
+
import { validateCheckpoint } from '@aztec/stdlib/checkpoint';
|
|
27
28
|
import { getEpochAtSlot, getTimestampForSlot } from '@aztec/stdlib/epoch-helpers';
|
|
28
29
|
import type {
|
|
29
30
|
CreateCheckpointProposalLastBlockData,
|
|
@@ -45,7 +46,7 @@ import type { CheckpointHeader } from '@aztec/stdlib/rollup';
|
|
|
45
46
|
import type { BlockHeader, CheckpointGlobalVariables, Tx } from '@aztec/stdlib/tx';
|
|
46
47
|
import { AttestationTimeoutError } from '@aztec/stdlib/validators';
|
|
47
48
|
import { type TelemetryClient, type Tracer, getTelemetryClient } from '@aztec/telemetry-client';
|
|
48
|
-
import { createHASigner } from '@aztec/validator-ha-signer/factory';
|
|
49
|
+
import { createHASigner, createLocalSignerWithProtection } from '@aztec/validator-ha-signer/factory';
|
|
49
50
|
import { DutyType, type SigningContext } from '@aztec/validator-ha-signer/types';
|
|
50
51
|
import type { ValidatorHASigner } from '@aztec/validator-ha-signer/validator-ha-signer';
|
|
51
52
|
|
|
@@ -108,7 +109,7 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
108
109
|
private l1ToL2MessageSource: L1ToL2MessageSource,
|
|
109
110
|
private config: ValidatorClientFullConfig,
|
|
110
111
|
private blobClient: BlobClientInterface,
|
|
111
|
-
private
|
|
112
|
+
private slashingProtectionSigner: ValidatorHASigner,
|
|
112
113
|
private dateProvider: DateProvider = new DateProvider(),
|
|
113
114
|
telemetry: TelemetryClient = getTelemetryClient(),
|
|
114
115
|
log = createLogger('validator'),
|
|
@@ -200,7 +201,7 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
200
201
|
const metrics = new ValidatorMetrics(telemetry);
|
|
201
202
|
const blockProposalValidator = new BlockProposalValidator(epochCache, {
|
|
202
203
|
txsPermitted: !config.disableTransactions,
|
|
203
|
-
maxTxsPerBlock: config.
|
|
204
|
+
maxTxsPerBlock: config.validateMaxTxsPerBlock,
|
|
204
205
|
});
|
|
205
206
|
const blockProposalHandler = new BlockProposalHandler(
|
|
206
207
|
checkpointsBuilder,
|
|
@@ -217,18 +218,27 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
217
218
|
);
|
|
218
219
|
|
|
219
220
|
const nodeKeystoreAdapter = NodeKeystoreAdapter.fromKeyStoreManager(keyStoreManager);
|
|
220
|
-
let
|
|
221
|
-
let haSigner: ValidatorHASigner | undefined;
|
|
221
|
+
let slashingProtectionSigner: ValidatorHASigner;
|
|
222
222
|
if (config.haSigningEnabled) {
|
|
223
|
+
// Multi-node HA mode: use PostgreSQL-backed distributed locking.
|
|
223
224
|
// If maxStuckDutiesAgeMs is not explicitly set, compute it from Aztec slot duration
|
|
224
225
|
const haConfig = {
|
|
225
226
|
...config,
|
|
226
227
|
maxStuckDutiesAgeMs: config.maxStuckDutiesAgeMs ?? epochCache.getL1Constants().slotDuration * 2 * 1000,
|
|
227
228
|
};
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
229
|
+
({ signer: slashingProtectionSigner } = await createHASigner(haConfig, {
|
|
230
|
+
telemetryClient: telemetry,
|
|
231
|
+
dateProvider,
|
|
232
|
+
}));
|
|
233
|
+
} else {
|
|
234
|
+
// Single-node mode: use LMDB-backed local signing protection.
|
|
235
|
+
// This prevents double-signing if the node crashes and restarts mid-proposal.
|
|
236
|
+
({ signer: slashingProtectionSigner } = await createLocalSignerWithProtection(config, {
|
|
237
|
+
telemetryClient: telemetry,
|
|
238
|
+
dateProvider,
|
|
239
|
+
}));
|
|
231
240
|
}
|
|
241
|
+
const validatorKeyStore: ExtendedValidatorKeyStore = new HAKeyStore(nodeKeystoreAdapter, slashingProtectionSigner);
|
|
232
242
|
|
|
233
243
|
const validator = new ValidatorClient(
|
|
234
244
|
validatorKeyStore,
|
|
@@ -241,7 +251,7 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
241
251
|
l1ToL2MessageSource,
|
|
242
252
|
config,
|
|
243
253
|
blobClient,
|
|
244
|
-
|
|
254
|
+
slashingProtectionSigner,
|
|
245
255
|
dateProvider,
|
|
246
256
|
telemetry,
|
|
247
257
|
);
|
|
@@ -280,24 +290,8 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
280
290
|
}
|
|
281
291
|
|
|
282
292
|
public reloadKeystore(newManager: KeystoreManager): void {
|
|
283
|
-
if (this.config.haSigningEnabled && !this.haSigner) {
|
|
284
|
-
this.log.warn(
|
|
285
|
-
'HA signing is enabled in config but was not initialized at startup. ' +
|
|
286
|
-
'Restart the node to enable HA signing.',
|
|
287
|
-
);
|
|
288
|
-
} else if (!this.config.haSigningEnabled && this.haSigner) {
|
|
289
|
-
this.log.warn(
|
|
290
|
-
'HA signing was disabled via config update but the HA signer is still active. ' +
|
|
291
|
-
'Restart the node to fully disable HA signing.',
|
|
292
|
-
);
|
|
293
|
-
}
|
|
294
|
-
|
|
295
293
|
const newAdapter = NodeKeystoreAdapter.fromKeyStoreManager(newManager);
|
|
296
|
-
|
|
297
|
-
this.keyStore = new HAKeyStore(newAdapter, this.haSigner);
|
|
298
|
-
} else {
|
|
299
|
-
this.keyStore = newAdapter;
|
|
300
|
-
}
|
|
294
|
+
this.keyStore = new HAKeyStore(newAdapter, this.slashingProtectionSigner);
|
|
301
295
|
this.validationService = new ValidationService(this.keyStore, this.log.createChild('validation-service'));
|
|
302
296
|
}
|
|
303
297
|
|
|
@@ -386,7 +380,7 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
386
380
|
|
|
387
381
|
// Ignore proposals from ourselves (may happen in HA setups)
|
|
388
382
|
if (this.getValidatorAddresses().some(addr => addr.equals(proposer))) {
|
|
389
|
-
this.log.
|
|
383
|
+
this.log.debug(`Ignoring block proposal from self for slot ${slotNumber}`, {
|
|
390
384
|
proposer: proposer.toString(),
|
|
391
385
|
slotNumber,
|
|
392
386
|
});
|
|
@@ -422,9 +416,10 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
422
416
|
);
|
|
423
417
|
|
|
424
418
|
if (!validationResult.isValid) {
|
|
425
|
-
this.log.warn(`Block proposal validation failed: ${validationResult.reason}`, proposalInfo);
|
|
426
|
-
|
|
427
419
|
const reason = validationResult.reason || 'unknown';
|
|
420
|
+
|
|
421
|
+
this.log.warn(`Block proposal validation failed: ${reason}`, proposalInfo);
|
|
422
|
+
|
|
428
423
|
// Classify failure reason: bad proposal vs node issue
|
|
429
424
|
const badProposalReasons: BlockProposalValidationFailureReason[] = [
|
|
430
425
|
'invalid_proposal',
|
|
@@ -496,7 +491,7 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
496
491
|
|
|
497
492
|
// Ignore proposals from ourselves (may happen in HA setups)
|
|
498
493
|
if (this.getValidatorAddresses().some(addr => addr.equals(proposer))) {
|
|
499
|
-
this.log.
|
|
494
|
+
this.log.debug(`Ignoring block proposal from self for slot ${slotNumber}`, {
|
|
500
495
|
proposer: proposer.toString(),
|
|
501
496
|
slotNumber,
|
|
502
497
|
});
|
|
@@ -519,11 +514,9 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
519
514
|
slotNumber,
|
|
520
515
|
archive: proposal.archive.toString(),
|
|
521
516
|
proposer: proposer.toString(),
|
|
522
|
-
txCount: proposal.txHashes.length,
|
|
523
517
|
};
|
|
524
518
|
this.log.info(`Received checkpoint proposal for slot ${slotNumber}`, {
|
|
525
519
|
...proposalInfo,
|
|
526
|
-
txHashes: proposal.txHashes.map(t => t.toString()),
|
|
527
520
|
fishermanMode: this.config.fishermanMode || false,
|
|
528
521
|
});
|
|
529
522
|
|
|
@@ -766,6 +759,20 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
|
|
|
766
759
|
return { isValid: false, reason: 'out_hash_mismatch' };
|
|
767
760
|
}
|
|
768
761
|
|
|
762
|
+
// Final round of validations on the checkpoint, just in case.
|
|
763
|
+
try {
|
|
764
|
+
validateCheckpoint(computedCheckpoint, {
|
|
765
|
+
rollupManaLimit: this.checkpointsBuilder.getConfig().rollupManaLimit,
|
|
766
|
+
maxDABlockGas: this.config.validateMaxDABlockGas,
|
|
767
|
+
maxL2BlockGas: this.config.validateMaxL2BlockGas,
|
|
768
|
+
maxTxsPerBlock: this.config.validateMaxTxsPerBlock,
|
|
769
|
+
maxTxsPerCheckpoint: this.config.validateMaxTxsPerCheckpoint,
|
|
770
|
+
});
|
|
771
|
+
} catch (err) {
|
|
772
|
+
this.log.warn(`Checkpoint validation failed: ${err}`, proposalInfo);
|
|
773
|
+
return { isValid: false, reason: 'checkpoint_validation_failed' };
|
|
774
|
+
}
|
|
775
|
+
|
|
769
776
|
this.log.verbose(`Checkpoint proposal validation successful for slot ${slot}`, proposalInfo);
|
|
770
777
|
return { isValid: true };
|
|
771
778
|
} finally {
|