@aztec/sequencer-client 3.0.0-devnet.5 → 3.0.0-devnet.6-patch.1
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/client/index.d.ts +1 -1
- package/dest/client/sequencer-client.d.ts +2 -2
- package/dest/client/sequencer-client.d.ts.map +1 -1
- package/dest/client/sequencer-client.js +6 -2
- package/dest/config.d.ts +3 -2
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +11 -1
- package/dest/global_variable_builder/global_builder.d.ts +5 -7
- package/dest/global_variable_builder/global_builder.d.ts.map +1 -1
- package/dest/global_variable_builder/global_builder.js +12 -8
- package/dest/global_variable_builder/index.d.ts +1 -1
- package/dest/index.d.ts +1 -1
- package/dest/publisher/config.d.ts +7 -2
- package/dest/publisher/config.d.ts.map +1 -1
- package/dest/publisher/config.js +12 -1
- package/dest/publisher/index.d.ts +1 -1
- package/dest/publisher/sequencer-publisher-factory.d.ts +3 -2
- package/dest/publisher/sequencer-publisher-factory.d.ts.map +1 -1
- package/dest/publisher/sequencer-publisher-metrics.d.ts +1 -1
- package/dest/publisher/sequencer-publisher-metrics.d.ts.map +1 -1
- package/dest/publisher/sequencer-publisher.d.ts +40 -31
- package/dest/publisher/sequencer-publisher.d.ts.map +1 -1
- package/dest/publisher/sequencer-publisher.js +117 -62
- package/dest/sequencer/block_builder.d.ts +4 -3
- package/dest/sequencer/block_builder.d.ts.map +1 -1
- package/dest/sequencer/block_builder.js +5 -8
- package/dest/sequencer/config.d.ts +2 -2
- package/dest/sequencer/config.d.ts.map +1 -1
- package/dest/sequencer/errors.d.ts +1 -1
- package/dest/sequencer/errors.d.ts.map +1 -1
- package/dest/sequencer/index.d.ts +1 -1
- package/dest/sequencer/metrics.d.ts +12 -3
- package/dest/sequencer/metrics.d.ts.map +1 -1
- package/dest/sequencer/metrics.js +38 -0
- package/dest/sequencer/sequencer.d.ts +48 -27
- package/dest/sequencer/sequencer.d.ts.map +1 -1
- package/dest/sequencer/sequencer.js +418 -166
- package/dest/sequencer/timetable.d.ts +3 -1
- package/dest/sequencer/timetable.d.ts.map +1 -1
- package/dest/sequencer/utils.d.ts +1 -1
- package/dest/test/index.d.ts +2 -2
- package/dest/test/index.d.ts.map +1 -1
- package/dest/tx_validator/nullifier_cache.d.ts +1 -1
- package/dest/tx_validator/nullifier_cache.d.ts.map +1 -1
- package/dest/tx_validator/tx_validator_factory.d.ts +4 -3
- package/dest/tx_validator/tx_validator_factory.d.ts.map +1 -1
- package/dest/tx_validator/tx_validator_factory.js +1 -1
- package/package.json +31 -30
- package/src/client/sequencer-client.ts +6 -9
- package/src/config.ts +12 -6
- package/src/global_variable_builder/global_builder.ts +19 -17
- package/src/publisher/config.ts +17 -6
- package/src/publisher/sequencer-publisher-factory.ts +4 -2
- package/src/publisher/sequencer-publisher.ts +165 -94
- package/src/sequencer/block_builder.ts +8 -12
- package/src/sequencer/config.ts +1 -1
- package/src/sequencer/metrics.ts +52 -3
- package/src/sequencer/sequencer.ts +480 -198
- package/src/sequencer/timetable.ts +7 -0
- package/src/test/index.ts +1 -1
- package/src/tx_validator/tx_validator_factory.ts +3 -2
|
@@ -2,32 +2,33 @@ import { L2Block } from '@aztec/aztec.js/block';
|
|
|
2
2
|
import { Blob, getBlobsPerL1Block, getPrefixedEthBlobCommitments } from '@aztec/blob-lib';
|
|
3
3
|
import { type BlobSinkClientInterface, createBlobSinkClient } from '@aztec/blob-sink/client';
|
|
4
4
|
import type { EpochCache } from '@aztec/epoch-cache';
|
|
5
|
+
import type { L1ContractsConfig } from '@aztec/ethereum/config';
|
|
5
6
|
import {
|
|
6
7
|
type EmpireSlashingProposerContract,
|
|
7
|
-
FormattedViemError,
|
|
8
8
|
type GovernanceProposerContract,
|
|
9
9
|
type IEmpireBase,
|
|
10
|
-
type L1BlobInputs,
|
|
11
|
-
type L1ContractsConfig,
|
|
12
|
-
type L1TxConfig,
|
|
13
|
-
type L1TxRequest,
|
|
14
10
|
MULTI_CALL_3_ADDRESS,
|
|
15
11
|
Multicall3,
|
|
16
12
|
RollupContract,
|
|
17
13
|
type TallySlashingProposerContract,
|
|
18
|
-
type TransactionStats,
|
|
19
14
|
type ViemCommitteeAttestations,
|
|
20
15
|
type ViemHeader,
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
16
|
+
} from '@aztec/ethereum/contracts';
|
|
17
|
+
import {
|
|
18
|
+
type L1BlobInputs,
|
|
19
|
+
type L1TxConfig,
|
|
20
|
+
type L1TxRequest,
|
|
21
|
+
type TransactionStats,
|
|
22
|
+
WEI_CONST,
|
|
23
|
+
} from '@aztec/ethereum/l1-tx-utils';
|
|
25
24
|
import type { L1TxUtilsWithBlobs } from '@aztec/ethereum/l1-tx-utils-with-blobs';
|
|
25
|
+
import { FormattedViemError, formatViemError, tryExtractEvent } from '@aztec/ethereum/utils';
|
|
26
26
|
import { sumBigint } from '@aztec/foundation/bigint';
|
|
27
27
|
import { toHex as toPaddedHex } from '@aztec/foundation/bigint-buffer';
|
|
28
|
+
import { BlockNumber, CheckpointNumber, SlotNumber } from '@aztec/foundation/branded-types';
|
|
29
|
+
import type { Fr } from '@aztec/foundation/curves/bn254';
|
|
28
30
|
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
29
31
|
import { Signature, type ViemSignature } from '@aztec/foundation/eth-signature';
|
|
30
|
-
import type { Fr } from '@aztec/foundation/fields';
|
|
31
32
|
import { type Logger, createLogger } from '@aztec/foundation/log';
|
|
32
33
|
import { bufferToHex } from '@aztec/foundation/string';
|
|
33
34
|
import { DateProvider, Timer } from '@aztec/foundation/timer';
|
|
@@ -37,10 +38,9 @@ import { CommitteeAttestation, CommitteeAttestationsAndSigners, type ValidateBlo
|
|
|
37
38
|
import { SlashFactoryContract } from '@aztec/stdlib/l1-contracts';
|
|
38
39
|
import type { CheckpointHeader } from '@aztec/stdlib/rollup';
|
|
39
40
|
import type { L1PublishBlockStats } from '@aztec/stdlib/stats';
|
|
40
|
-
import { StateReference } from '@aztec/stdlib/tx';
|
|
41
41
|
import { type TelemetryClient, getTelemetryClient } from '@aztec/telemetry-client';
|
|
42
42
|
|
|
43
|
-
import { type TransactionReceipt, type TypedDataDefinition, encodeFunctionData, toHex } from 'viem';
|
|
43
|
+
import { type StateOverride, type TransactionReceipt, type TypedDataDefinition, encodeFunctionData, toHex } from 'viem';
|
|
44
44
|
|
|
45
45
|
import type { PublisherConfig, TxSenderConfig } from './config.js';
|
|
46
46
|
import { SequencerPublisherMetrics } from './sequencer-publisher-metrics.js';
|
|
@@ -51,8 +51,6 @@ type L1ProcessArgs = {
|
|
|
51
51
|
header: CheckpointHeader;
|
|
52
52
|
/** A root of the archive tree after the L2 block is applied. */
|
|
53
53
|
archive: Buffer;
|
|
54
|
-
/** State reference after the L2 block is applied. */
|
|
55
|
-
stateReference: StateReference;
|
|
56
54
|
/** L2 block blobs containing all tx effects. */
|
|
57
55
|
blobs: Blob[];
|
|
58
56
|
/** Attestations */
|
|
@@ -84,14 +82,14 @@ export type InvalidateBlockRequest = {
|
|
|
84
82
|
request: L1TxRequest;
|
|
85
83
|
reason: 'invalid-attestation' | 'insufficient-attestations';
|
|
86
84
|
gasUsed: bigint;
|
|
87
|
-
blockNumber:
|
|
88
|
-
forcePendingBlockNumber:
|
|
85
|
+
blockNumber: BlockNumber;
|
|
86
|
+
forcePendingBlockNumber: BlockNumber;
|
|
89
87
|
};
|
|
90
88
|
|
|
91
89
|
interface RequestWithExpiry {
|
|
92
90
|
action: Action;
|
|
93
91
|
request: L1TxRequest;
|
|
94
|
-
lastValidL2Slot:
|
|
92
|
+
lastValidL2Slot: SlotNumber;
|
|
95
93
|
gasConfig?: Pick<L1TxConfig, 'txTimeoutAt' | 'gasLimit'>;
|
|
96
94
|
blobConfig?: L1BlobInputs;
|
|
97
95
|
checkSuccess: (
|
|
@@ -108,12 +106,15 @@ export class SequencerPublisher {
|
|
|
108
106
|
protected governanceLog = createLogger('sequencer:publisher:governance');
|
|
109
107
|
protected slashingLog = createLogger('sequencer:publisher:slashing');
|
|
110
108
|
|
|
111
|
-
protected lastActions: Partial<Record<Action,
|
|
109
|
+
protected lastActions: Partial<Record<Action, SlotNumber>> = {};
|
|
112
110
|
|
|
113
111
|
protected log: Logger;
|
|
114
112
|
protected ethereumSlotDuration: bigint;
|
|
115
113
|
|
|
116
114
|
private blobSinkClient: BlobSinkClientInterface;
|
|
115
|
+
|
|
116
|
+
/** Address to use for simulations in fisherman mode (actual proposer's address) */
|
|
117
|
+
private proposerAddressForSimulation?: EthAddress;
|
|
117
118
|
// @note - with blobs, the below estimate seems too large.
|
|
118
119
|
// Total used for full block from int_l1_pub e2e test: 1m (of which 86k is 1x blob)
|
|
119
120
|
// Total used for emptier block from above test: 429k (of which 84k is 1x blob)
|
|
@@ -146,7 +147,7 @@ export class SequencerPublisher {
|
|
|
146
147
|
epochCache: EpochCache;
|
|
147
148
|
dateProvider: DateProvider;
|
|
148
149
|
metrics: SequencerPublisherMetrics;
|
|
149
|
-
lastActions: Partial<Record<Action,
|
|
150
|
+
lastActions: Partial<Record<Action, SlotNumber>>;
|
|
150
151
|
log?: Logger;
|
|
151
152
|
},
|
|
152
153
|
) {
|
|
@@ -183,14 +184,33 @@ export class SequencerPublisher {
|
|
|
183
184
|
return this.l1TxUtils.getSenderAddress();
|
|
184
185
|
}
|
|
185
186
|
|
|
187
|
+
/**
|
|
188
|
+
* Sets the proposer address to use for simulations in fisherman mode.
|
|
189
|
+
* @param proposerAddress - The actual proposer's address to use for balance lookups in simulations
|
|
190
|
+
*/
|
|
191
|
+
public setProposerAddressForSimulation(proposerAddress: EthAddress | undefined) {
|
|
192
|
+
this.proposerAddressForSimulation = proposerAddress;
|
|
193
|
+
}
|
|
194
|
+
|
|
186
195
|
public addRequest(request: RequestWithExpiry) {
|
|
187
196
|
this.requests.push(request);
|
|
188
197
|
}
|
|
189
198
|
|
|
190
|
-
public getCurrentL2Slot():
|
|
199
|
+
public getCurrentL2Slot(): SlotNumber {
|
|
191
200
|
return this.epochCache.getEpochAndSlotNow().slot;
|
|
192
201
|
}
|
|
193
202
|
|
|
203
|
+
/**
|
|
204
|
+
* Clears all pending requests without sending them.
|
|
205
|
+
*/
|
|
206
|
+
public clearPendingRequests(): void {
|
|
207
|
+
const count = this.requests.length;
|
|
208
|
+
this.requests = [];
|
|
209
|
+
if (count > 0) {
|
|
210
|
+
this.log.debug(`Cleared ${count} pending request(s)`);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
194
214
|
/**
|
|
195
215
|
* Sends all requests that are still valid.
|
|
196
216
|
* @returns one of:
|
|
@@ -315,13 +335,18 @@ export class SequencerPublisher {
|
|
|
315
335
|
public canProposeAtNextEthBlock(
|
|
316
336
|
tipArchive: Fr,
|
|
317
337
|
msgSender: EthAddress,
|
|
318
|
-
opts: { forcePendingBlockNumber?:
|
|
338
|
+
opts: { forcePendingBlockNumber?: BlockNumber } = {},
|
|
319
339
|
) {
|
|
320
340
|
// TODO: #14291 - should loop through multiple keys to check if any of them can propose
|
|
321
341
|
const ignoredErrors = ['SlotAlreadyInChain', 'InvalidProposer', 'InvalidArchive'];
|
|
322
342
|
|
|
323
343
|
return this.rollupContract
|
|
324
|
-
.canProposeAtNextEthBlock(tipArchive.toBuffer(), msgSender.toString(), this.ethereumSlotDuration,
|
|
344
|
+
.canProposeAtNextEthBlock(tipArchive.toBuffer(), msgSender.toString(), Number(this.ethereumSlotDuration), {
|
|
345
|
+
forcePendingCheckpointNumber:
|
|
346
|
+
opts.forcePendingBlockNumber !== undefined
|
|
347
|
+
? CheckpointNumber.fromBlockNumber(opts.forcePendingBlockNumber)
|
|
348
|
+
: undefined,
|
|
349
|
+
})
|
|
325
350
|
.catch(err => {
|
|
326
351
|
if (err instanceof FormattedViemError && ignoredErrors.find(e => err.message.includes(e))) {
|
|
327
352
|
this.log.warn(`Failed canProposeAtTime check with ${ignoredErrors.find(e => err.message.includes(e))}`, {
|
|
@@ -339,7 +364,10 @@ export class SequencerPublisher {
|
|
|
339
364
|
* It will throw if the block header is invalid.
|
|
340
365
|
* @param header - The block header to validate
|
|
341
366
|
*/
|
|
342
|
-
public async validateBlockHeader(
|
|
367
|
+
public async validateBlockHeader(
|
|
368
|
+
header: CheckpointHeader,
|
|
369
|
+
opts?: { forcePendingBlockNumber: BlockNumber | undefined },
|
|
370
|
+
) {
|
|
343
371
|
const flags = { ignoreDA: true, ignoreSignatures: true };
|
|
344
372
|
|
|
345
373
|
const args = [
|
|
@@ -353,10 +381,26 @@ export class SequencerPublisher {
|
|
|
353
381
|
] as const;
|
|
354
382
|
|
|
355
383
|
const ts = BigInt((await this.l1TxUtils.getBlock()).timestamp + this.ethereumSlotDuration);
|
|
384
|
+
const optsForcePendingCheckpointNumber =
|
|
385
|
+
opts?.forcePendingBlockNumber !== undefined
|
|
386
|
+
? CheckpointNumber.fromBlockNumber(opts.forcePendingBlockNumber)
|
|
387
|
+
: undefined;
|
|
388
|
+
const stateOverrides = await this.rollupContract.makePendingCheckpointNumberOverride(
|
|
389
|
+
optsForcePendingCheckpointNumber,
|
|
390
|
+
);
|
|
391
|
+
let balance = 0n;
|
|
392
|
+
if (this.config.fishermanMode) {
|
|
393
|
+
// In fisherman mode, we can't know where the proposer is publishing from
|
|
394
|
+
// so we just add sufficient balance to the multicall3 address
|
|
395
|
+
balance = 10n * WEI_CONST * WEI_CONST; // 10 ETH
|
|
396
|
+
} else {
|
|
397
|
+
balance = await this.l1TxUtils.getSenderBalance();
|
|
398
|
+
}
|
|
399
|
+
stateOverrides.push({
|
|
400
|
+
address: MULTI_CALL_3_ADDRESS,
|
|
401
|
+
balance,
|
|
402
|
+
});
|
|
356
403
|
|
|
357
|
-
// use sender balance to simulate
|
|
358
|
-
const balance = await this.l1TxUtils.getSenderBalance();
|
|
359
|
-
this.log.debug(`Simulating validateHeader with balance: ${balance}`);
|
|
360
404
|
await this.l1TxUtils.simulate(
|
|
361
405
|
{
|
|
362
406
|
to: this.rollupContract.address,
|
|
@@ -364,10 +408,7 @@ export class SequencerPublisher {
|
|
|
364
408
|
from: MULTI_CALL_3_ADDRESS,
|
|
365
409
|
},
|
|
366
410
|
{ time: ts + 1n },
|
|
367
|
-
|
|
368
|
-
{ address: MULTI_CALL_3_ADDRESS, balance },
|
|
369
|
-
...(await this.rollupContract.makePendingBlockNumberOverride(opts?.forcePendingBlockNumber)),
|
|
370
|
-
],
|
|
411
|
+
stateOverrides,
|
|
371
412
|
);
|
|
372
413
|
this.log.debug(`Simulated validateHeader`);
|
|
373
414
|
}
|
|
@@ -387,7 +428,7 @@ export class SequencerPublisher {
|
|
|
387
428
|
const blockNumber = block.blockNumber;
|
|
388
429
|
const logData = { ...block, reason };
|
|
389
430
|
|
|
390
|
-
const currentBlockNumber = await this.rollupContract.
|
|
431
|
+
const currentBlockNumber = await this.rollupContract.getCheckpointNumber();
|
|
391
432
|
if (currentBlockNumber < validationResult.block.blockNumber) {
|
|
392
433
|
this.log.verbose(
|
|
393
434
|
`Skipping block ${blockNumber} invalidation since it has already been removed from the pending chain`,
|
|
@@ -403,7 +444,7 @@ export class SequencerPublisher {
|
|
|
403
444
|
const { gasUsed } = await this.l1TxUtils.simulate(request, undefined, undefined, ErrorsAbi);
|
|
404
445
|
this.log.verbose(`Simulation for invalidate block ${blockNumber} succeeded`, { ...logData, request, gasUsed });
|
|
405
446
|
|
|
406
|
-
return { request, gasUsed, blockNumber, forcePendingBlockNumber: blockNumber - 1, reason };
|
|
447
|
+
return { request, gasUsed, blockNumber, forcePendingBlockNumber: BlockNumber(blockNumber - 1), reason };
|
|
407
448
|
} catch (err) {
|
|
408
449
|
const viemError = formatViemError(err);
|
|
409
450
|
|
|
@@ -414,7 +455,7 @@ export class SequencerPublisher {
|
|
|
414
455
|
`Simulation for invalidate block ${blockNumber} failed due to block not being in pending chain`,
|
|
415
456
|
{ ...logData, request, error: viemError.message },
|
|
416
457
|
);
|
|
417
|
-
const latestPendingBlockNumber = await this.rollupContract.
|
|
458
|
+
const latestPendingBlockNumber = await this.rollupContract.getCheckpointNumber();
|
|
418
459
|
if (latestPendingBlockNumber < blockNumber) {
|
|
419
460
|
this.log.verbose(`Block number ${blockNumber} has already been invalidated`, { ...logData });
|
|
420
461
|
return undefined;
|
|
@@ -451,14 +492,14 @@ export class SequencerPublisher {
|
|
|
451
492
|
|
|
452
493
|
if (reason === 'invalid-attestation') {
|
|
453
494
|
return this.rollupContract.buildInvalidateBadAttestationRequest(
|
|
454
|
-
block.blockNumber,
|
|
495
|
+
CheckpointNumber.fromBlockNumber(block.blockNumber),
|
|
455
496
|
attestationsAndSigners,
|
|
456
497
|
committee,
|
|
457
498
|
validationResult.invalidIndex,
|
|
458
499
|
);
|
|
459
500
|
} else if (reason === 'insufficient-attestations') {
|
|
460
501
|
return this.rollupContract.buildInvalidateInsufficientAttestationsRequest(
|
|
461
|
-
block.blockNumber,
|
|
502
|
+
CheckpointNumber.fromBlockNumber(block.blockNumber),
|
|
462
503
|
attestationsAndSigners,
|
|
463
504
|
committee,
|
|
464
505
|
);
|
|
@@ -481,7 +522,7 @@ export class SequencerPublisher {
|
|
|
481
522
|
block: L2Block,
|
|
482
523
|
attestationsAndSigners: CommitteeAttestationsAndSigners,
|
|
483
524
|
attestationsAndSignersSignature: Signature,
|
|
484
|
-
options: { forcePendingBlockNumber?:
|
|
525
|
+
options: { forcePendingBlockNumber?: BlockNumber },
|
|
485
526
|
): Promise<bigint> {
|
|
486
527
|
const ts = BigInt((await this.l1TxUtils.getBlock()).timestamp + this.ethereumSlotDuration);
|
|
487
528
|
|
|
@@ -489,10 +530,10 @@ export class SequencerPublisher {
|
|
|
489
530
|
// so that the committee is recalculated correctly
|
|
490
531
|
const ignoreSignatures = attestationsAndSigners.attestations.length === 0;
|
|
491
532
|
if (ignoreSignatures) {
|
|
492
|
-
const { committee } = await this.epochCache.getCommittee(block.header.globalVariables.slotNumber
|
|
533
|
+
const { committee } = await this.epochCache.getCommittee(block.header.globalVariables.slotNumber);
|
|
493
534
|
if (!committee) {
|
|
494
|
-
this.log.warn(`No committee found for slot ${block.header.globalVariables.slotNumber
|
|
495
|
-
throw new Error(`No committee found for slot ${block.header.globalVariables.slotNumber
|
|
535
|
+
this.log.warn(`No committee found for slot ${block.header.globalVariables.slotNumber}`);
|
|
536
|
+
throw new Error(`No committee found for slot ${block.header.globalVariables.slotNumber}`);
|
|
496
537
|
}
|
|
497
538
|
attestationsAndSigners.attestations = committee.map(committeeMember =>
|
|
498
539
|
CommitteeAttestation.fromAddress(committeeMember),
|
|
@@ -507,7 +548,6 @@ export class SequencerPublisher {
|
|
|
507
548
|
{
|
|
508
549
|
header: block.getCheckpointHeader().toViem(),
|
|
509
550
|
archive: toHex(block.archive.root.toBuffer()),
|
|
510
|
-
stateReference: block.header.state.toViem(),
|
|
511
551
|
oracleInput: {
|
|
512
552
|
feeAssetPriceModifier: 0n,
|
|
513
553
|
},
|
|
@@ -523,7 +563,7 @@ export class SequencerPublisher {
|
|
|
523
563
|
}
|
|
524
564
|
|
|
525
565
|
private async enqueueCastSignalHelper(
|
|
526
|
-
slotNumber:
|
|
566
|
+
slotNumber: SlotNumber,
|
|
527
567
|
timestamp: bigint,
|
|
528
568
|
signalType: GovernanceSignalAction,
|
|
529
569
|
payload: EthAddress,
|
|
@@ -616,7 +656,7 @@ export class SequencerPublisher {
|
|
|
616
656
|
*/
|
|
617
657
|
public enqueueGovernanceCastSignal(
|
|
618
658
|
governancePayload: EthAddress,
|
|
619
|
-
slotNumber:
|
|
659
|
+
slotNumber: SlotNumber,
|
|
620
660
|
timestamp: bigint,
|
|
621
661
|
signerAddress: EthAddress,
|
|
622
662
|
signer: (msg: TypedDataDefinition) => Promise<`0x${string}`>,
|
|
@@ -635,7 +675,7 @@ export class SequencerPublisher {
|
|
|
635
675
|
/** Enqueues all slashing actions as returned by the slasher client. */
|
|
636
676
|
public async enqueueSlashingActions(
|
|
637
677
|
actions: ProposerSlashAction[],
|
|
638
|
-
slotNumber:
|
|
678
|
+
slotNumber: SlotNumber,
|
|
639
679
|
timestamp: bigint,
|
|
640
680
|
signerAddress: EthAddress,
|
|
641
681
|
signer: (msg: TypedDataDefinition) => Promise<`0x${string}`>,
|
|
@@ -765,7 +805,7 @@ export class SequencerPublisher {
|
|
|
765
805
|
block: L2Block,
|
|
766
806
|
attestationsAndSigners: CommitteeAttestationsAndSigners,
|
|
767
807
|
attestationsAndSignersSignature: Signature,
|
|
768
|
-
opts: { txTimeoutAt?: Date; forcePendingBlockNumber?:
|
|
808
|
+
opts: { txTimeoutAt?: Date; forcePendingBlockNumber?: BlockNumber } = {},
|
|
769
809
|
): Promise<boolean> {
|
|
770
810
|
const checkpointHeader = block.getCheckpointHeader();
|
|
771
811
|
|
|
@@ -775,7 +815,6 @@ export class SequencerPublisher {
|
|
|
775
815
|
const proposeTxArgs = {
|
|
776
816
|
header: checkpointHeader,
|
|
777
817
|
archive: block.archive.root.toBuffer(),
|
|
778
|
-
stateReference: block.header.state,
|
|
779
818
|
body: block.body.toBuffer(),
|
|
780
819
|
blobs,
|
|
781
820
|
attestationsAndSigners,
|
|
@@ -794,7 +833,7 @@ export class SequencerPublisher {
|
|
|
794
833
|
} catch (err: any) {
|
|
795
834
|
this.log.error(`Block validation failed. ${err instanceof Error ? err.message : 'No error message'}`, err, {
|
|
796
835
|
...block.getStats(),
|
|
797
|
-
slotNumber: block.header.globalVariables.slotNumber
|
|
836
|
+
slotNumber: block.header.globalVariables.slotNumber,
|
|
798
837
|
forcePendingBlockNumber: opts.forcePendingBlockNumber,
|
|
799
838
|
});
|
|
800
839
|
throw err;
|
|
@@ -820,13 +859,13 @@ export class SequencerPublisher {
|
|
|
820
859
|
action: `invalidate-by-${request.reason}`,
|
|
821
860
|
request: request.request,
|
|
822
861
|
gasConfig: { gasLimit, txTimeoutAt: opts.txTimeoutAt },
|
|
823
|
-
lastValidL2Slot: this.getCurrentL2Slot() +
|
|
862
|
+
lastValidL2Slot: SlotNumber(this.getCurrentL2Slot() + 2),
|
|
824
863
|
checkSuccess: (_req, result) => {
|
|
825
864
|
const success =
|
|
826
865
|
result &&
|
|
827
866
|
result.receipt &&
|
|
828
867
|
result.receipt.status === 'success' &&
|
|
829
|
-
tryExtractEvent(result.receipt.logs, this.rollupContract.address, RollupAbi, '
|
|
868
|
+
tryExtractEvent(result.receipt.logs, this.rollupContract.address, RollupAbi, 'CheckpointInvalidated');
|
|
830
869
|
if (!success) {
|
|
831
870
|
this.log.warn(`Invalidate block ${request.blockNumber} failed`, { ...result, ...logData });
|
|
832
871
|
} else {
|
|
@@ -841,7 +880,7 @@ export class SequencerPublisher {
|
|
|
841
880
|
action: Action,
|
|
842
881
|
request: L1TxRequest,
|
|
843
882
|
checkSuccess: (receipt: TransactionReceipt) => boolean | undefined,
|
|
844
|
-
slotNumber:
|
|
883
|
+
slotNumber: SlotNumber,
|
|
845
884
|
timestamp: bigint,
|
|
846
885
|
) {
|
|
847
886
|
const logData = { slotNumber, timestamp, gasLimit: undefined as bigint | undefined };
|
|
@@ -909,41 +948,50 @@ export class SequencerPublisher {
|
|
|
909
948
|
private async prepareProposeTx(
|
|
910
949
|
encodedData: L1ProcessArgs,
|
|
911
950
|
timestamp: bigint,
|
|
912
|
-
options: { forcePendingBlockNumber?:
|
|
951
|
+
options: { forcePendingBlockNumber?: BlockNumber },
|
|
913
952
|
) {
|
|
914
953
|
const kzg = Blob.getViemKzgInstance();
|
|
915
954
|
const blobInput = getPrefixedEthBlobCommitments(encodedData.blobs);
|
|
916
955
|
this.log.debug('Validating blob input', { blobInput });
|
|
917
|
-
const blobEvaluationGas = await this.l1TxUtils
|
|
918
|
-
.estimateGas(
|
|
919
|
-
this.getSenderAddress().toString(),
|
|
920
|
-
{
|
|
921
|
-
to: this.rollupContract.address,
|
|
922
|
-
data: encodeFunctionData({
|
|
923
|
-
abi: RollupAbi,
|
|
924
|
-
functionName: 'validateBlobs',
|
|
925
|
-
args: [blobInput],
|
|
926
|
-
}),
|
|
927
|
-
},
|
|
928
|
-
{},
|
|
929
|
-
{
|
|
930
|
-
blobs: encodedData.blobs.map(b => b.data),
|
|
931
|
-
kzg,
|
|
932
|
-
},
|
|
933
|
-
)
|
|
934
|
-
.catch(err => {
|
|
935
|
-
const { message, metaMessages } = formatViemError(err);
|
|
936
|
-
this.log.error(`Failed to validate blobs`, message, { metaMessages });
|
|
937
|
-
throw new Error('Failed to validate blobs');
|
|
938
|
-
});
|
|
939
956
|
|
|
957
|
+
// Get blob evaluation gas
|
|
958
|
+
let blobEvaluationGas: bigint;
|
|
959
|
+
if (this.config.fishermanMode) {
|
|
960
|
+
// In fisherman mode, we can't estimate blob gas because estimateGas doesn't support state overrides
|
|
961
|
+
// Use a fixed estimate.
|
|
962
|
+
blobEvaluationGas = BigInt(encodedData.blobs.length) * 21_000n;
|
|
963
|
+
this.log.debug(`Using fixed blob evaluation gas estimate in fisherman mode: ${blobEvaluationGas}`);
|
|
964
|
+
} else {
|
|
965
|
+
// Normal mode - use estimateGas with blob inputs
|
|
966
|
+
blobEvaluationGas = await this.l1TxUtils
|
|
967
|
+
.estimateGas(
|
|
968
|
+
this.getSenderAddress().toString(),
|
|
969
|
+
{
|
|
970
|
+
to: this.rollupContract.address,
|
|
971
|
+
data: encodeFunctionData({
|
|
972
|
+
abi: RollupAbi,
|
|
973
|
+
functionName: 'validateBlobs',
|
|
974
|
+
args: [blobInput],
|
|
975
|
+
}),
|
|
976
|
+
},
|
|
977
|
+
{},
|
|
978
|
+
{
|
|
979
|
+
blobs: encodedData.blobs.map(b => b.data),
|
|
980
|
+
kzg,
|
|
981
|
+
},
|
|
982
|
+
)
|
|
983
|
+
.catch(err => {
|
|
984
|
+
const { message, metaMessages } = formatViemError(err);
|
|
985
|
+
this.log.error(`Failed to validate blobs`, message, { metaMessages });
|
|
986
|
+
throw new Error('Failed to validate blobs');
|
|
987
|
+
});
|
|
988
|
+
}
|
|
940
989
|
const signers = encodedData.attestationsAndSigners.getSigners().map(signer => signer.toString());
|
|
941
990
|
|
|
942
991
|
const args = [
|
|
943
992
|
{
|
|
944
993
|
header: encodedData.header.toViem(),
|
|
945
994
|
archive: toHex(encodedData.archive),
|
|
946
|
-
stateReference: encodedData.stateReference.toViem(),
|
|
947
995
|
oracleInput: {
|
|
948
996
|
// We are currently not modifying these. See #9963
|
|
949
997
|
feeAssetPriceModifier: 0n,
|
|
@@ -971,7 +1019,6 @@ export class SequencerPublisher {
|
|
|
971
1019
|
{
|
|
972
1020
|
readonly header: ViemHeader;
|
|
973
1021
|
readonly archive: `0x${string}`;
|
|
974
|
-
readonly stateReference: ViemStateReference;
|
|
975
1022
|
readonly oracleInput: {
|
|
976
1023
|
readonly feeAssetPriceModifier: 0n;
|
|
977
1024
|
};
|
|
@@ -982,7 +1029,7 @@ export class SequencerPublisher {
|
|
|
982
1029
|
`0x${string}`,
|
|
983
1030
|
],
|
|
984
1031
|
timestamp: bigint,
|
|
985
|
-
options: { forcePendingBlockNumber?:
|
|
1032
|
+
options: { forcePendingBlockNumber?: BlockNumber },
|
|
986
1033
|
) {
|
|
987
1034
|
const rollupData = encodeFunctionData({
|
|
988
1035
|
abi: RollupAbi,
|
|
@@ -990,19 +1037,42 @@ export class SequencerPublisher {
|
|
|
990
1037
|
args,
|
|
991
1038
|
});
|
|
992
1039
|
|
|
993
|
-
// override the pending
|
|
994
|
-
const
|
|
1040
|
+
// override the pending checkpoint number if requested
|
|
1041
|
+
const optsForcePendingCheckpointNumber =
|
|
995
1042
|
options.forcePendingBlockNumber !== undefined
|
|
996
|
-
?
|
|
1043
|
+
? CheckpointNumber.fromBlockNumber(options.forcePendingBlockNumber)
|
|
1044
|
+
: undefined;
|
|
1045
|
+
const forcePendingCheckpointNumberStateDiff = (
|
|
1046
|
+
optsForcePendingCheckpointNumber !== undefined
|
|
1047
|
+
? await this.rollupContract.makePendingCheckpointNumberOverride(optsForcePendingCheckpointNumber)
|
|
997
1048
|
: []
|
|
998
1049
|
).flatMap(override => override.stateDiff ?? []);
|
|
999
1050
|
|
|
1051
|
+
const stateOverrides: StateOverride = [
|
|
1052
|
+
{
|
|
1053
|
+
address: this.rollupContract.address,
|
|
1054
|
+
// @note we override checkBlob to false since blobs are not part simulate()
|
|
1055
|
+
stateDiff: [
|
|
1056
|
+
{ slot: toPaddedHex(RollupContract.checkBlobStorageSlot, true), value: toPaddedHex(0n, true) },
|
|
1057
|
+
...forcePendingCheckpointNumberStateDiff,
|
|
1058
|
+
],
|
|
1059
|
+
},
|
|
1060
|
+
];
|
|
1061
|
+
// In fisherman mode, simulate as the proposer but with sufficient balance
|
|
1062
|
+
if (this.proposerAddressForSimulation) {
|
|
1063
|
+
stateOverrides.push({
|
|
1064
|
+
address: this.proposerAddressForSimulation.toString(),
|
|
1065
|
+
balance: 10n * WEI_CONST * WEI_CONST, // 10 ETH
|
|
1066
|
+
});
|
|
1067
|
+
}
|
|
1068
|
+
|
|
1000
1069
|
const simulationResult = await this.l1TxUtils
|
|
1001
1070
|
.simulate(
|
|
1002
1071
|
{
|
|
1003
1072
|
to: this.rollupContract.address,
|
|
1004
1073
|
data: rollupData,
|
|
1005
1074
|
gas: SequencerPublisher.PROPOSE_GAS_GUESS,
|
|
1075
|
+
...(this.proposerAddressForSimulation && { from: this.proposerAddressForSimulation.toString() }),
|
|
1006
1076
|
},
|
|
1007
1077
|
{
|
|
1008
1078
|
// @note we add 1n to the timestamp because geth implementation doesn't like simulation timestamp to be equal to the current block timestamp
|
|
@@ -1010,16 +1080,7 @@ export class SequencerPublisher {
|
|
|
1010
1080
|
// @note reth should have a 30m gas limit per block but throws errors that this tx is beyond limit so we increase here
|
|
1011
1081
|
gasLimit: SequencerPublisher.PROPOSE_GAS_GUESS * 2n,
|
|
1012
1082
|
},
|
|
1013
|
-
|
|
1014
|
-
{
|
|
1015
|
-
address: this.rollupContract.address,
|
|
1016
|
-
// @note we override checkBlob to false since blobs are not part simulate()
|
|
1017
|
-
stateDiff: [
|
|
1018
|
-
{ slot: toPaddedHex(RollupContract.checkBlobStorageSlot, true), value: toPaddedHex(0n, true) },
|
|
1019
|
-
...forcePendingBlockNumberStateDiff,
|
|
1020
|
-
],
|
|
1021
|
-
},
|
|
1022
|
-
],
|
|
1083
|
+
stateOverrides,
|
|
1023
1084
|
RollupAbi,
|
|
1024
1085
|
{
|
|
1025
1086
|
// @note fallback gas estimate to use if the node doesn't support simulation API
|
|
@@ -1027,7 +1088,17 @@ export class SequencerPublisher {
|
|
|
1027
1088
|
},
|
|
1028
1089
|
)
|
|
1029
1090
|
.catch(err => {
|
|
1030
|
-
|
|
1091
|
+
// In fisherman mode, we expect ValidatorSelection__MissingProposerSignature since fisherman doesn't have proposer signature
|
|
1092
|
+
const viemError = formatViemError(err);
|
|
1093
|
+
if (this.config.fishermanMode && viemError.message?.includes('ValidatorSelection__MissingProposerSignature')) {
|
|
1094
|
+
this.log.debug(`Ignoring expected ValidatorSelection__MissingProposerSignature error in fisherman mode`);
|
|
1095
|
+
// Return a minimal simulation result with the fallback gas estimate
|
|
1096
|
+
return {
|
|
1097
|
+
gasUsed: SequencerPublisher.PROPOSE_GAS_GUESS,
|
|
1098
|
+
logs: [],
|
|
1099
|
+
};
|
|
1100
|
+
}
|
|
1101
|
+
this.log.error(`Failed to simulate propose tx`, viemError);
|
|
1031
1102
|
throw err;
|
|
1032
1103
|
});
|
|
1033
1104
|
|
|
@@ -1037,7 +1108,7 @@ export class SequencerPublisher {
|
|
|
1037
1108
|
private async addProposeTx(
|
|
1038
1109
|
block: L2Block,
|
|
1039
1110
|
encodedData: L1ProcessArgs,
|
|
1040
|
-
opts: { txTimeoutAt?: Date; forcePendingBlockNumber?:
|
|
1111
|
+
opts: { txTimeoutAt?: Date; forcePendingBlockNumber?: BlockNumber } = {},
|
|
1041
1112
|
timestamp: bigint,
|
|
1042
1113
|
): Promise<void> {
|
|
1043
1114
|
const timer = new Timer();
|
|
@@ -1066,7 +1137,7 @@ export class SequencerPublisher {
|
|
|
1066
1137
|
to: this.rollupContract.address,
|
|
1067
1138
|
data: rollupData,
|
|
1068
1139
|
},
|
|
1069
|
-
lastValidL2Slot: block.header.globalVariables.slotNumber
|
|
1140
|
+
lastValidL2Slot: block.header.globalVariables.slotNumber,
|
|
1070
1141
|
gasConfig: { ...opts, gasLimit },
|
|
1071
1142
|
blobConfig: {
|
|
1072
1143
|
blobs: encodedData.blobs.map(b => b.data),
|
|
@@ -1080,7 +1151,7 @@ export class SequencerPublisher {
|
|
|
1080
1151
|
const success =
|
|
1081
1152
|
receipt &&
|
|
1082
1153
|
receipt.status === 'success' &&
|
|
1083
|
-
tryExtractEvent(receipt.logs, this.rollupContract.address, RollupAbi, '
|
|
1154
|
+
tryExtractEvent(receipt.logs, this.rollupContract.address, RollupAbi, 'CheckpointProposed');
|
|
1084
1155
|
if (success) {
|
|
1085
1156
|
const endBlock = receipt.blockNumber;
|
|
1086
1157
|
const inclusionBlocks = Number(endBlock - startBlock);
|
|
@@ -1109,7 +1180,7 @@ export class SequencerPublisher {
|
|
|
1109
1180
|
...block.getStats(),
|
|
1110
1181
|
receipt,
|
|
1111
1182
|
txHash: receipt.transactionHash,
|
|
1112
|
-
slotNumber: block.header.globalVariables.slotNumber
|
|
1183
|
+
slotNumber: block.header.globalVariables.slotNumber,
|
|
1113
1184
|
});
|
|
1114
1185
|
return false;
|
|
1115
1186
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { MerkleTreeId } from '@aztec/aztec.js/trees';
|
|
2
|
+
import { BlockNumber } from '@aztec/foundation/branded-types';
|
|
2
3
|
import { merge, pick } from '@aztec/foundation/collection';
|
|
3
|
-
import type { Fr } from '@aztec/foundation/
|
|
4
|
+
import type { Fr } from '@aztec/foundation/curves/bn254';
|
|
4
5
|
import { createLogger } from '@aztec/foundation/log';
|
|
5
6
|
import { retryUntil } from '@aztec/foundation/retry';
|
|
6
7
|
import { bufferToHex } from '@aztec/foundation/string';
|
|
@@ -11,7 +12,7 @@ import {
|
|
|
11
12
|
GuardedMerkleTreeOperations,
|
|
12
13
|
PublicContractsDB,
|
|
13
14
|
PublicProcessor,
|
|
14
|
-
|
|
15
|
+
createPublicTxSimulatorForBlockBuilding,
|
|
15
16
|
} from '@aztec/simulator/server';
|
|
16
17
|
import type { ContractDataSource } from '@aztec/stdlib/contract';
|
|
17
18
|
import { type L1RollupConstants, getTimestampForSlot } from '@aztec/stdlib/epoch-helpers';
|
|
@@ -46,7 +47,7 @@ export async function buildBlock(
|
|
|
46
47
|
): Promise<BuildBlockResult> {
|
|
47
48
|
const blockBuildingTimer = new Timer();
|
|
48
49
|
const blockNumber = newGlobalVariables.blockNumber;
|
|
49
|
-
const slot = newGlobalVariables.slotNumber
|
|
50
|
+
const slot = newGlobalVariables.slotNumber;
|
|
50
51
|
const msgCount = l1ToL2Messages.length;
|
|
51
52
|
const stateReference = await worldStateFork.getStateReference();
|
|
52
53
|
const archiveTree = await worldStateFork.getTreeInfo(MerkleTreeId.ARCHIVE);
|
|
@@ -120,16 +121,11 @@ export class FullNodeBlockBuilder implements IFullNodeBlockBuilder {
|
|
|
120
121
|
const contractsDB = new PublicContractsDB(this.contractDataSource);
|
|
121
122
|
const guardedFork = new GuardedMerkleTreeOperations(fork);
|
|
122
123
|
|
|
123
|
-
const publicTxSimulator =
|
|
124
|
+
const publicTxSimulator = createPublicTxSimulatorForBlockBuilding(
|
|
124
125
|
guardedFork,
|
|
125
126
|
contractsDB,
|
|
126
127
|
globalVariables,
|
|
127
128
|
this.telemetryClient,
|
|
128
|
-
{
|
|
129
|
-
doMerkleOperations: true,
|
|
130
|
-
skipFeeEnforcement: true,
|
|
131
|
-
clientInitiatedSimulation: false,
|
|
132
|
-
},
|
|
133
129
|
);
|
|
134
130
|
|
|
135
131
|
const processor = new PublicProcessor(
|
|
@@ -156,7 +152,7 @@ export class FullNodeBlockBuilder implements IFullNodeBlockBuilder {
|
|
|
156
152
|
};
|
|
157
153
|
}
|
|
158
154
|
|
|
159
|
-
private async syncToPreviousBlock(parentBlockNumber:
|
|
155
|
+
private async syncToPreviousBlock(parentBlockNumber: BlockNumber, timeout: number | undefined) {
|
|
160
156
|
await retryUntil(
|
|
161
157
|
() => this.worldState.syncImmediate(parentBlockNumber, true).then(syncedTo => syncedTo >= parentBlockNumber),
|
|
162
158
|
'sync to previous block',
|
|
@@ -173,7 +169,7 @@ export class FullNodeBlockBuilder implements IFullNodeBlockBuilder {
|
|
|
173
169
|
opts: PublicProcessorLimits,
|
|
174
170
|
suppliedFork?: MerkleTreeWriteOperations,
|
|
175
171
|
): Promise<BuildBlockResult> {
|
|
176
|
-
const parentBlockNumber = globalVariables.blockNumber - 1;
|
|
172
|
+
const parentBlockNumber = BlockNumber(globalVariables.blockNumber - 1);
|
|
177
173
|
const syncTimeout = opts.deadline ? (opts.deadline.getTime() - this.dateProvider.now()) / 1000 : undefined;
|
|
178
174
|
await this.syncToPreviousBlock(parentBlockNumber, syncTimeout);
|
|
179
175
|
const fork = suppliedFork ?? (await this.worldState.fork(parentBlockNumber));
|
|
@@ -212,7 +208,7 @@ export class FullNodeBlockBuilder implements IFullNodeBlockBuilder {
|
|
|
212
208
|
}
|
|
213
209
|
}
|
|
214
210
|
|
|
215
|
-
getFork(blockNumber:
|
|
211
|
+
getFork(blockNumber: BlockNumber): Promise<MerkleTreeWriteOperations> {
|
|
216
212
|
return this.worldState.fork(blockNumber);
|
|
217
213
|
}
|
|
218
214
|
}
|
package/src/sequencer/config.ts
CHANGED