@aztec/sequencer-client 2.0.3-rc.2 → 2.0.3-rc.21
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/sequencer-client.d.ts.map +1 -1
- package/dest/client/sequencer-client.js +5 -4
- package/dest/config.d.ts +2 -1
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +2 -0
- package/dest/publisher/config.d.ts +5 -3
- package/dest/publisher/config.d.ts.map +1 -1
- package/dest/publisher/config.js +13 -5
- package/dest/publisher/index.d.ts +1 -1
- package/dest/publisher/index.d.ts.map +1 -1
- package/dest/publisher/index.js +1 -1
- package/dest/publisher/sequencer-publisher-factory.d.ts +5 -1
- package/dest/publisher/sequencer-publisher-factory.d.ts.map +1 -1
- package/dest/publisher/sequencer-publisher-factory.js +8 -1
- package/dest/publisher/sequencer-publisher.d.ts +13 -15
- package/dest/publisher/sequencer-publisher.d.ts.map +1 -1
- package/dest/publisher/sequencer-publisher.js +51 -52
- package/dest/sequencer/metrics.d.ts +5 -17
- package/dest/sequencer/metrics.d.ts.map +1 -1
- package/dest/sequencer/metrics.js +22 -88
- package/dest/sequencer/sequencer.d.ts +4 -3
- package/dest/sequencer/sequencer.d.ts.map +1 -1
- package/dest/sequencer/sequencer.js +16 -17
- package/package.json +27 -26
- package/src/client/sequencer-client.ts +4 -2
- package/src/config.ts +3 -0
- package/src/publisher/config.ts +23 -5
- package/src/publisher/index.ts +1 -1
- package/src/publisher/sequencer-publisher-factory.ts +12 -2
- package/src/publisher/sequencer-publisher.ts +71 -66
- package/src/sequencer/metrics.ts +24 -100
- package/src/sequencer/sequencer.ts +42 -38
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { EthAddress } from '@aztec/aztec.js';
|
|
1
|
+
import { EthAddress, type Logger, createLogger } from '@aztec/aztec.js';
|
|
2
2
|
import type { BlobSinkClientInterface } from '@aztec/blob-sink/client';
|
|
3
3
|
import type { EpochCache } from '@aztec/epoch-cache';
|
|
4
4
|
import type { GovernanceProposerContract, PublisherFilter, PublisherManager, RollupContract } from '@aztec/ethereum';
|
|
@@ -10,7 +10,7 @@ import { NodeKeystoreAdapter } from '@aztec/validator-client';
|
|
|
10
10
|
|
|
11
11
|
import type { SequencerClientConfig } from '../config.js';
|
|
12
12
|
import { SequencerPublisherMetrics } from './sequencer-publisher-metrics.js';
|
|
13
|
-
import { SequencerPublisher } from './sequencer-publisher.js';
|
|
13
|
+
import { type Action, SequencerPublisher } from './sequencer-publisher.js';
|
|
14
14
|
|
|
15
15
|
export type AttestorPublisherPair = {
|
|
16
16
|
attestorAddress: EthAddress;
|
|
@@ -19,6 +19,12 @@ export type AttestorPublisherPair = {
|
|
|
19
19
|
|
|
20
20
|
export class SequencerPublisherFactory {
|
|
21
21
|
private publisherMetrics: SequencerPublisherMetrics;
|
|
22
|
+
|
|
23
|
+
/** Stores the last slot in which every action was carried out by a publisher */
|
|
24
|
+
private lastActions: Partial<Record<Action, bigint>> = {};
|
|
25
|
+
|
|
26
|
+
private logger: Logger;
|
|
27
|
+
|
|
22
28
|
constructor(
|
|
23
29
|
private sequencerConfig: SequencerClientConfig,
|
|
24
30
|
private deps: {
|
|
@@ -31,9 +37,11 @@ export class SequencerPublisherFactory {
|
|
|
31
37
|
governanceProposerContract: GovernanceProposerContract;
|
|
32
38
|
slashFactoryContract: SlashFactoryContract;
|
|
33
39
|
nodeKeyStore: NodeKeystoreAdapter;
|
|
40
|
+
logger?: Logger;
|
|
34
41
|
},
|
|
35
42
|
) {
|
|
36
43
|
this.publisherMetrics = new SequencerPublisherMetrics(deps.telemetry, 'SequencerPublisher');
|
|
44
|
+
this.logger = deps.logger ?? createLogger('sequencer');
|
|
37
45
|
}
|
|
38
46
|
/**
|
|
39
47
|
* Creates a new SequencerPublisher instance.
|
|
@@ -69,6 +77,8 @@ export class SequencerPublisherFactory {
|
|
|
69
77
|
slashFactoryContract: this.deps.slashFactoryContract,
|
|
70
78
|
dateProvider: this.deps.dateProvider,
|
|
71
79
|
metrics: this.publisherMetrics,
|
|
80
|
+
lastActions: this.lastActions,
|
|
81
|
+
log: this.logger.createChild('publisher'),
|
|
72
82
|
});
|
|
73
83
|
|
|
74
84
|
return {
|
|
@@ -27,17 +27,17 @@ import type { L1TxUtilsWithBlobs } from '@aztec/ethereum/l1-tx-utils-with-blobs'
|
|
|
27
27
|
import { sumBigint } from '@aztec/foundation/bigint';
|
|
28
28
|
import { toHex as toPaddedHex } from '@aztec/foundation/bigint-buffer';
|
|
29
29
|
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
30
|
+
import { Signature, type ViemSignature } from '@aztec/foundation/eth-signature';
|
|
30
31
|
import type { Fr } from '@aztec/foundation/fields';
|
|
31
|
-
import { createLogger } from '@aztec/foundation/log';
|
|
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';
|
|
34
35
|
import { EmpireBaseAbi, ErrorsAbi, RollupAbi } from '@aztec/l1-artifacts';
|
|
35
36
|
import { type ProposerSlashAction, encodeSlashConsensusVotes } from '@aztec/slasher';
|
|
36
|
-
import { CommitteeAttestation, type ValidateBlockResult } from '@aztec/stdlib/block';
|
|
37
|
+
import { CommitteeAttestation, CommitteeAttestationsAndSigners, type ValidateBlockResult } from '@aztec/stdlib/block';
|
|
37
38
|
import { SlashFactoryContract } from '@aztec/stdlib/l1-contracts';
|
|
38
|
-
import { ConsensusPayload, SignatureDomainSeparator, getHashedSignaturePayload } from '@aztec/stdlib/p2p';
|
|
39
39
|
import type { L1PublishBlockStats } from '@aztec/stdlib/stats';
|
|
40
|
-
import { type ProposedBlockHeader, StateReference
|
|
40
|
+
import { type ProposedBlockHeader, StateReference } from '@aztec/stdlib/tx';
|
|
41
41
|
import { type TelemetryClient, getTelemetryClient } from '@aztec/telemetry-client';
|
|
42
42
|
|
|
43
43
|
import pick from 'lodash.pick';
|
|
@@ -56,17 +56,12 @@ type L1ProcessArgs = {
|
|
|
56
56
|
stateReference: StateReference;
|
|
57
57
|
/** L2 block blobs containing all tx effects. */
|
|
58
58
|
blobs: Blob[];
|
|
59
|
-
/** L2 block tx hashes */
|
|
60
|
-
txHashes: TxHash[];
|
|
61
59
|
/** Attestations */
|
|
62
|
-
|
|
60
|
+
attestationsAndSigners: CommitteeAttestationsAndSigners;
|
|
61
|
+
/** Attestations and signers signature */
|
|
62
|
+
attestationsAndSignersSignature: Signature;
|
|
63
63
|
};
|
|
64
64
|
|
|
65
|
-
export enum SignalType {
|
|
66
|
-
GOVERNANCE,
|
|
67
|
-
SLASHING,
|
|
68
|
-
}
|
|
69
|
-
|
|
70
65
|
export const Actions = [
|
|
71
66
|
'invalidate-by-invalid-attestation',
|
|
72
67
|
'invalidate-by-insufficient-attestations',
|
|
@@ -78,8 +73,11 @@ export const Actions = [
|
|
|
78
73
|
'vote-offenses',
|
|
79
74
|
'execute-slash',
|
|
80
75
|
] as const;
|
|
76
|
+
|
|
81
77
|
export type Action = (typeof Actions)[number];
|
|
82
78
|
|
|
79
|
+
type GovernanceSignalAction = Extract<Action, 'governance-signal' | 'empire-slashing-signal'>;
|
|
80
|
+
|
|
83
81
|
// Sorting for actions such that invalidations go before proposals, and proposals go before votes
|
|
84
82
|
export const compareActions = (a: Action, b: Action) => Actions.indexOf(a) - Actions.indexOf(b);
|
|
85
83
|
|
|
@@ -104,6 +102,7 @@ interface RequestWithExpiry {
|
|
|
104
102
|
}
|
|
105
103
|
|
|
106
104
|
export class SequencerPublisher {
|
|
105
|
+
private enabled: boolean;
|
|
107
106
|
private interrupted = false;
|
|
108
107
|
private metrics: SequencerPublisherMetrics;
|
|
109
108
|
public epochCache: EpochCache;
|
|
@@ -111,12 +110,9 @@ export class SequencerPublisher {
|
|
|
111
110
|
protected governanceLog = createLogger('sequencer:publisher:governance');
|
|
112
111
|
protected slashingLog = createLogger('sequencer:publisher:slashing');
|
|
113
112
|
|
|
114
|
-
|
|
115
|
-
[SignalType.GOVERNANCE]: 0n,
|
|
116
|
-
[SignalType.SLASHING]: 0n,
|
|
117
|
-
};
|
|
113
|
+
protected lastActions: Partial<Record<Action, bigint>> = {};
|
|
118
114
|
|
|
119
|
-
protected log
|
|
115
|
+
protected log: Logger;
|
|
120
116
|
protected ethereumSlotDuration: bigint;
|
|
121
117
|
|
|
122
118
|
private blobSinkClient: BlobSinkClientInterface;
|
|
@@ -152,10 +148,15 @@ export class SequencerPublisher {
|
|
|
152
148
|
epochCache: EpochCache;
|
|
153
149
|
dateProvider: DateProvider;
|
|
154
150
|
metrics: SequencerPublisherMetrics;
|
|
151
|
+
lastActions: Partial<Record<Action, bigint>>;
|
|
152
|
+
log?: Logger;
|
|
155
153
|
},
|
|
156
154
|
) {
|
|
155
|
+
this.enabled = config.publisherEnabled ?? true;
|
|
156
|
+
this.log = deps.log ?? createLogger('sequencer:publisher');
|
|
157
157
|
this.ethereumSlotDuration = BigInt(config.ethereumSlotDuration);
|
|
158
158
|
this.epochCache = deps.epochCache;
|
|
159
|
+
this.lastActions = deps.lastActions;
|
|
159
160
|
|
|
160
161
|
this.blobSinkClient =
|
|
161
162
|
deps.blobSinkClient ?? createBlobSinkClient(config, { logger: createLogger('sequencer:blob-sink:client') });
|
|
@@ -201,6 +202,14 @@ export class SequencerPublisher {
|
|
|
201
202
|
* - undefined if no valid requests are found OR the tx failed to send.
|
|
202
203
|
*/
|
|
203
204
|
public async sendRequests() {
|
|
205
|
+
if (!this.enabled) {
|
|
206
|
+
this.log.warn(`Sending L1 txs is disabled`, {
|
|
207
|
+
requestsDiscarded: this.requests.map(r => r.action),
|
|
208
|
+
});
|
|
209
|
+
this.requests = [];
|
|
210
|
+
return undefined;
|
|
211
|
+
}
|
|
212
|
+
|
|
204
213
|
const requestsToProcess = [...this.requests];
|
|
205
214
|
this.requests = [];
|
|
206
215
|
if (this.interrupted) {
|
|
@@ -346,8 +355,9 @@ export class SequencerPublisher {
|
|
|
346
355
|
|
|
347
356
|
const args = [
|
|
348
357
|
header.toViem(),
|
|
349
|
-
|
|
358
|
+
CommitteeAttestationsAndSigners.empty().getPackedAttestations(),
|
|
350
359
|
[], // no signers
|
|
360
|
+
Signature.empty().toViemSignature(),
|
|
351
361
|
`0x${'0'.repeat(64)}`, // 32 empty bytes
|
|
352
362
|
header.contentCommitment.blobsHash.toString(),
|
|
353
363
|
flags,
|
|
@@ -446,17 +456,19 @@ export class SequencerPublisher {
|
|
|
446
456
|
const logData = { ...block, reason };
|
|
447
457
|
this.log.debug(`Simulating invalidate block ${block.blockNumber}`, logData);
|
|
448
458
|
|
|
459
|
+
const attestationsAndSigners = new CommitteeAttestationsAndSigners(attestations).getPackedAttestations();
|
|
460
|
+
|
|
449
461
|
if (reason === 'invalid-attestation') {
|
|
450
462
|
return this.rollupContract.buildInvalidateBadAttestationRequest(
|
|
451
463
|
block.blockNumber,
|
|
452
|
-
|
|
464
|
+
attestationsAndSigners,
|
|
453
465
|
committee,
|
|
454
466
|
validationResult.invalidIndex,
|
|
455
467
|
);
|
|
456
468
|
} else if (reason === 'insufficient-attestations') {
|
|
457
469
|
return this.rollupContract.buildInvalidateInsufficientAttestationsRequest(
|
|
458
470
|
block.blockNumber,
|
|
459
|
-
|
|
471
|
+
attestationsAndSigners,
|
|
460
472
|
committee,
|
|
461
473
|
);
|
|
462
474
|
} else {
|
|
@@ -476,24 +488,22 @@ export class SequencerPublisher {
|
|
|
476
488
|
*/
|
|
477
489
|
public async validateBlockForSubmission(
|
|
478
490
|
block: L2Block,
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
attestations: [],
|
|
482
|
-
},
|
|
491
|
+
attestationsAndSigners: CommitteeAttestationsAndSigners,
|
|
492
|
+
attestationsAndSignersSignature: Signature,
|
|
483
493
|
options: { forcePendingBlockNumber?: number },
|
|
484
494
|
): Promise<bigint> {
|
|
485
495
|
const ts = BigInt((await this.l1TxUtils.getBlock()).timestamp + this.ethereumSlotDuration);
|
|
486
496
|
|
|
487
497
|
// If we have no attestations, we still need to provide the empty attestations
|
|
488
498
|
// so that the committee is recalculated correctly
|
|
489
|
-
const ignoreSignatures =
|
|
499
|
+
const ignoreSignatures = attestationsAndSigners.attestations.length === 0;
|
|
490
500
|
if (ignoreSignatures) {
|
|
491
501
|
const { committee } = await this.epochCache.getCommittee(block.header.globalVariables.slotNumber.toBigInt());
|
|
492
502
|
if (!committee) {
|
|
493
503
|
this.log.warn(`No committee found for slot ${block.header.globalVariables.slotNumber.toBigInt()}`);
|
|
494
504
|
throw new Error(`No committee found for slot ${block.header.globalVariables.slotNumber.toBigInt()}`);
|
|
495
505
|
}
|
|
496
|
-
|
|
506
|
+
attestationsAndSigners.attestations = committee.map(committeeMember =>
|
|
497
507
|
CommitteeAttestation.fromAddress(committeeMember),
|
|
498
508
|
);
|
|
499
509
|
}
|
|
@@ -501,23 +511,18 @@ export class SequencerPublisher {
|
|
|
501
511
|
const blobs = await Blob.getBlobsPerBlock(block.body.toBlobFields());
|
|
502
512
|
const blobInput = Blob.getPrefixedEthBlobCommitments(blobs);
|
|
503
513
|
|
|
504
|
-
const formattedAttestations = attestationData.attestations.map(attest => attest.toViem());
|
|
505
|
-
const signers = attestationData.attestations
|
|
506
|
-
.filter(attest => !attest.signature.isEmpty())
|
|
507
|
-
.map(attest => attest.address.toString());
|
|
508
|
-
|
|
509
514
|
const args = [
|
|
510
515
|
{
|
|
511
516
|
header: block.header.toPropose().toViem(),
|
|
512
517
|
archive: toHex(block.archive.root.toBuffer()),
|
|
513
518
|
stateReference: block.header.state.toViem(),
|
|
514
|
-
txHashes: block.body.txEffects.map(txEffect => txEffect.txHash.toString()),
|
|
515
519
|
oracleInput: {
|
|
516
520
|
feeAssetPriceModifier: 0n,
|
|
517
521
|
},
|
|
518
522
|
},
|
|
519
|
-
|
|
520
|
-
|
|
523
|
+
attestationsAndSigners.getPackedAttestations(),
|
|
524
|
+
attestationsAndSigners.getSigners().map(signer => signer.toString()),
|
|
525
|
+
attestationsAndSignersSignature.toViemSignature(),
|
|
521
526
|
blobInput,
|
|
522
527
|
] as const;
|
|
523
528
|
|
|
@@ -528,13 +533,14 @@ export class SequencerPublisher {
|
|
|
528
533
|
private async enqueueCastSignalHelper(
|
|
529
534
|
slotNumber: bigint,
|
|
530
535
|
timestamp: bigint,
|
|
531
|
-
signalType:
|
|
536
|
+
signalType: GovernanceSignalAction,
|
|
532
537
|
payload: EthAddress,
|
|
533
538
|
base: IEmpireBase,
|
|
534
539
|
signerAddress: EthAddress,
|
|
535
540
|
signer: (msg: TypedDataDefinition) => Promise<`0x${string}`>,
|
|
536
541
|
): Promise<boolean> {
|
|
537
|
-
if (this.
|
|
542
|
+
if (this.lastActions[signalType] && this.lastActions[signalType] === slotNumber) {
|
|
543
|
+
this.log.debug(`Skipping duplicate vote cast signal ${signalType} for slot ${slotNumber}`);
|
|
538
544
|
return false;
|
|
539
545
|
}
|
|
540
546
|
if (payload.equals(EthAddress.ZERO)) {
|
|
@@ -551,10 +557,9 @@ export class SequencerPublisher {
|
|
|
551
557
|
return false;
|
|
552
558
|
}
|
|
553
559
|
|
|
554
|
-
const cachedLastVote = this.
|
|
555
|
-
this.
|
|
556
|
-
|
|
557
|
-
const action = signalType === SignalType.GOVERNANCE ? 'governance-signal' : 'empire-slashing-signal';
|
|
560
|
+
const cachedLastVote = this.lastActions[signalType];
|
|
561
|
+
this.lastActions[signalType] = slotNumber;
|
|
562
|
+
const action = signalType;
|
|
558
563
|
|
|
559
564
|
const request = await base.createSignalRequestWithSignature(
|
|
560
565
|
payload.toString(),
|
|
@@ -597,7 +602,7 @@ export class SequencerPublisher {
|
|
|
597
602
|
`Signaling in [${action}] for ${payload} at slot ${slotNumber} in round ${round} failed`,
|
|
598
603
|
logData,
|
|
599
604
|
);
|
|
600
|
-
this.
|
|
605
|
+
this.lastActions[signalType] = cachedLastVote;
|
|
601
606
|
return false;
|
|
602
607
|
} else {
|
|
603
608
|
this.log.info(
|
|
@@ -627,7 +632,7 @@ export class SequencerPublisher {
|
|
|
627
632
|
return this.enqueueCastSignalHelper(
|
|
628
633
|
slotNumber,
|
|
629
634
|
timestamp,
|
|
630
|
-
|
|
635
|
+
'governance-signal',
|
|
631
636
|
governancePayload,
|
|
632
637
|
this.govProposerContract,
|
|
633
638
|
signerAddress,
|
|
@@ -661,7 +666,7 @@ export class SequencerPublisher {
|
|
|
661
666
|
await this.enqueueCastSignalHelper(
|
|
662
667
|
slotNumber,
|
|
663
668
|
timestamp,
|
|
664
|
-
|
|
669
|
+
'empire-slashing-signal',
|
|
665
670
|
action.payload,
|
|
666
671
|
this.slashingProposerContract,
|
|
667
672
|
signerAddress,
|
|
@@ -766,15 +771,12 @@ export class SequencerPublisher {
|
|
|
766
771
|
*/
|
|
767
772
|
public async enqueueProposeL2Block(
|
|
768
773
|
block: L2Block,
|
|
769
|
-
|
|
770
|
-
|
|
774
|
+
attestationsAndSigners: CommitteeAttestationsAndSigners,
|
|
775
|
+
attestationsAndSignersSignature: Signature,
|
|
771
776
|
opts: { txTimeoutAt?: Date; forcePendingBlockNumber?: number } = {},
|
|
772
777
|
): Promise<boolean> {
|
|
773
778
|
const proposedBlockHeader = block.header.toPropose();
|
|
774
779
|
|
|
775
|
-
const consensusPayload = ConsensusPayload.fromBlock(block);
|
|
776
|
-
const digest = getHashedSignaturePayload(consensusPayload, SignatureDomainSeparator.blockAttestation);
|
|
777
|
-
|
|
778
780
|
const blobs = await Blob.getBlobsPerBlock(block.body.toBlobFields());
|
|
779
781
|
const proposeTxArgs = {
|
|
780
782
|
header: proposedBlockHeader,
|
|
@@ -782,8 +784,8 @@ export class SequencerPublisher {
|
|
|
782
784
|
stateReference: block.header.state,
|
|
783
785
|
body: block.body.toBuffer(),
|
|
784
786
|
blobs,
|
|
785
|
-
|
|
786
|
-
|
|
787
|
+
attestationsAndSigners,
|
|
788
|
+
attestationsAndSignersSignature,
|
|
787
789
|
};
|
|
788
790
|
|
|
789
791
|
let ts: bigint;
|
|
@@ -793,9 +795,8 @@ export class SequencerPublisher {
|
|
|
793
795
|
// This means that we can avoid the simulation issues in later checks.
|
|
794
796
|
// By simulation issue, I mean the fact that the block.timestamp is equal to the last block, not the next, which
|
|
795
797
|
// make time consistency checks break.
|
|
796
|
-
const attestationData = { digest: digest.toBuffer(), attestations: attestations ?? [] };
|
|
797
798
|
// TODO(palla): Check whether we're validating twice, once here and once within addProposeTx, since we call simulateProposeTx in both places.
|
|
798
|
-
ts = await this.validateBlockForSubmission(block,
|
|
799
|
+
ts = await this.validateBlockForSubmission(block, attestationsAndSigners, attestationsAndSignersSignature, opts);
|
|
799
800
|
} catch (err: any) {
|
|
800
801
|
this.log.error(`Block validation failed. ${err instanceof Error ? err.message : 'No error message'}`, err, {
|
|
801
802
|
...block.getStats(),
|
|
@@ -842,16 +843,24 @@ export class SequencerPublisher {
|
|
|
842
843
|
}
|
|
843
844
|
|
|
844
845
|
private async simulateAndEnqueueRequest(
|
|
845
|
-
action:
|
|
846
|
+
action: Action,
|
|
846
847
|
request: L1TxRequest,
|
|
847
848
|
checkSuccess: (receipt: TransactionReceipt) => boolean | undefined,
|
|
848
849
|
slotNumber: bigint,
|
|
849
850
|
timestamp: bigint,
|
|
850
851
|
) {
|
|
851
852
|
const logData = { slotNumber, timestamp, gasLimit: undefined as bigint | undefined };
|
|
852
|
-
|
|
853
|
+
if (this.lastActions[action] && this.lastActions[action] === slotNumber) {
|
|
854
|
+
this.log.debug(`Skipping duplicate action ${action} for slot ${slotNumber}`);
|
|
855
|
+
return false;
|
|
856
|
+
}
|
|
853
857
|
|
|
854
|
-
this.
|
|
858
|
+
const cachedLastActionSlot = this.lastActions[action];
|
|
859
|
+
this.lastActions[action] = slotNumber;
|
|
860
|
+
|
|
861
|
+
this.log.debug(`Simulating ${action} for slot ${slotNumber}`, logData);
|
|
862
|
+
|
|
863
|
+
let gasUsed: bigint;
|
|
855
864
|
try {
|
|
856
865
|
({ gasUsed } = await this.l1TxUtils.simulate(request, { time: timestamp }, [], ErrorsAbi)); // TODO(palla/slash): Check the timestamp logic
|
|
857
866
|
this.log.verbose(`Simulation for ${action} succeeded`, { ...logData, request, gasUsed });
|
|
@@ -875,6 +884,7 @@ export class SequencerPublisher {
|
|
|
875
884
|
const success = result && result.receipt && result.receipt.status === 'success' && checkSuccess(result.receipt);
|
|
876
885
|
if (!success) {
|
|
877
886
|
this.log.warn(`Action ${action} at ${slotNumber} failed`, { ...result, ...logData });
|
|
887
|
+
this.lastActions[action] = cachedLastActionSlot;
|
|
878
888
|
} else {
|
|
879
889
|
this.log.info(`Action ${action} at ${slotNumber} succeeded`, { ...result, ...logData });
|
|
880
890
|
}
|
|
@@ -932,12 +942,7 @@ export class SequencerPublisher {
|
|
|
932
942
|
throw new Error('Failed to validate blobs');
|
|
933
943
|
});
|
|
934
944
|
|
|
935
|
-
const
|
|
936
|
-
const txHashes = encodedData.txHashes ? encodedData.txHashes.map(txHash => txHash.toString()) : [];
|
|
937
|
-
|
|
938
|
-
const signers = encodedData.attestations
|
|
939
|
-
?.filter(attest => !attest.signature.isEmpty())
|
|
940
|
-
.map(attest => attest.address.toString());
|
|
945
|
+
const signers = encodedData.attestationsAndSigners.getSigners().map(signer => signer.toString());
|
|
941
946
|
|
|
942
947
|
const args = [
|
|
943
948
|
{
|
|
@@ -948,10 +953,10 @@ export class SequencerPublisher {
|
|
|
948
953
|
// We are currently not modifying these. See #9963
|
|
949
954
|
feeAssetPriceModifier: 0n,
|
|
950
955
|
},
|
|
951
|
-
txHashes,
|
|
952
956
|
},
|
|
953
|
-
|
|
954
|
-
signers
|
|
957
|
+
encodedData.attestationsAndSigners.getPackedAttestations(),
|
|
958
|
+
signers,
|
|
959
|
+
encodedData.attestationsAndSignersSignature.toViemSignature(),
|
|
955
960
|
blobInput,
|
|
956
961
|
] as const;
|
|
957
962
|
|
|
@@ -972,13 +977,13 @@ export class SequencerPublisher {
|
|
|
972
977
|
readonly header: ViemHeader;
|
|
973
978
|
readonly archive: `0x${string}`;
|
|
974
979
|
readonly stateReference: ViemStateReference;
|
|
975
|
-
readonly txHashes: `0x${string}`[];
|
|
976
980
|
readonly oracleInput: {
|
|
977
981
|
readonly feeAssetPriceModifier: 0n;
|
|
978
982
|
};
|
|
979
983
|
},
|
|
980
984
|
ViemCommitteeAttestations,
|
|
981
|
-
`0x${string}`[],
|
|
985
|
+
`0x${string}`[], // Signers
|
|
986
|
+
ViemSignature,
|
|
982
987
|
`0x${string}`,
|
|
983
988
|
],
|
|
984
989
|
timestamp: bigint,
|
package/src/sequencer/metrics.ts
CHANGED
|
@@ -2,21 +2,19 @@ import type { EthAddress } from '@aztec/aztec.js';
|
|
|
2
2
|
import type { RollupContract } from '@aztec/ethereum';
|
|
3
3
|
import {
|
|
4
4
|
Attributes,
|
|
5
|
-
type BatchObservableResult,
|
|
6
5
|
type Gauge,
|
|
7
6
|
type Histogram,
|
|
8
7
|
type Meter,
|
|
9
8
|
Metrics,
|
|
10
|
-
type ObservableGauge,
|
|
11
9
|
type TelemetryClient,
|
|
12
10
|
type Tracer,
|
|
13
11
|
type UpDownCounter,
|
|
14
12
|
ValueType,
|
|
15
13
|
} from '@aztec/telemetry-client';
|
|
16
14
|
|
|
17
|
-
import { formatUnits } from 'viem';
|
|
15
|
+
import { type Hex, formatUnits } from 'viem';
|
|
18
16
|
|
|
19
|
-
import {
|
|
17
|
+
import type { SequencerState } from './utils.js';
|
|
20
18
|
|
|
21
19
|
export class SequencerMetrics {
|
|
22
20
|
public readonly tracer: Tracer;
|
|
@@ -26,9 +24,6 @@ export class SequencerMetrics {
|
|
|
26
24
|
private blockBuildDuration: Histogram;
|
|
27
25
|
private blockBuildManaPerSecond: Gauge;
|
|
28
26
|
private stateTransitionBufferDuration: Histogram;
|
|
29
|
-
private currentBlockNumber: Gauge;
|
|
30
|
-
private currentBlockSize: Gauge;
|
|
31
|
-
private blockBuilderInsertions: Histogram;
|
|
32
27
|
|
|
33
28
|
// these are gauges because for individual sequencers building a block is not something that happens often enough to warrant a histogram
|
|
34
29
|
private timeToCollectAttestations: Gauge;
|
|
@@ -36,18 +31,15 @@ export class SequencerMetrics {
|
|
|
36
31
|
private requiredAttestions: Gauge;
|
|
37
32
|
private collectedAttestions: Gauge;
|
|
38
33
|
|
|
39
|
-
private rewards:
|
|
34
|
+
private rewards: Gauge;
|
|
40
35
|
|
|
41
36
|
private slots: UpDownCounter;
|
|
42
37
|
private filledSlots: UpDownCounter;
|
|
43
|
-
private missedSlots: UpDownCounter;
|
|
44
38
|
|
|
45
39
|
private lastSeenSlot?: bigint;
|
|
46
40
|
|
|
47
41
|
constructor(
|
|
48
42
|
client: TelemetryClient,
|
|
49
|
-
getState: SequencerStateCallback,
|
|
50
|
-
private coinbase: EthAddress,
|
|
51
43
|
private rollup: RollupContract,
|
|
52
44
|
name = 'Sequencer',
|
|
53
45
|
) {
|
|
@@ -78,35 +70,7 @@ export class SequencerMetrics {
|
|
|
78
70
|
},
|
|
79
71
|
);
|
|
80
72
|
|
|
81
|
-
const currentState = this.meter.createObservableGauge(Metrics.SEQUENCER_CURRENT_STATE, {
|
|
82
|
-
description: 'Current state of the sequencer',
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
currentState.addCallback(observer => {
|
|
86
|
-
observer.observe(sequencerStateToNumber(getState()));
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
this.currentBlockNumber = this.meter.createGauge(Metrics.SEQUENCER_CURRENT_BLOCK_NUMBER, {
|
|
90
|
-
description: 'Current block number',
|
|
91
|
-
valueType: ValueType.INT,
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
this.currentBlockSize = this.meter.createGauge(Metrics.SEQUENCER_CURRENT_BLOCK_SIZE, {
|
|
95
|
-
description: 'Current block size',
|
|
96
|
-
valueType: ValueType.INT,
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
this.blockBuilderInsertions = this.meter.createHistogram(Metrics.SEQUENCER_BLOCK_BUILD_INSERTION_TIME, {
|
|
100
|
-
description: 'Timer for tree insertions performed by the block builder',
|
|
101
|
-
unit: 'us',
|
|
102
|
-
valueType: ValueType.INT,
|
|
103
|
-
});
|
|
104
|
-
|
|
105
73
|
// Init gauges and counters
|
|
106
|
-
this.setCurrentBlock(0, 0);
|
|
107
|
-
this.blockCounter.add(0, {
|
|
108
|
-
[Attributes.STATUS]: 'cancelled',
|
|
109
|
-
});
|
|
110
74
|
this.blockCounter.add(0, {
|
|
111
75
|
[Attributes.STATUS]: 'failed',
|
|
112
76
|
});
|
|
@@ -114,7 +78,7 @@ export class SequencerMetrics {
|
|
|
114
78
|
[Attributes.STATUS]: 'built',
|
|
115
79
|
});
|
|
116
80
|
|
|
117
|
-
this.rewards = this.meter.
|
|
81
|
+
this.rewards = this.meter.createGauge(Metrics.SEQUENCER_CURRENT_BLOCK_REWARDS, {
|
|
118
82
|
valueType: ValueType.DOUBLE,
|
|
119
83
|
description: 'The rewards earned',
|
|
120
84
|
});
|
|
@@ -124,16 +88,15 @@ export class SequencerMetrics {
|
|
|
124
88
|
description: 'The number of slots this sequencer was selected for',
|
|
125
89
|
});
|
|
126
90
|
|
|
91
|
+
/**
|
|
92
|
+
* NOTE: we do not track missed slots as a separate metric. That would be difficult to determine
|
|
93
|
+
* Instead, use a computed metric, `slots - filledSlots` to get the number of slots a sequencer has missed.
|
|
94
|
+
*/
|
|
127
95
|
this.filledSlots = this.meter.createUpDownCounter(Metrics.SEQUENCER_FILLED_SLOT_COUNT, {
|
|
128
96
|
valueType: ValueType.INT,
|
|
129
97
|
description: 'The number of slots this sequencer has filled',
|
|
130
98
|
});
|
|
131
99
|
|
|
132
|
-
this.missedSlots = this.meter.createUpDownCounter(Metrics.SEQUENCER_MISSED_SLOT_COUNT, {
|
|
133
|
-
valueType: ValueType.INT,
|
|
134
|
-
description: 'The number of slots this sequencer has missed to fill',
|
|
135
|
-
});
|
|
136
|
-
|
|
137
100
|
this.timeToCollectAttestations = this.meter.createGauge(Metrics.SEQUENCER_COLLECT_ATTESTATIONS_DURATION, {
|
|
138
101
|
description: 'The time spent collecting attestations from committee members',
|
|
139
102
|
unit: 'ms',
|
|
@@ -160,28 +123,6 @@ export class SequencerMetrics {
|
|
|
160
123
|
});
|
|
161
124
|
}
|
|
162
125
|
|
|
163
|
-
public setCoinbase(coinbase: EthAddress) {
|
|
164
|
-
this.coinbase = coinbase;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
public start() {
|
|
168
|
-
this.meter.addBatchObservableCallback(this.observe, [this.rewards]);
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
public stop() {
|
|
172
|
-
this.meter.removeBatchObservableCallback(this.observe, [this.rewards]);
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
private observe = async (observer: BatchObservableResult): Promise<void> => {
|
|
176
|
-
let rewards = 0n;
|
|
177
|
-
rewards = await this.rollup.getSequencerRewards(this.coinbase);
|
|
178
|
-
|
|
179
|
-
const fmt = parseFloat(formatUnits(rewards, 18));
|
|
180
|
-
observer.observe(this.rewards, fmt, {
|
|
181
|
-
[Attributes.COINBASE]: this.coinbase.toString(),
|
|
182
|
-
});
|
|
183
|
-
};
|
|
184
|
-
|
|
185
126
|
public recordRequiredAttestations(requiredAttestationsCount: number, allowanceMs: number) {
|
|
186
127
|
this.requiredAttestions.record(requiredAttestationsCount);
|
|
187
128
|
this.allowanceToCollectAttestations.record(Math.ceil(allowanceMs));
|
|
@@ -196,17 +137,6 @@ export class SequencerMetrics {
|
|
|
196
137
|
this.timeToCollectAttestations.record(Math.ceil(durationMs));
|
|
197
138
|
}
|
|
198
139
|
|
|
199
|
-
recordBlockBuilderTreeInsertions(timeUs: number) {
|
|
200
|
-
this.blockBuilderInsertions.record(Math.ceil(timeUs));
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
recordCancelledBlock() {
|
|
204
|
-
this.blockCounter.add(1, {
|
|
205
|
-
[Attributes.STATUS]: 'cancelled',
|
|
206
|
-
});
|
|
207
|
-
this.setCurrentBlock(0, 0);
|
|
208
|
-
}
|
|
209
|
-
|
|
210
140
|
recordBuiltBlock(buildDurationMs: number, totalMana: number) {
|
|
211
141
|
this.blockCounter.add(1, {
|
|
212
142
|
[Attributes.STATUS]: 'built',
|
|
@@ -219,11 +149,6 @@ export class SequencerMetrics {
|
|
|
219
149
|
this.blockCounter.add(1, {
|
|
220
150
|
[Attributes.STATUS]: 'failed',
|
|
221
151
|
});
|
|
222
|
-
this.setCurrentBlock(0, 0);
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
recordNewBlock(blockNumber: number, txCount: number) {
|
|
226
|
-
this.setCurrentBlock(blockNumber, txCount);
|
|
227
152
|
}
|
|
228
153
|
|
|
229
154
|
recordStateTransitionBufferMs(durationMs: number, state: SequencerState) {
|
|
@@ -232,36 +157,35 @@ export class SequencerMetrics {
|
|
|
232
157
|
});
|
|
233
158
|
}
|
|
234
159
|
|
|
235
|
-
|
|
160
|
+
incOpenSlot(slot: bigint, proposer: string) {
|
|
236
161
|
// sequencer went through the loop a second time. Noop
|
|
237
162
|
if (slot === this.lastSeenSlot) {
|
|
238
163
|
return;
|
|
239
164
|
}
|
|
240
165
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
});
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
if (typeof slot === 'bigint') {
|
|
248
|
-
this.slots.add(1, {
|
|
249
|
-
[Attributes.BLOCK_PROPOSER]: proposer,
|
|
250
|
-
});
|
|
251
|
-
}
|
|
166
|
+
this.slots.add(1, {
|
|
167
|
+
[Attributes.BLOCK_PROPOSER]: proposer,
|
|
168
|
+
});
|
|
252
169
|
|
|
253
170
|
this.lastSeenSlot = slot;
|
|
254
171
|
}
|
|
255
172
|
|
|
256
|
-
incFilledSlot(proposer: string) {
|
|
173
|
+
async incFilledSlot(proposer: string, coinbase: Hex | EthAddress | undefined): Promise<void> {
|
|
257
174
|
this.filledSlots.add(1, {
|
|
258
175
|
[Attributes.BLOCK_PROPOSER]: proposer,
|
|
259
176
|
});
|
|
260
177
|
this.lastSeenSlot = undefined;
|
|
261
|
-
}
|
|
262
178
|
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
179
|
+
if (coinbase) {
|
|
180
|
+
try {
|
|
181
|
+
const rewards = await this.rollup.getSequencerRewards(coinbase);
|
|
182
|
+
const fmt = parseFloat(formatUnits(rewards, 18));
|
|
183
|
+
this.rewards.record(fmt, {
|
|
184
|
+
[Attributes.COINBASE]: coinbase.toString(),
|
|
185
|
+
});
|
|
186
|
+
} catch {
|
|
187
|
+
// no-op
|
|
188
|
+
}
|
|
189
|
+
}
|
|
266
190
|
}
|
|
267
191
|
}
|