@aztec/sequencer-client 0.0.1-commit.03f7ef2 → 0.0.1-commit.0b941701
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 +5 -6
- package/dest/client/sequencer-client.d.ts.map +1 -1
- package/dest/config.d.ts +1 -1
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +6 -1
- package/dest/global_variable_builder/global_builder.d.ts +4 -4
- package/dest/global_variable_builder/global_builder.d.ts.map +1 -1
- package/dest/global_variable_builder/global_builder.js +13 -13
- package/dest/index.d.ts +2 -3
- package/dest/index.d.ts.map +1 -1
- package/dest/index.js +1 -2
- package/dest/publisher/sequencer-publisher-factory.d.ts +2 -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-metrics.js +15 -86
- package/dest/publisher/sequencer-publisher.d.ts +19 -18
- package/dest/publisher/sequencer-publisher.d.ts.map +1 -1
- package/dest/publisher/sequencer-publisher.js +443 -53
- package/dest/sequencer/checkpoint_proposal_job.d.ts +14 -9
- package/dest/sequencer/checkpoint_proposal_job.d.ts.map +1 -1
- package/dest/sequencer/checkpoint_proposal_job.js +565 -40
- package/dest/sequencer/checkpoint_voter.d.ts +3 -2
- package/dest/sequencer/checkpoint_voter.d.ts.map +1 -1
- package/dest/sequencer/checkpoint_voter.js +34 -10
- package/dest/sequencer/index.d.ts +1 -3
- package/dest/sequencer/index.d.ts.map +1 -1
- package/dest/sequencer/index.js +0 -2
- package/dest/sequencer/metrics.d.ts +3 -3
- package/dest/sequencer/metrics.d.ts.map +1 -1
- package/dest/sequencer/metrics.js +30 -121
- package/dest/sequencer/sequencer.d.ts +25 -15
- package/dest/sequencer/sequencer.d.ts.map +1 -1
- package/dest/sequencer/sequencer.js +486 -42
- package/dest/test/index.d.ts +2 -3
- package/dest/test/index.d.ts.map +1 -1
- package/dest/test/mock_checkpoint_builder.d.ts +23 -11
- package/dest/test/mock_checkpoint_builder.d.ts.map +1 -1
- package/dest/test/mock_checkpoint_builder.js +50 -7
- package/dest/test/utils.d.ts +13 -9
- package/dest/test/utils.d.ts.map +1 -1
- package/dest/test/utils.js +26 -17
- package/package.json +30 -28
- package/src/client/sequencer-client.ts +4 -5
- package/src/config.ts +5 -0
- package/src/global_variable_builder/global_builder.ts +13 -13
- package/src/index.ts +1 -9
- package/src/publisher/sequencer-publisher-factory.ts +1 -1
- package/src/publisher/sequencer-publisher-metrics.ts +14 -70
- package/src/publisher/sequencer-publisher.ts +87 -77
- package/src/sequencer/checkpoint_proposal_job.ts +201 -59
- package/src/sequencer/checkpoint_voter.ts +32 -7
- package/src/sequencer/index.ts +0 -2
- package/src/sequencer/metrics.ts +23 -131
- package/src/sequencer/sequencer.ts +124 -41
- package/src/test/index.ts +1 -2
- package/src/test/mock_checkpoint_builder.ts +92 -28
- package/src/test/utils.ts +55 -28
- package/dest/sequencer/block_builder.d.ts +0 -26
- package/dest/sequencer/block_builder.d.ts.map +0 -1
- package/dest/sequencer/block_builder.js +0 -129
- package/dest/sequencer/checkpoint_builder.d.ts +0 -63
- package/dest/sequencer/checkpoint_builder.d.ts.map +0 -1
- package/dest/sequencer/checkpoint_builder.js +0 -131
- package/dest/tx_validator/nullifier_cache.d.ts +0 -14
- package/dest/tx_validator/nullifier_cache.d.ts.map +0 -1
- package/dest/tx_validator/nullifier_cache.js +0 -24
- package/dest/tx_validator/tx_validator_factory.d.ts +0 -18
- package/dest/tx_validator/tx_validator_factory.d.ts.map +0 -1
- package/dest/tx_validator/tx_validator_factory.js +0 -53
- package/src/sequencer/block_builder.ts +0 -217
- package/src/sequencer/checkpoint_builder.ts +0 -217
- package/src/tx_validator/nullifier_cache.ts +0 -30
- package/src/tx_validator/tx_validator_factory.ts +0 -133
|
@@ -23,7 +23,7 @@ import { createPublicClient, fallback, http } from 'viem';
|
|
|
23
23
|
*/
|
|
24
24
|
export class GlobalVariableBuilder implements GlobalVariableBuilderInterface {
|
|
25
25
|
private log = createLogger('sequencer:global_variable_builder');
|
|
26
|
-
private
|
|
26
|
+
private currentMinFees: Promise<GasFees> = Promise.resolve(new GasFees(0, 0));
|
|
27
27
|
private currentL1BlockNumber: bigint | undefined = undefined;
|
|
28
28
|
|
|
29
29
|
private readonly rollupContract: RollupContract;
|
|
@@ -61,34 +61,34 @@ export class GlobalVariableBuilder implements GlobalVariableBuilderInterface {
|
|
|
61
61
|
}
|
|
62
62
|
|
|
63
63
|
/**
|
|
64
|
-
* Computes the "current"
|
|
65
|
-
* @returns
|
|
64
|
+
* Computes the "current" min fees, e.g., the price that you currently should pay to get include in the next block
|
|
65
|
+
* @returns Min fees for the next block
|
|
66
66
|
*/
|
|
67
|
-
private async
|
|
67
|
+
private async computeCurrentMinFees(): Promise<GasFees> {
|
|
68
68
|
// Since this might be called in the middle of a slot where a block might have been published,
|
|
69
69
|
// we need to fetch the last block written, and estimate the earliest timestamp for the next block.
|
|
70
70
|
// The timestamp of that last block will act as a lower bound for the next block.
|
|
71
71
|
|
|
72
|
-
const
|
|
72
|
+
const lastCheckpoint = await this.rollupContract.getPendingCheckpoint();
|
|
73
73
|
const earliestTimestamp = await this.rollupContract.getTimestampForSlot(
|
|
74
|
-
SlotNumber.fromBigInt(
|
|
74
|
+
SlotNumber.fromBigInt(BigInt(lastCheckpoint.slotNumber) + 1n),
|
|
75
75
|
);
|
|
76
76
|
const nextEthTimestamp = BigInt((await this.publicClient.getBlock()).timestamp + BigInt(this.ethereumSlotDuration));
|
|
77
77
|
const timestamp = earliestTimestamp > nextEthTimestamp ? earliestTimestamp : nextEthTimestamp;
|
|
78
78
|
|
|
79
|
-
return new GasFees(0, await this.rollupContract.
|
|
79
|
+
return new GasFees(0, await this.rollupContract.getManaMinFeeAt(timestamp, true));
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
-
public async
|
|
82
|
+
public async getCurrentMinFees(): Promise<GasFees> {
|
|
83
83
|
// Get the current block number
|
|
84
84
|
const blockNumber = await this.publicClient.getBlockNumber();
|
|
85
85
|
|
|
86
|
-
// If the L1 block number has changed then chain a new promise to get the current
|
|
86
|
+
// If the L1 block number has changed then chain a new promise to get the current min fees
|
|
87
87
|
if (this.currentL1BlockNumber === undefined || blockNumber > this.currentL1BlockNumber) {
|
|
88
88
|
this.currentL1BlockNumber = blockNumber;
|
|
89
|
-
this.
|
|
89
|
+
this.currentMinFees = this.currentMinFees.then(() => this.computeCurrentMinFees());
|
|
90
90
|
}
|
|
91
|
-
return this.
|
|
91
|
+
return this.currentMinFees;
|
|
92
92
|
}
|
|
93
93
|
|
|
94
94
|
/**
|
|
@@ -128,9 +128,9 @@ export class GlobalVariableBuilder implements GlobalVariableBuilderInterface {
|
|
|
128
128
|
l1GenesisTime: this.l1GenesisTime,
|
|
129
129
|
});
|
|
130
130
|
|
|
131
|
-
// We can skip much of the logic in
|
|
131
|
+
// We can skip much of the logic in getCurrentMinFees since it we already check that we are not within a slot elsewhere.
|
|
132
132
|
// TODO(palla/mbps): Can we use a cached value here?
|
|
133
|
-
const gasFees = new GasFees(0, await this.rollupContract.
|
|
133
|
+
const gasFees = new GasFees(0, await this.rollupContract.getManaMinFeeAt(timestamp, true));
|
|
134
134
|
|
|
135
135
|
return { chainId, version, slotNumber, timestamp, coinbase, feeRecipient, gasFees };
|
|
136
136
|
}
|
package/src/index.ts
CHANGED
|
@@ -1,15 +1,7 @@
|
|
|
1
1
|
export * from './client/index.js';
|
|
2
2
|
export * from './config.js';
|
|
3
3
|
export * from './publisher/index.js';
|
|
4
|
-
export {
|
|
5
|
-
FullNodeBlockBuilder as BlockBuilder,
|
|
6
|
-
FullNodeCheckpointsBuilder as CheckpointsBuilder,
|
|
7
|
-
CheckpointBuilder,
|
|
8
|
-
Sequencer,
|
|
9
|
-
SequencerState,
|
|
10
|
-
type SequencerEvents,
|
|
11
|
-
} from './sequencer/index.js';
|
|
12
|
-
export * from './tx_validator/tx_validator_factory.js';
|
|
4
|
+
export { Sequencer, SequencerState, type SequencerEvents } from './sequencer/index.js';
|
|
13
5
|
|
|
14
6
|
// Used by the node to simulate public parts of transactions. Should these be moved to a shared library?
|
|
15
7
|
// ISSUE(#9832)
|
|
@@ -33,7 +33,7 @@ export class SequencerPublisherFactory {
|
|
|
33
33
|
private deps: {
|
|
34
34
|
telemetry: TelemetryClient;
|
|
35
35
|
publisherManager: PublisherManager<L1TxUtilsWithBlobs>;
|
|
36
|
-
blobClient
|
|
36
|
+
blobClient: BlobClientInterface;
|
|
37
37
|
dateProvider: DateProvider;
|
|
38
38
|
epochCache: EpochCache;
|
|
39
39
|
rollupContract: RollupContract;
|
|
@@ -7,7 +7,6 @@ import {
|
|
|
7
7
|
Metrics,
|
|
8
8
|
type TelemetryClient,
|
|
9
9
|
type UpDownCounter,
|
|
10
|
-
ValueType,
|
|
11
10
|
} from '@aztec/telemetry-client';
|
|
12
11
|
|
|
13
12
|
import { formatEther } from 'viem/utils';
|
|
@@ -40,88 +39,33 @@ export class SequencerPublisherMetrics {
|
|
|
40
39
|
) {
|
|
41
40
|
const meter = client.getMeter(name);
|
|
42
41
|
|
|
43
|
-
this.gasPrice = meter.createHistogram(Metrics.L1_PUBLISHER_GAS_PRICE
|
|
44
|
-
description: 'The gas price used for transactions',
|
|
45
|
-
unit: 'gwei',
|
|
46
|
-
valueType: ValueType.DOUBLE,
|
|
47
|
-
});
|
|
42
|
+
this.gasPrice = meter.createHistogram(Metrics.L1_PUBLISHER_GAS_PRICE);
|
|
48
43
|
|
|
49
|
-
this.txCount = meter.createUpDownCounter(Metrics.L1_PUBLISHER_TX_COUNT
|
|
50
|
-
description: 'The number of transactions processed',
|
|
51
|
-
});
|
|
44
|
+
this.txCount = meter.createUpDownCounter(Metrics.L1_PUBLISHER_TX_COUNT);
|
|
52
45
|
|
|
53
|
-
this.txDuration = meter.createHistogram(Metrics.L1_PUBLISHER_TX_DURATION
|
|
54
|
-
description: 'The duration of transaction processing',
|
|
55
|
-
unit: 'ms',
|
|
56
|
-
valueType: ValueType.INT,
|
|
57
|
-
});
|
|
46
|
+
this.txDuration = meter.createHistogram(Metrics.L1_PUBLISHER_TX_DURATION);
|
|
58
47
|
|
|
59
|
-
this.txGas = meter.createHistogram(Metrics.L1_PUBLISHER_TX_GAS
|
|
60
|
-
description: 'The gas consumed by transactions',
|
|
61
|
-
unit: 'gas',
|
|
62
|
-
valueType: ValueType.INT,
|
|
63
|
-
});
|
|
48
|
+
this.txGas = meter.createHistogram(Metrics.L1_PUBLISHER_TX_GAS);
|
|
64
49
|
|
|
65
|
-
this.txCalldataSize = meter.createHistogram(Metrics.L1_PUBLISHER_TX_CALLDATA_SIZE
|
|
66
|
-
description: 'The size of the calldata in transactions',
|
|
67
|
-
unit: 'By',
|
|
68
|
-
valueType: ValueType.INT,
|
|
69
|
-
});
|
|
50
|
+
this.txCalldataSize = meter.createHistogram(Metrics.L1_PUBLISHER_TX_CALLDATA_SIZE);
|
|
70
51
|
|
|
71
|
-
this.txCalldataGas = meter.createHistogram(Metrics.L1_PUBLISHER_TX_CALLDATA_GAS
|
|
72
|
-
description: 'The gas consumed by the calldata in transactions',
|
|
73
|
-
unit: 'gas',
|
|
74
|
-
valueType: ValueType.INT,
|
|
75
|
-
});
|
|
52
|
+
this.txCalldataGas = meter.createHistogram(Metrics.L1_PUBLISHER_TX_CALLDATA_GAS);
|
|
76
53
|
|
|
77
|
-
this.txBlobDataGasUsed = meter.createHistogram(Metrics.L1_PUBLISHER_TX_BLOBDATA_GAS_USED
|
|
78
|
-
description: 'The amount of blob gas used in transactions',
|
|
79
|
-
unit: 'gas',
|
|
80
|
-
valueType: ValueType.INT,
|
|
81
|
-
});
|
|
54
|
+
this.txBlobDataGasUsed = meter.createHistogram(Metrics.L1_PUBLISHER_TX_BLOBDATA_GAS_USED);
|
|
82
55
|
|
|
83
|
-
this.txBlobDataGasCost = meter.createHistogram(Metrics.L1_PUBLISHER_TX_BLOBDATA_GAS_COST
|
|
84
|
-
description: 'The gas cost of blobs in transactions',
|
|
85
|
-
unit: 'gwei',
|
|
86
|
-
valueType: ValueType.INT,
|
|
87
|
-
});
|
|
56
|
+
this.txBlobDataGasCost = meter.createHistogram(Metrics.L1_PUBLISHER_TX_BLOBDATA_GAS_COST);
|
|
88
57
|
|
|
89
|
-
this.blobCountHistogram = meter.createHistogram(Metrics.L1_PUBLISHER_BLOB_COUNT
|
|
90
|
-
description: 'Number of blobs in L1 transactions',
|
|
91
|
-
unit: 'blobs',
|
|
92
|
-
valueType: ValueType.INT,
|
|
93
|
-
});
|
|
58
|
+
this.blobCountHistogram = meter.createHistogram(Metrics.L1_PUBLISHER_BLOB_COUNT);
|
|
94
59
|
|
|
95
|
-
this.blobInclusionBlocksHistogram = meter.createHistogram(Metrics.L1_PUBLISHER_BLOB_INCLUSION_BLOCKS
|
|
96
|
-
description: 'Number of L1 blocks between blob tx submission and inclusion',
|
|
97
|
-
unit: 'blocks',
|
|
98
|
-
valueType: ValueType.INT,
|
|
99
|
-
});
|
|
60
|
+
this.blobInclusionBlocksHistogram = meter.createHistogram(Metrics.L1_PUBLISHER_BLOB_INCLUSION_BLOCKS);
|
|
100
61
|
|
|
101
|
-
this.blobTxSuccessCounter = meter.createUpDownCounter(Metrics.L1_PUBLISHER_BLOB_TX_SUCCESS
|
|
102
|
-
description: 'Number of successful L1 transactions with blobs',
|
|
103
|
-
});
|
|
62
|
+
this.blobTxSuccessCounter = meter.createUpDownCounter(Metrics.L1_PUBLISHER_BLOB_TX_SUCCESS);
|
|
104
63
|
|
|
105
|
-
this.blobTxFailureCounter = meter.createUpDownCounter(Metrics.L1_PUBLISHER_BLOB_TX_FAILURE
|
|
106
|
-
description: 'Number of failed L1 transactions with blobs',
|
|
107
|
-
});
|
|
64
|
+
this.blobTxFailureCounter = meter.createUpDownCounter(Metrics.L1_PUBLISHER_BLOB_TX_FAILURE);
|
|
108
65
|
|
|
109
|
-
this.txTotalFee = meter.createHistogram(Metrics.L1_PUBLISHER_TX_TOTAL_FEE
|
|
110
|
-
description: 'How much L1 tx costs',
|
|
111
|
-
unit: 'eth',
|
|
112
|
-
valueType: ValueType.DOUBLE,
|
|
113
|
-
advice: {
|
|
114
|
-
explicitBucketBoundaries: [
|
|
115
|
-
0.001, 0.002, 0.004, 0.008, 0.01, 0.02, 0.04, 0.08, 0.1, 0.2, 0.4, 0.8, 1, 1.2, 1.4, 1.8, 2,
|
|
116
|
-
],
|
|
117
|
-
},
|
|
118
|
-
});
|
|
66
|
+
this.txTotalFee = meter.createHistogram(Metrics.L1_PUBLISHER_TX_TOTAL_FEE);
|
|
119
67
|
|
|
120
|
-
this.senderBalance = meter.createGauge(Metrics.L1_PUBLISHER_BALANCE
|
|
121
|
-
unit: 'eth',
|
|
122
|
-
description: 'The balance of the sender address',
|
|
123
|
-
valueType: ValueType.DOUBLE,
|
|
124
|
-
});
|
|
68
|
+
this.senderBalance = meter.createGauge(Metrics.L1_PUBLISHER_BALANCE);
|
|
125
69
|
}
|
|
126
70
|
|
|
127
71
|
recordFailedTx(txType: L1TxType) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { BlobClientInterface } from '@aztec/blob-client/client';
|
|
2
2
|
import { Blob, getBlobsPerL1Block, getPrefixedEthBlobCommitments } from '@aztec/blob-lib';
|
|
3
3
|
import type { EpochCache } from '@aztec/epoch-cache';
|
|
4
4
|
import type { L1ContractsConfig } from '@aztec/ethereum/config';
|
|
@@ -25,7 +25,7 @@ import type { L1TxUtilsWithBlobs } from '@aztec/ethereum/l1-tx-utils-with-blobs'
|
|
|
25
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 {
|
|
28
|
+
import { CheckpointNumber, SlotNumber } from '@aztec/foundation/branded-types';
|
|
29
29
|
import { pick } from '@aztec/foundation/collection';
|
|
30
30
|
import type { Fr } from '@aztec/foundation/curves/bn254';
|
|
31
31
|
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
@@ -35,12 +35,12 @@ import { bufferToHex } from '@aztec/foundation/string';
|
|
|
35
35
|
import { DateProvider, Timer } from '@aztec/foundation/timer';
|
|
36
36
|
import { EmpireBaseAbi, ErrorsAbi, RollupAbi } from '@aztec/l1-artifacts';
|
|
37
37
|
import { type ProposerSlashAction, encodeSlashConsensusVotes } from '@aztec/slasher';
|
|
38
|
-
import { CommitteeAttestationsAndSigners, type
|
|
38
|
+
import { CommitteeAttestationsAndSigners, type ValidateCheckpointResult } from '@aztec/stdlib/block';
|
|
39
39
|
import type { Checkpoint } from '@aztec/stdlib/checkpoint';
|
|
40
40
|
import { SlashFactoryContract } from '@aztec/stdlib/l1-contracts';
|
|
41
41
|
import type { CheckpointHeader } from '@aztec/stdlib/rollup';
|
|
42
42
|
import type { L1PublishCheckpointStats } from '@aztec/stdlib/stats';
|
|
43
|
-
import { type TelemetryClient, getTelemetryClient } from '@aztec/telemetry-client';
|
|
43
|
+
import { type TelemetryClient, type Tracer, getTelemetryClient, trackSpan } from '@aztec/telemetry-client';
|
|
44
44
|
|
|
45
45
|
import { type StateOverride, type TransactionReceipt, type TypedDataDefinition, encodeFunctionData, toHex } from 'viem';
|
|
46
46
|
|
|
@@ -80,12 +80,12 @@ type GovernanceSignalAction = Extract<Action, 'governance-signal' | 'empire-slas
|
|
|
80
80
|
// Sorting for actions such that invalidations go before proposals, and proposals go before votes
|
|
81
81
|
export const compareActions = (a: Action, b: Action) => Actions.indexOf(a) - Actions.indexOf(b);
|
|
82
82
|
|
|
83
|
-
export type
|
|
83
|
+
export type InvalidateCheckpointRequest = {
|
|
84
84
|
request: L1TxRequest;
|
|
85
85
|
reason: 'invalid-attestation' | 'insufficient-attestations';
|
|
86
86
|
gasUsed: bigint;
|
|
87
|
-
|
|
88
|
-
|
|
87
|
+
checkpointNumber: CheckpointNumber;
|
|
88
|
+
forcePendingCheckpointNumber: CheckpointNumber;
|
|
89
89
|
};
|
|
90
90
|
|
|
91
91
|
interface RequestWithExpiry {
|
|
@@ -139,13 +139,15 @@ export class SequencerPublisher {
|
|
|
139
139
|
public slashingProposerContract: EmpireSlashingProposerContract | TallySlashingProposerContract | undefined;
|
|
140
140
|
public slashFactoryContract: SlashFactoryContract;
|
|
141
141
|
|
|
142
|
+
public readonly tracer: Tracer;
|
|
143
|
+
|
|
142
144
|
protected requests: RequestWithExpiry[] = [];
|
|
143
145
|
|
|
144
146
|
constructor(
|
|
145
147
|
private config: TxSenderConfig & PublisherConfig & Pick<L1ContractsConfig, 'ethereumSlotDuration'>,
|
|
146
148
|
deps: {
|
|
147
149
|
telemetry?: TelemetryClient;
|
|
148
|
-
blobClient
|
|
150
|
+
blobClient: BlobClientInterface;
|
|
149
151
|
l1TxUtils: L1TxUtilsWithBlobs;
|
|
150
152
|
rollupContract: RollupContract;
|
|
151
153
|
slashingProposerContract: EmpireSlashingProposerContract | TallySlashingProposerContract | undefined;
|
|
@@ -163,11 +165,11 @@ export class SequencerPublisher {
|
|
|
163
165
|
this.epochCache = deps.epochCache;
|
|
164
166
|
this.lastActions = deps.lastActions;
|
|
165
167
|
|
|
166
|
-
this.blobClient =
|
|
167
|
-
deps.blobClient ?? createBlobClient(config, { logger: createLogger('sequencer:blob-client:client') });
|
|
168
|
+
this.blobClient = deps.blobClient;
|
|
168
169
|
|
|
169
170
|
const telemetry = deps.telemetry ?? getTelemetryClient();
|
|
170
171
|
this.metrics = deps.metrics ?? new SequencerPublisherMetrics(telemetry, 'SequencerPublisher');
|
|
172
|
+
this.tracer = telemetry.getTracer('SequencerPublisher');
|
|
171
173
|
this.l1TxUtils = deps.l1TxUtils;
|
|
172
174
|
|
|
173
175
|
this.rollupContract = deps.rollupContract;
|
|
@@ -297,6 +299,7 @@ export class SequencerPublisher {
|
|
|
297
299
|
* - a receipt and errorMsg if it failed on L1
|
|
298
300
|
* - undefined if no valid requests are found OR the tx failed to send.
|
|
299
301
|
*/
|
|
302
|
+
@trackSpan('SequencerPublisher.sendRequests')
|
|
300
303
|
public async sendRequests() {
|
|
301
304
|
const requestsToProcess = [...this.requests];
|
|
302
305
|
this.requests = [];
|
|
@@ -414,17 +417,14 @@ export class SequencerPublisher {
|
|
|
414
417
|
public canProposeAtNextEthBlock(
|
|
415
418
|
tipArchive: Fr,
|
|
416
419
|
msgSender: EthAddress,
|
|
417
|
-
opts: {
|
|
420
|
+
opts: { forcePendingCheckpointNumber?: CheckpointNumber } = {},
|
|
418
421
|
) {
|
|
419
422
|
// TODO: #14291 - should loop through multiple keys to check if any of them can propose
|
|
420
423
|
const ignoredErrors = ['SlotAlreadyInChain', 'InvalidProposer', 'InvalidArchive'];
|
|
421
424
|
|
|
422
425
|
return this.rollupContract
|
|
423
426
|
.canProposeAtNextEthBlock(tipArchive.toBuffer(), msgSender.toString(), Number(this.ethereumSlotDuration), {
|
|
424
|
-
forcePendingCheckpointNumber:
|
|
425
|
-
opts.forcePendingBlockNumber !== undefined
|
|
426
|
-
? CheckpointNumber.fromBlockNumber(opts.forcePendingBlockNumber)
|
|
427
|
-
: undefined,
|
|
427
|
+
forcePendingCheckpointNumber: opts.forcePendingCheckpointNumber,
|
|
428
428
|
})
|
|
429
429
|
.catch(err => {
|
|
430
430
|
if (err instanceof FormattedViemError && ignoredErrors.find(e => err.message.includes(e))) {
|
|
@@ -443,10 +443,11 @@ export class SequencerPublisher {
|
|
|
443
443
|
* It will throw if the block header is invalid.
|
|
444
444
|
* @param header - The block header to validate
|
|
445
445
|
*/
|
|
446
|
+
@trackSpan('SequencerPublisher.validateBlockHeader')
|
|
446
447
|
public async validateBlockHeader(
|
|
447
448
|
header: CheckpointHeader,
|
|
448
|
-
opts?: {
|
|
449
|
-
) {
|
|
449
|
+
opts?: { forcePendingCheckpointNumber: CheckpointNumber | undefined },
|
|
450
|
+
): Promise<void> {
|
|
450
451
|
const flags = { ignoreDA: true, ignoreSignatures: true };
|
|
451
452
|
|
|
452
453
|
const args = [
|
|
@@ -455,17 +456,13 @@ export class SequencerPublisher {
|
|
|
455
456
|
[], // no signers
|
|
456
457
|
Signature.empty().toViemSignature(),
|
|
457
458
|
`0x${'0'.repeat(64)}`, // 32 empty bytes
|
|
458
|
-
header.
|
|
459
|
+
header.blobsHash.toString(),
|
|
459
460
|
flags,
|
|
460
461
|
] as const;
|
|
461
462
|
|
|
462
463
|
const ts = BigInt((await this.l1TxUtils.getBlock()).timestamp + this.ethereumSlotDuration);
|
|
463
|
-
const optsForcePendingCheckpointNumber =
|
|
464
|
-
opts?.forcePendingBlockNumber !== undefined
|
|
465
|
-
? CheckpointNumber.fromBlockNumber(opts.forcePendingBlockNumber)
|
|
466
|
-
: undefined;
|
|
467
464
|
const stateOverrides = await this.rollupContract.makePendingCheckpointNumberOverride(
|
|
468
|
-
|
|
465
|
+
opts?.forcePendingCheckpointNumber,
|
|
469
466
|
);
|
|
470
467
|
let balance = 0n;
|
|
471
468
|
if (this.config.fishermanMode) {
|
|
@@ -493,77 +490,90 @@ export class SequencerPublisher {
|
|
|
493
490
|
}
|
|
494
491
|
|
|
495
492
|
/**
|
|
496
|
-
* Simulate making a call to invalidate a
|
|
497
|
-
* @param
|
|
493
|
+
* Simulate making a call to invalidate a checkpoint with invalid attestations. Returns undefined if no need to invalidate.
|
|
494
|
+
* @param validationResult - The validation result indicating which checkpoint to invalidate (as returned by the archiver)
|
|
498
495
|
*/
|
|
499
|
-
public async
|
|
500
|
-
validationResult:
|
|
501
|
-
): Promise<
|
|
496
|
+
public async simulateInvalidateCheckpoint(
|
|
497
|
+
validationResult: ValidateCheckpointResult,
|
|
498
|
+
): Promise<InvalidateCheckpointRequest | undefined> {
|
|
502
499
|
if (validationResult.valid) {
|
|
503
500
|
return undefined;
|
|
504
501
|
}
|
|
505
502
|
|
|
506
|
-
const { reason,
|
|
507
|
-
const
|
|
508
|
-
const logData = { ...
|
|
503
|
+
const { reason, checkpoint } = validationResult;
|
|
504
|
+
const checkpointNumber = checkpoint.checkpointNumber;
|
|
505
|
+
const logData = { ...checkpoint, reason };
|
|
509
506
|
|
|
510
|
-
const
|
|
511
|
-
if (
|
|
507
|
+
const currentCheckpointNumber = await this.rollupContract.getCheckpointNumber();
|
|
508
|
+
if (currentCheckpointNumber < checkpointNumber) {
|
|
512
509
|
this.log.verbose(
|
|
513
|
-
`Skipping
|
|
514
|
-
{
|
|
510
|
+
`Skipping checkpoint ${checkpointNumber} invalidation since it has already been removed from the pending chain`,
|
|
511
|
+
{ currentCheckpointNumber, ...logData },
|
|
515
512
|
);
|
|
516
513
|
return undefined;
|
|
517
514
|
}
|
|
518
515
|
|
|
519
|
-
const request = this.
|
|
520
|
-
this.log.debug(`Simulating invalidate
|
|
516
|
+
const request = this.buildInvalidateCheckpointRequest(validationResult);
|
|
517
|
+
this.log.debug(`Simulating invalidate checkpoint ${checkpointNumber}`, { ...logData, request });
|
|
521
518
|
|
|
522
519
|
try {
|
|
523
520
|
const { gasUsed } = await this.l1TxUtils.simulate(request, undefined, undefined, ErrorsAbi);
|
|
524
|
-
this.log.verbose(`Simulation for invalidate
|
|
521
|
+
this.log.verbose(`Simulation for invalidate checkpoint ${checkpointNumber} succeeded`, {
|
|
522
|
+
...logData,
|
|
523
|
+
request,
|
|
524
|
+
gasUsed,
|
|
525
|
+
});
|
|
525
526
|
|
|
526
|
-
return {
|
|
527
|
+
return {
|
|
528
|
+
request,
|
|
529
|
+
gasUsed,
|
|
530
|
+
checkpointNumber,
|
|
531
|
+
forcePendingCheckpointNumber: CheckpointNumber(checkpointNumber - 1),
|
|
532
|
+
reason,
|
|
533
|
+
};
|
|
527
534
|
} catch (err) {
|
|
528
535
|
const viemError = formatViemError(err);
|
|
529
536
|
|
|
530
|
-
// If the error is due to the
|
|
531
|
-
// we can safely ignore it and return undefined so we go ahead with
|
|
537
|
+
// If the error is due to the checkpoint not being in the pending chain, and it was indeed removed by someone else,
|
|
538
|
+
// we can safely ignore it and return undefined so we go ahead with checkpoint building.
|
|
532
539
|
if (viemError.message?.includes('Rollup__BlockNotInPendingChain')) {
|
|
533
540
|
this.log.verbose(
|
|
534
|
-
`Simulation for invalidate
|
|
541
|
+
`Simulation for invalidate checkpoint ${checkpointNumber} failed due to checkpoint not being in pending chain`,
|
|
535
542
|
{ ...logData, request, error: viemError.message },
|
|
536
543
|
);
|
|
537
|
-
const
|
|
538
|
-
if (
|
|
539
|
-
this.log.verbose(`
|
|
544
|
+
const latestPendingCheckpointNumber = await this.rollupContract.getCheckpointNumber();
|
|
545
|
+
if (latestPendingCheckpointNumber < checkpointNumber) {
|
|
546
|
+
this.log.verbose(`Checkpoint ${checkpointNumber} has already been invalidated`, { ...logData });
|
|
540
547
|
return undefined;
|
|
541
548
|
} else {
|
|
542
549
|
this.log.error(
|
|
543
|
-
`Simulation for invalidate ${
|
|
550
|
+
`Simulation for invalidate checkpoint ${checkpointNumber} failed and it is still in pending chain`,
|
|
544
551
|
viemError,
|
|
545
552
|
logData,
|
|
546
553
|
);
|
|
547
|
-
throw new Error(
|
|
548
|
-
|
|
549
|
-
|
|
554
|
+
throw new Error(
|
|
555
|
+
`Failed to simulate invalidate checkpoint ${checkpointNumber} while it is still in pending chain`,
|
|
556
|
+
{
|
|
557
|
+
cause: viemError,
|
|
558
|
+
},
|
|
559
|
+
);
|
|
550
560
|
}
|
|
551
561
|
}
|
|
552
562
|
|
|
553
|
-
// Otherwise, throw. We cannot build the next
|
|
554
|
-
this.log.error(`Simulation for invalidate
|
|
555
|
-
throw new Error(`Failed to simulate invalidate
|
|
563
|
+
// Otherwise, throw. We cannot build the next checkpoint if we cannot invalidate the previous one.
|
|
564
|
+
this.log.error(`Simulation for invalidate checkpoint ${checkpointNumber} failed`, viemError, logData);
|
|
565
|
+
throw new Error(`Failed to simulate invalidate checkpoint ${checkpointNumber}`, { cause: viemError });
|
|
556
566
|
}
|
|
557
567
|
}
|
|
558
568
|
|
|
559
|
-
private
|
|
569
|
+
private buildInvalidateCheckpointRequest(validationResult: ValidateCheckpointResult) {
|
|
560
570
|
if (validationResult.valid) {
|
|
561
|
-
throw new Error('Cannot invalidate a valid
|
|
571
|
+
throw new Error('Cannot invalidate a valid checkpoint');
|
|
562
572
|
}
|
|
563
573
|
|
|
564
|
-
const {
|
|
565
|
-
const logData = { ...
|
|
566
|
-
this.log.debug(`
|
|
574
|
+
const { checkpoint, committee, reason } = validationResult;
|
|
575
|
+
const logData = { ...checkpoint, reason };
|
|
576
|
+
this.log.debug(`Building invalidate checkpoint ${checkpoint.checkpointNumber} request`, logData);
|
|
567
577
|
|
|
568
578
|
const attestationsAndSigners = new CommitteeAttestationsAndSigners(
|
|
569
579
|
validationResult.attestations,
|
|
@@ -571,14 +581,14 @@ export class SequencerPublisher {
|
|
|
571
581
|
|
|
572
582
|
if (reason === 'invalid-attestation') {
|
|
573
583
|
return this.rollupContract.buildInvalidateBadAttestationRequest(
|
|
574
|
-
|
|
584
|
+
checkpoint.checkpointNumber,
|
|
575
585
|
attestationsAndSigners,
|
|
576
586
|
committee,
|
|
577
587
|
validationResult.invalidIndex,
|
|
578
588
|
);
|
|
579
589
|
} else if (reason === 'insufficient-attestations') {
|
|
580
590
|
return this.rollupContract.buildInvalidateInsufficientAttestationsRequest(
|
|
581
|
-
|
|
591
|
+
checkpoint.checkpointNumber,
|
|
582
592
|
attestationsAndSigners,
|
|
583
593
|
committee,
|
|
584
594
|
);
|
|
@@ -589,11 +599,12 @@ export class SequencerPublisher {
|
|
|
589
599
|
}
|
|
590
600
|
|
|
591
601
|
/** Simulates `propose` to make sure that the checkpoint is valid for submission */
|
|
602
|
+
@trackSpan('SequencerPublisher.validateCheckpointForSubmission')
|
|
592
603
|
public async validateCheckpointForSubmission(
|
|
593
604
|
checkpoint: Checkpoint,
|
|
594
605
|
attestationsAndSigners: CommitteeAttestationsAndSigners,
|
|
595
606
|
attestationsAndSignersSignature: Signature,
|
|
596
|
-
options: {
|
|
607
|
+
options: { forcePendingCheckpointNumber?: CheckpointNumber },
|
|
597
608
|
): Promise<bigint> {
|
|
598
609
|
const ts = BigInt((await this.l1TxUtils.getBlock()).timestamp + this.ethereumSlotDuration);
|
|
599
610
|
|
|
@@ -892,7 +903,7 @@ export class SequencerPublisher {
|
|
|
892
903
|
checkpoint: Checkpoint,
|
|
893
904
|
attestationsAndSigners: CommitteeAttestationsAndSigners,
|
|
894
905
|
attestationsAndSignersSignature: Signature,
|
|
895
|
-
opts: { txTimeoutAt?: Date;
|
|
906
|
+
opts: { txTimeoutAt?: Date; forcePendingCheckpointNumber?: CheckpointNumber } = {},
|
|
896
907
|
): Promise<void> {
|
|
897
908
|
const checkpointHeader = checkpoint.header;
|
|
898
909
|
|
|
@@ -925,7 +936,7 @@ export class SequencerPublisher {
|
|
|
925
936
|
this.log.error(`Checkpoint validation failed. ${err instanceof Error ? err.message : 'No error message'}`, err, {
|
|
926
937
|
...checkpoint.getStats(),
|
|
927
938
|
slotNumber: checkpoint.header.slotNumber,
|
|
928
|
-
|
|
939
|
+
forcePendingCheckpointNumber: opts.forcePendingCheckpointNumber,
|
|
929
940
|
});
|
|
930
941
|
throw err;
|
|
931
942
|
}
|
|
@@ -934,7 +945,10 @@ export class SequencerPublisher {
|
|
|
934
945
|
await this.addProposeTx(checkpoint, proposeTxArgs, opts, ts);
|
|
935
946
|
}
|
|
936
947
|
|
|
937
|
-
public
|
|
948
|
+
public enqueueInvalidateCheckpoint(
|
|
949
|
+
request: InvalidateCheckpointRequest | undefined,
|
|
950
|
+
opts: { txTimeoutAt?: Date } = {},
|
|
951
|
+
) {
|
|
938
952
|
if (!request) {
|
|
939
953
|
return;
|
|
940
954
|
}
|
|
@@ -942,9 +956,9 @@ export class SequencerPublisher {
|
|
|
942
956
|
// We issued the simulation against the rollup contract, so we need to account for the overhead of the multicall3
|
|
943
957
|
const gasLimit = this.l1TxUtils.bumpGasLimit(BigInt(Math.ceil((Number(request.gasUsed) * 64) / 63)));
|
|
944
958
|
|
|
945
|
-
const { gasUsed,
|
|
946
|
-
const logData = { gasUsed,
|
|
947
|
-
this.log.verbose(`Enqueuing invalidate
|
|
959
|
+
const { gasUsed, checkpointNumber } = request;
|
|
960
|
+
const logData = { gasUsed, checkpointNumber, gasLimit, opts };
|
|
961
|
+
this.log.verbose(`Enqueuing invalidate checkpoint request`, logData);
|
|
948
962
|
this.addRequest({
|
|
949
963
|
action: `invalidate-by-${request.reason}`,
|
|
950
964
|
request: request.request,
|
|
@@ -957,9 +971,9 @@ export class SequencerPublisher {
|
|
|
957
971
|
result.receipt.status === 'success' &&
|
|
958
972
|
tryExtractEvent(result.receipt.logs, this.rollupContract.address, RollupAbi, 'CheckpointInvalidated');
|
|
959
973
|
if (!success) {
|
|
960
|
-
this.log.warn(`Invalidate
|
|
974
|
+
this.log.warn(`Invalidate checkpoint ${request.checkpointNumber} failed`, { ...result, ...logData });
|
|
961
975
|
} else {
|
|
962
|
-
this.log.info(`Invalidate
|
|
976
|
+
this.log.info(`Invalidate checkpoint ${request.checkpointNumber} succeeded`, { ...result, ...logData });
|
|
963
977
|
}
|
|
964
978
|
return !!success;
|
|
965
979
|
},
|
|
@@ -1038,7 +1052,7 @@ export class SequencerPublisher {
|
|
|
1038
1052
|
private async prepareProposeTx(
|
|
1039
1053
|
encodedData: L1ProcessArgs,
|
|
1040
1054
|
timestamp: bigint,
|
|
1041
|
-
options: {
|
|
1055
|
+
options: { forcePendingCheckpointNumber?: CheckpointNumber },
|
|
1042
1056
|
) {
|
|
1043
1057
|
const kzg = Blob.getViemKzgInstance();
|
|
1044
1058
|
const blobInput = getPrefixedEthBlobCommitments(encodedData.blobs);
|
|
@@ -1119,7 +1133,7 @@ export class SequencerPublisher {
|
|
|
1119
1133
|
`0x${string}`,
|
|
1120
1134
|
],
|
|
1121
1135
|
timestamp: bigint,
|
|
1122
|
-
options: {
|
|
1136
|
+
options: { forcePendingCheckpointNumber?: CheckpointNumber },
|
|
1123
1137
|
) {
|
|
1124
1138
|
const rollupData = encodeFunctionData({
|
|
1125
1139
|
abi: RollupAbi,
|
|
@@ -1128,13 +1142,9 @@ export class SequencerPublisher {
|
|
|
1128
1142
|
});
|
|
1129
1143
|
|
|
1130
1144
|
// override the pending checkpoint number if requested
|
|
1131
|
-
const optsForcePendingCheckpointNumber =
|
|
1132
|
-
options.forcePendingBlockNumber !== undefined
|
|
1133
|
-
? CheckpointNumber.fromBlockNumber(options.forcePendingBlockNumber)
|
|
1134
|
-
: undefined;
|
|
1135
1145
|
const forcePendingCheckpointNumberStateDiff = (
|
|
1136
|
-
|
|
1137
|
-
? await this.rollupContract.makePendingCheckpointNumberOverride(
|
|
1146
|
+
options.forcePendingCheckpointNumber !== undefined
|
|
1147
|
+
? await this.rollupContract.makePendingCheckpointNumberOverride(options.forcePendingCheckpointNumber)
|
|
1138
1148
|
: []
|
|
1139
1149
|
).flatMap(override => override.stateDiff ?? []);
|
|
1140
1150
|
|
|
@@ -1198,7 +1208,7 @@ export class SequencerPublisher {
|
|
|
1198
1208
|
private async addProposeTx(
|
|
1199
1209
|
checkpoint: Checkpoint,
|
|
1200
1210
|
encodedData: L1ProcessArgs,
|
|
1201
|
-
opts: { txTimeoutAt?: Date;
|
|
1211
|
+
opts: { txTimeoutAt?: Date; forcePendingCheckpointNumber?: CheckpointNumber } = {},
|
|
1202
1212
|
timestamp: bigint,
|
|
1203
1213
|
): Promise<void> {
|
|
1204
1214
|
const slot = checkpoint.header.slotNumber;
|