@aztec/sequencer-client 0.0.1-commit.fcb71a6 → 0.0.1-commit.fffb133c
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 +4 -5
- 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-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 +17 -16
- package/dest/publisher/sequencer-publisher.d.ts.map +1 -1
- package/dest/publisher/sequencer-publisher.js +442 -49
- 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 +562 -39
- 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 +3 -4
- 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-metrics.ts +14 -70
- package/src/publisher/sequencer-publisher.ts +84 -73
- package/src/sequencer/checkpoint_proposal_job.ts +197 -58
- 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
package/src/sequencer/metrics.ts
CHANGED
|
@@ -11,7 +11,6 @@ import {
|
|
|
11
11
|
type TelemetryClient,
|
|
12
12
|
type Tracer,
|
|
13
13
|
type UpDownCounter,
|
|
14
|
-
ValueType,
|
|
15
14
|
} from '@aztec/telemetry-client';
|
|
16
15
|
|
|
17
16
|
import { type Hex, formatUnits } from 'viem';
|
|
@@ -44,7 +43,7 @@ export class SequencerMetrics {
|
|
|
44
43
|
private blockProposalPrecheckFailed: UpDownCounter;
|
|
45
44
|
private checkpointSuccess: UpDownCounter;
|
|
46
45
|
private slashingAttempts: UpDownCounter;
|
|
47
|
-
private
|
|
46
|
+
private checkpointAttestationDelay: Histogram;
|
|
48
47
|
|
|
49
48
|
// Fisherman fee analysis metrics
|
|
50
49
|
private fishermanWouldBeIncluded: UpDownCounter;
|
|
@@ -70,33 +69,13 @@ export class SequencerMetrics {
|
|
|
70
69
|
|
|
71
70
|
this.blockCounter = this.meter.createUpDownCounter(Metrics.SEQUENCER_BLOCK_COUNT);
|
|
72
71
|
|
|
73
|
-
this.blockBuildDuration = this.meter.createHistogram(Metrics.SEQUENCER_BLOCK_BUILD_DURATION
|
|
74
|
-
unit: 'ms',
|
|
75
|
-
description: 'Duration to build a block',
|
|
76
|
-
valueType: ValueType.INT,
|
|
77
|
-
});
|
|
72
|
+
this.blockBuildDuration = this.meter.createHistogram(Metrics.SEQUENCER_BLOCK_BUILD_DURATION);
|
|
78
73
|
|
|
79
|
-
this.blockBuildManaPerSecond = this.meter.createGauge(Metrics.SEQUENCER_BLOCK_BUILD_MANA_PER_SECOND
|
|
80
|
-
unit: 'mana/s',
|
|
81
|
-
description: 'Mana per second when building a block',
|
|
82
|
-
valueType: ValueType.INT,
|
|
83
|
-
});
|
|
74
|
+
this.blockBuildManaPerSecond = this.meter.createGauge(Metrics.SEQUENCER_BLOCK_BUILD_MANA_PER_SECOND);
|
|
84
75
|
|
|
85
|
-
this.stateTransitionBufferDuration = this.meter.createHistogram(
|
|
86
|
-
Metrics.SEQUENCER_STATE_TRANSITION_BUFFER_DURATION,
|
|
87
|
-
{
|
|
88
|
-
unit: 'ms',
|
|
89
|
-
description:
|
|
90
|
-
'The time difference between when the sequencer needed to transition to a new state and when it actually did.',
|
|
91
|
-
valueType: ValueType.INT,
|
|
92
|
-
},
|
|
93
|
-
);
|
|
76
|
+
this.stateTransitionBufferDuration = this.meter.createHistogram(Metrics.SEQUENCER_STATE_TRANSITION_BUFFER_DURATION);
|
|
94
77
|
|
|
95
|
-
this.
|
|
96
|
-
unit: 'ms',
|
|
97
|
-
description: 'The time difference between block proposal and minimal attestation count reached,',
|
|
98
|
-
valueType: ValueType.INT,
|
|
99
|
-
});
|
|
78
|
+
this.checkpointAttestationDelay = this.meter.createHistogram(Metrics.SEQUENCER_CHECKPOINT_ATTESTATION_DELAY);
|
|
100
79
|
|
|
101
80
|
// Init gauges and counters
|
|
102
81
|
this.blockCounter.add(0, {
|
|
@@ -106,152 +85,65 @@ export class SequencerMetrics {
|
|
|
106
85
|
[Attributes.STATUS]: 'built',
|
|
107
86
|
});
|
|
108
87
|
|
|
109
|
-
this.rewards = this.meter.createGauge(Metrics.SEQUENCER_CURRENT_BLOCK_REWARDS
|
|
110
|
-
valueType: ValueType.DOUBLE,
|
|
111
|
-
description: 'The rewards earned',
|
|
112
|
-
});
|
|
88
|
+
this.rewards = this.meter.createGauge(Metrics.SEQUENCER_CURRENT_BLOCK_REWARDS);
|
|
113
89
|
|
|
114
|
-
this.slots = this.meter.createUpDownCounter(Metrics.SEQUENCER_SLOT_COUNT
|
|
115
|
-
valueType: ValueType.INT,
|
|
116
|
-
description: 'The number of slots this sequencer was selected for',
|
|
117
|
-
});
|
|
90
|
+
this.slots = this.meter.createUpDownCounter(Metrics.SEQUENCER_SLOT_COUNT);
|
|
118
91
|
|
|
119
92
|
/**
|
|
120
93
|
* NOTE: we do not track missed slots as a separate metric. That would be difficult to determine
|
|
121
94
|
* Instead, use a computed metric, `slots - filledSlots` to get the number of slots a sequencer has missed.
|
|
122
95
|
*/
|
|
123
|
-
this.filledSlots = this.meter.createUpDownCounter(Metrics.SEQUENCER_FILLED_SLOT_COUNT
|
|
124
|
-
valueType: ValueType.INT,
|
|
125
|
-
description: 'The number of slots this sequencer has filled',
|
|
126
|
-
});
|
|
96
|
+
this.filledSlots = this.meter.createUpDownCounter(Metrics.SEQUENCER_FILLED_SLOT_COUNT);
|
|
127
97
|
|
|
128
|
-
this.timeToCollectAttestations = this.meter.createGauge(Metrics.SEQUENCER_COLLECT_ATTESTATIONS_DURATION
|
|
129
|
-
description: 'The time spent collecting attestations from committee members',
|
|
130
|
-
unit: 'ms',
|
|
131
|
-
valueType: ValueType.INT,
|
|
132
|
-
});
|
|
98
|
+
this.timeToCollectAttestations = this.meter.createGauge(Metrics.SEQUENCER_COLLECT_ATTESTATIONS_DURATION);
|
|
133
99
|
|
|
134
|
-
this.allowanceToCollectAttestations = this.meter.createGauge(
|
|
135
|
-
Metrics.SEQUENCER_COLLECT_ATTESTATIONS_TIME_ALLOWANCE,
|
|
136
|
-
{
|
|
137
|
-
description: 'Maximum amount of time to collect attestations',
|
|
138
|
-
unit: 'ms',
|
|
139
|
-
valueType: ValueType.INT,
|
|
140
|
-
},
|
|
141
|
-
);
|
|
100
|
+
this.allowanceToCollectAttestations = this.meter.createGauge(Metrics.SEQUENCER_COLLECT_ATTESTATIONS_TIME_ALLOWANCE);
|
|
142
101
|
|
|
143
|
-
this.requiredAttestions = this.meter.createGauge(Metrics.SEQUENCER_REQUIRED_ATTESTATIONS_COUNT
|
|
144
|
-
valueType: ValueType.INT,
|
|
145
|
-
description: 'The minimum number of attestations required to publish a block',
|
|
146
|
-
});
|
|
102
|
+
this.requiredAttestions = this.meter.createGauge(Metrics.SEQUENCER_REQUIRED_ATTESTATIONS_COUNT);
|
|
147
103
|
|
|
148
|
-
this.collectedAttestions = this.meter.createGauge(Metrics.SEQUENCER_COLLECTED_ATTESTATIONS_COUNT
|
|
149
|
-
valueType: ValueType.INT,
|
|
150
|
-
description: 'The minimum number of attestations required to publish a block',
|
|
151
|
-
});
|
|
104
|
+
this.collectedAttestions = this.meter.createGauge(Metrics.SEQUENCER_COLLECTED_ATTESTATIONS_COUNT);
|
|
152
105
|
|
|
153
|
-
this.blockProposalFailed = this.meter.createUpDownCounter(Metrics.SEQUENCER_BLOCK_PROPOSAL_FAILED_COUNT
|
|
154
|
-
valueType: ValueType.INT,
|
|
155
|
-
description: 'The number of times block proposal failed (including validation builds)',
|
|
156
|
-
});
|
|
106
|
+
this.blockProposalFailed = this.meter.createUpDownCounter(Metrics.SEQUENCER_BLOCK_PROPOSAL_FAILED_COUNT);
|
|
157
107
|
|
|
158
|
-
this.blockProposalSuccess = this.meter.createUpDownCounter(Metrics.SEQUENCER_BLOCK_PROPOSAL_SUCCESS_COUNT
|
|
159
|
-
valueType: ValueType.INT,
|
|
160
|
-
description: 'The number of times block proposal succeeded (including validation builds)',
|
|
161
|
-
});
|
|
108
|
+
this.blockProposalSuccess = this.meter.createUpDownCounter(Metrics.SEQUENCER_BLOCK_PROPOSAL_SUCCESS_COUNT);
|
|
162
109
|
|
|
163
|
-
this.checkpointSuccess = this.meter.createUpDownCounter(Metrics.SEQUENCER_CHECKPOINT_SUCCESS_COUNT
|
|
164
|
-
valueType: ValueType.INT,
|
|
165
|
-
description: 'The number of times checkpoint publishing succeeded',
|
|
166
|
-
});
|
|
110
|
+
this.checkpointSuccess = this.meter.createUpDownCounter(Metrics.SEQUENCER_CHECKPOINT_SUCCESS_COUNT);
|
|
167
111
|
|
|
168
112
|
this.blockProposalPrecheckFailed = this.meter.createUpDownCounter(
|
|
169
113
|
Metrics.SEQUENCER_BLOCK_PROPOSAL_PRECHECK_FAILED_COUNT,
|
|
170
|
-
{
|
|
171
|
-
valueType: ValueType.INT,
|
|
172
|
-
description: 'The number of times block proposal pre-build checks failed',
|
|
173
|
-
},
|
|
174
114
|
);
|
|
175
115
|
|
|
176
|
-
this.slashingAttempts = this.meter.createUpDownCounter(Metrics.SEQUENCER_SLASHING_ATTEMPTS_COUNT
|
|
177
|
-
valueType: ValueType.INT,
|
|
178
|
-
description: 'The number of slashing action attempts',
|
|
179
|
-
});
|
|
116
|
+
this.slashingAttempts = this.meter.createUpDownCounter(Metrics.SEQUENCER_SLASHING_ATTEMPTS_COUNT);
|
|
180
117
|
|
|
181
118
|
// Fisherman fee analysis metrics
|
|
182
|
-
this.fishermanWouldBeIncluded = this.meter.createUpDownCounter(Metrics.FISHERMAN_FEE_ANALYSIS_WOULD_BE_INCLUDED
|
|
183
|
-
valueType: ValueType.INT,
|
|
184
|
-
description: 'Whether the transaction would have been included in the block',
|
|
185
|
-
});
|
|
119
|
+
this.fishermanWouldBeIncluded = this.meter.createUpDownCounter(Metrics.FISHERMAN_FEE_ANALYSIS_WOULD_BE_INCLUDED);
|
|
186
120
|
|
|
187
|
-
this.fishermanTimeBeforeBlock = this.meter.createHistogram(Metrics.FISHERMAN_FEE_ANALYSIS_TIME_BEFORE_BLOCK
|
|
188
|
-
unit: 'ms',
|
|
189
|
-
description: 'Time in ms between fee analysis and block being mined',
|
|
190
|
-
valueType: ValueType.INT,
|
|
191
|
-
});
|
|
121
|
+
this.fishermanTimeBeforeBlock = this.meter.createHistogram(Metrics.FISHERMAN_FEE_ANALYSIS_TIME_BEFORE_BLOCK);
|
|
192
122
|
|
|
193
|
-
this.fishermanPendingBlobTxCount = this.meter.createHistogram(
|
|
194
|
-
Metrics.FISHERMAN_FEE_ANALYSIS_PENDING_BLOB_TX_COUNT,
|
|
195
|
-
{
|
|
196
|
-
description: 'Number of blob transactions seen in the pending block',
|
|
197
|
-
valueType: ValueType.INT,
|
|
198
|
-
},
|
|
199
|
-
);
|
|
123
|
+
this.fishermanPendingBlobTxCount = this.meter.createHistogram(Metrics.FISHERMAN_FEE_ANALYSIS_PENDING_BLOB_TX_COUNT);
|
|
200
124
|
|
|
201
125
|
this.fishermanIncludedBlobTxCount = this.meter.createHistogram(
|
|
202
126
|
Metrics.FISHERMAN_FEE_ANALYSIS_INCLUDED_BLOB_TX_COUNT,
|
|
203
|
-
{
|
|
204
|
-
description: 'Number of blob transactions that got included in the mined block',
|
|
205
|
-
valueType: ValueType.INT,
|
|
206
|
-
},
|
|
207
127
|
);
|
|
208
128
|
|
|
209
129
|
this.fishermanCalculatedPriorityFee = this.meter.createHistogram(
|
|
210
130
|
Metrics.FISHERMAN_FEE_ANALYSIS_CALCULATED_PRIORITY_FEE,
|
|
211
|
-
{
|
|
212
|
-
unit: 'gwei',
|
|
213
|
-
description: 'Priority fee calculated by each strategy',
|
|
214
|
-
valueType: ValueType.DOUBLE,
|
|
215
|
-
},
|
|
216
131
|
);
|
|
217
132
|
|
|
218
|
-
this.fishermanPriorityFeeDelta = this.meter.createHistogram(Metrics.FISHERMAN_FEE_ANALYSIS_PRIORITY_FEE_DELTA
|
|
219
|
-
unit: 'gwei',
|
|
220
|
-
description: 'Difference between our priority fee and minimum included priority fee',
|
|
221
|
-
valueType: ValueType.DOUBLE,
|
|
222
|
-
});
|
|
133
|
+
this.fishermanPriorityFeeDelta = this.meter.createHistogram(Metrics.FISHERMAN_FEE_ANALYSIS_PRIORITY_FEE_DELTA);
|
|
223
134
|
|
|
224
|
-
this.fishermanEstimatedCost = this.meter.createHistogram(Metrics.FISHERMAN_FEE_ANALYSIS_ESTIMATED_COST
|
|
225
|
-
unit: 'eth',
|
|
226
|
-
description: 'Estimated total cost in ETH for the transaction with this strategy',
|
|
227
|
-
valueType: ValueType.DOUBLE,
|
|
228
|
-
});
|
|
135
|
+
this.fishermanEstimatedCost = this.meter.createHistogram(Metrics.FISHERMAN_FEE_ANALYSIS_ESTIMATED_COST);
|
|
229
136
|
|
|
230
137
|
this.fishermanEstimatedOverpayment = this.meter.createHistogram(
|
|
231
138
|
Metrics.FISHERMAN_FEE_ANALYSIS_ESTIMATED_OVERPAYMENT,
|
|
232
|
-
{
|
|
233
|
-
unit: 'eth',
|
|
234
|
-
description: 'Estimated overpayment in ETH vs minimum required for inclusion',
|
|
235
|
-
valueType: ValueType.DOUBLE,
|
|
236
|
-
},
|
|
237
139
|
);
|
|
238
140
|
|
|
239
141
|
this.fishermanMinedBlobTxPriorityFee = this.meter.createHistogram(
|
|
240
142
|
Metrics.FISHERMAN_FEE_ANALYSIS_MINED_BLOB_TX_PRIORITY_FEE,
|
|
241
|
-
{
|
|
242
|
-
unit: 'gwei',
|
|
243
|
-
description: 'Priority fee per gas for blob transactions in mined blocks',
|
|
244
|
-
valueType: ValueType.DOUBLE,
|
|
245
|
-
},
|
|
246
143
|
);
|
|
247
144
|
|
|
248
145
|
this.fishermanMinedBlobTxTotalCost = this.meter.createHistogram(
|
|
249
146
|
Metrics.FISHERMAN_FEE_ANALYSIS_MINED_BLOB_TX_TOTAL_COST,
|
|
250
|
-
{
|
|
251
|
-
unit: 'eth',
|
|
252
|
-
description: 'Total cost in ETH for blob transactions in mined blocks',
|
|
253
|
-
valueType: ValueType.DOUBLE,
|
|
254
|
-
},
|
|
255
147
|
);
|
|
256
148
|
}
|
|
257
149
|
|
|
@@ -264,8 +156,8 @@ export class SequencerMetrics {
|
|
|
264
156
|
this.timeToCollectAttestations.record(0);
|
|
265
157
|
}
|
|
266
158
|
|
|
267
|
-
public
|
|
268
|
-
this.
|
|
159
|
+
public recordCheckpointAttestationDelay(duration: number) {
|
|
160
|
+
this.checkpointAttestationDelay.record(duration);
|
|
269
161
|
}
|
|
270
162
|
|
|
271
163
|
public recordCollectedAttestations(count: number, durationMs: number) {
|
|
@@ -12,7 +12,7 @@ import type { DateProvider } from '@aztec/foundation/timer';
|
|
|
12
12
|
import type { TypedEventEmitter } from '@aztec/foundation/types';
|
|
13
13
|
import type { P2P } from '@aztec/p2p';
|
|
14
14
|
import type { SlasherClientInterface } from '@aztec/slasher';
|
|
15
|
-
import type {
|
|
15
|
+
import type { L2Block, L2BlockSink, L2BlockSource, ValidateCheckpointResult } from '@aztec/stdlib/block';
|
|
16
16
|
import type { Checkpoint } from '@aztec/stdlib/checkpoint';
|
|
17
17
|
import { getSlotAtTimestamp, getSlotStartBuildTimestamp } from '@aztec/stdlib/epoch-helpers';
|
|
18
18
|
import {
|
|
@@ -24,16 +24,15 @@ import {
|
|
|
24
24
|
import type { L1ToL2MessageSource } from '@aztec/stdlib/messaging';
|
|
25
25
|
import { pickFromSchema } from '@aztec/stdlib/schemas';
|
|
26
26
|
import { MerkleTreeId } from '@aztec/stdlib/trees';
|
|
27
|
-
import { type TelemetryClient, type Tracer, getTelemetryClient, trackSpan } from '@aztec/telemetry-client';
|
|
28
|
-
import type
|
|
27
|
+
import { Attributes, type TelemetryClient, type Tracer, getTelemetryClient, trackSpan } from '@aztec/telemetry-client';
|
|
28
|
+
import { FullNodeCheckpointsBuilder, type ValidatorClient } from '@aztec/validator-client';
|
|
29
29
|
|
|
30
30
|
import EventEmitter from 'node:events';
|
|
31
31
|
|
|
32
32
|
import { DefaultSequencerConfig } from '../config.js';
|
|
33
33
|
import type { GlobalVariableBuilder } from '../global_variable_builder/global_builder.js';
|
|
34
34
|
import type { SequencerPublisherFactory } from '../publisher/sequencer-publisher-factory.js';
|
|
35
|
-
import type {
|
|
36
|
-
import { FullNodeCheckpointsBuilder } from './checkpoint_builder.js';
|
|
35
|
+
import type { InvalidateCheckpointRequest, SequencerPublisher } from '../publisher/sequencer-publisher.js';
|
|
37
36
|
import { CheckpointProposalJob } from './checkpoint_proposal_job.js';
|
|
38
37
|
import { CheckpointVoter } from './checkpoint_voter.js';
|
|
39
38
|
import { SequencerInterruptedError, SequencerTooSlowError } from './errors.js';
|
|
@@ -58,8 +57,8 @@ export class Sequencer extends (EventEmitter as new () => TypedEventEmitter<Sequ
|
|
|
58
57
|
private state = SequencerState.STOPPED;
|
|
59
58
|
private metrics: SequencerMetrics;
|
|
60
59
|
|
|
61
|
-
/** The last slot for which we attempted to
|
|
62
|
-
private
|
|
60
|
+
/** The last slot for which we attempted to perform our voting duties with degraded block production */
|
|
61
|
+
private lastSlotForFallbackVote: SlotNumber | undefined;
|
|
63
62
|
|
|
64
63
|
/** The last slot for which we triggered a checkpoint proposal job, to prevent duplicate attempts. */
|
|
65
64
|
private lastSlotForCheckpointProposalJob: SlotNumber | undefined;
|
|
@@ -91,7 +90,7 @@ export class Sequencer extends (EventEmitter as new () => TypedEventEmitter<Sequ
|
|
|
91
90
|
protected p2pClient: P2P,
|
|
92
91
|
protected worldState: WorldStateSynchronizer,
|
|
93
92
|
protected slasherClient: SlasherClientInterface | undefined,
|
|
94
|
-
protected l2BlockSource: L2BlockSource,
|
|
93
|
+
protected l2BlockSource: L2BlockSource & L2BlockSink,
|
|
95
94
|
protected l1ToL2MessageSource: L1ToL2MessageSource,
|
|
96
95
|
protected checkpointsBuilder: FullNodeCheckpointsBuilder,
|
|
97
96
|
protected l1Constants: SequencerRollupConstants,
|
|
@@ -160,7 +159,6 @@ export class Sequencer extends (EventEmitter as new () => TypedEventEmitter<Sequ
|
|
|
160
159
|
this.log.info('Stopped sequencer');
|
|
161
160
|
}
|
|
162
161
|
|
|
163
|
-
@trackSpan('Sequencer.work')
|
|
164
162
|
/** Main sequencer loop with a try/catch */
|
|
165
163
|
protected async safeWork() {
|
|
166
164
|
try {
|
|
@@ -198,12 +196,13 @@ export class Sequencer extends (EventEmitter as new () => TypedEventEmitter<Sequ
|
|
|
198
196
|
* - Collect attestations for the final block
|
|
199
197
|
* - Submit checkpoint
|
|
200
198
|
*/
|
|
199
|
+
@trackSpan('Sequencer.work')
|
|
201
200
|
protected async work() {
|
|
202
201
|
this.setState(SequencerState.SYNCHRONIZING, undefined);
|
|
203
202
|
const { slot, ts, now, epoch } = this.epochCache.getEpochAndSlotInNextL1Slot();
|
|
204
203
|
|
|
205
204
|
// Check if we are synced and it's our slot, grab a publisher, check previous block invalidation, etc
|
|
206
|
-
const checkpointProposalJob = await this.prepareCheckpointProposal(slot, ts, now);
|
|
205
|
+
const checkpointProposalJob = await this.prepareCheckpointProposal(epoch, slot, ts, now);
|
|
207
206
|
if (!checkpointProposalJob) {
|
|
208
207
|
return;
|
|
209
208
|
}
|
|
@@ -233,7 +232,9 @@ export class Sequencer extends (EventEmitter as new () => TypedEventEmitter<Sequ
|
|
|
233
232
|
* This is the initial step in the main loop.
|
|
234
233
|
* @returns CheckpointProposalJob if successful, undefined if we are not yet synced or are not the proposer.
|
|
235
234
|
*/
|
|
235
|
+
@trackSpan('Sequencer.prepareCheckpointProposal')
|
|
236
236
|
private async prepareCheckpointProposal(
|
|
237
|
+
epoch: EpochNumber,
|
|
237
238
|
slot: SlotNumber,
|
|
238
239
|
ts: bigint,
|
|
239
240
|
now: bigint,
|
|
@@ -263,8 +264,27 @@ export class Sequencer extends (EventEmitter as new () => TypedEventEmitter<Sequ
|
|
|
263
264
|
return undefined;
|
|
264
265
|
}
|
|
265
266
|
|
|
266
|
-
//
|
|
267
|
-
|
|
267
|
+
// If escape hatch is open for this epoch, do not start checkpoint proposal work and do not attempt invalidations.
|
|
268
|
+
// Still perform governance/slashing voting (as proposer) once per slot.
|
|
269
|
+
const isEscapeHatchOpen = await this.epochCache.isEscapeHatchOpen(epoch);
|
|
270
|
+
|
|
271
|
+
if (isEscapeHatchOpen) {
|
|
272
|
+
this.setState(SequencerState.PROPOSER_CHECK, slot);
|
|
273
|
+
const [canPropose, proposer] = await this.checkCanPropose(slot);
|
|
274
|
+
if (canPropose) {
|
|
275
|
+
await this.tryVoteWhenEscapeHatchOpen({ slot, proposer });
|
|
276
|
+
} else {
|
|
277
|
+
this.log.trace(`Escape hatch open but we are not proposer, skipping vote-only actions`, {
|
|
278
|
+
slot,
|
|
279
|
+
epoch,
|
|
280
|
+
proposer,
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
return undefined;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// Next checkpoint follows from the last synced one
|
|
287
|
+
const checkpointNumber = CheckpointNumber(syncedTo.checkpointNumber + 1);
|
|
268
288
|
|
|
269
289
|
const logCtx = {
|
|
270
290
|
now,
|
|
@@ -280,9 +300,9 @@ export class Sequencer extends (EventEmitter as new () => TypedEventEmitter<Sequ
|
|
|
280
300
|
this.setState(SequencerState.PROPOSER_CHECK, slot);
|
|
281
301
|
const [canPropose, proposer] = await this.checkCanPropose(slot);
|
|
282
302
|
|
|
283
|
-
// If we are not a proposer check if we should invalidate
|
|
303
|
+
// If we are not a proposer check if we should invalidate an invalid checkpoint, and bail
|
|
284
304
|
if (!canPropose) {
|
|
285
|
-
await this.
|
|
305
|
+
await this.considerInvalidatingCheckpoint(syncedTo, slot);
|
|
286
306
|
return undefined;
|
|
287
307
|
}
|
|
288
308
|
|
|
@@ -312,15 +332,14 @@ export class Sequencer extends (EventEmitter as new () => TypedEventEmitter<Sequ
|
|
|
312
332
|
}
|
|
313
333
|
|
|
314
334
|
// Prepare invalidation request if the pending chain is invalid (returns undefined if no need)
|
|
315
|
-
|
|
316
|
-
const invalidateBlock = await publisher.simulateInvalidateBlock(syncedTo.pendingChainValidationStatus);
|
|
335
|
+
const invalidateCheckpoint = await publisher.simulateInvalidateCheckpoint(syncedTo.pendingChainValidationStatus);
|
|
317
336
|
|
|
318
337
|
// Check with the rollup contract if we can indeed propose at the next L2 slot. This check should not fail
|
|
319
338
|
// if all the previous checks are good, but we do it just in case.
|
|
320
339
|
const canProposeCheck = await publisher.canProposeAtNextEthBlock(
|
|
321
340
|
syncedTo.archive,
|
|
322
341
|
proposer ?? EthAddress.ZERO,
|
|
323
|
-
|
|
342
|
+
invalidateCheckpoint,
|
|
324
343
|
);
|
|
325
344
|
|
|
326
345
|
if (canProposeCheck === undefined) {
|
|
@@ -358,39 +377,44 @@ export class Sequencer extends (EventEmitter as new () => TypedEventEmitter<Sequ
|
|
|
358
377
|
|
|
359
378
|
// Create and return the checkpoint proposal job
|
|
360
379
|
return this.createCheckpointProposalJob(
|
|
380
|
+
epoch,
|
|
361
381
|
slot,
|
|
362
382
|
checkpointNumber,
|
|
363
383
|
syncedTo.blockNumber,
|
|
364
384
|
proposer,
|
|
365
385
|
publisher,
|
|
366
386
|
attestorAddress,
|
|
367
|
-
|
|
387
|
+
invalidateCheckpoint,
|
|
368
388
|
);
|
|
369
389
|
}
|
|
370
390
|
|
|
371
391
|
protected createCheckpointProposalJob(
|
|
392
|
+
epoch: EpochNumber,
|
|
372
393
|
slot: SlotNumber,
|
|
373
394
|
checkpointNumber: CheckpointNumber,
|
|
374
395
|
syncedToBlockNumber: BlockNumber,
|
|
375
396
|
proposer: EthAddress | undefined,
|
|
376
397
|
publisher: SequencerPublisher,
|
|
377
398
|
attestorAddress: EthAddress,
|
|
378
|
-
|
|
399
|
+
invalidateCheckpoint: InvalidateCheckpointRequest | undefined,
|
|
379
400
|
): CheckpointProposalJob {
|
|
380
401
|
return new CheckpointProposalJob(
|
|
402
|
+
epoch,
|
|
381
403
|
slot,
|
|
382
404
|
checkpointNumber,
|
|
383
405
|
syncedToBlockNumber,
|
|
384
406
|
proposer,
|
|
385
407
|
publisher,
|
|
386
408
|
attestorAddress,
|
|
387
|
-
|
|
409
|
+
invalidateCheckpoint,
|
|
388
410
|
this.validatorClient,
|
|
389
411
|
this.globalsBuilder,
|
|
390
412
|
this.p2pClient,
|
|
391
413
|
this.worldState,
|
|
392
414
|
this.l1ToL2MessageSource,
|
|
415
|
+
this.l2BlockSource,
|
|
393
416
|
this.checkpointsBuilder,
|
|
417
|
+
this.l2BlockSource,
|
|
394
418
|
this.l1Constants,
|
|
395
419
|
this.config,
|
|
396
420
|
this.timetable,
|
|
@@ -401,6 +425,7 @@ export class Sequencer extends (EventEmitter as new () => TypedEventEmitter<Sequ
|
|
|
401
425
|
this,
|
|
402
426
|
this.setState.bind(this),
|
|
403
427
|
this.log,
|
|
428
|
+
this.tracer,
|
|
404
429
|
);
|
|
405
430
|
}
|
|
406
431
|
|
|
@@ -469,9 +494,9 @@ export class Sequencer extends (EventEmitter as new () => TypedEventEmitter<Sequ
|
|
|
469
494
|
number: syncSummary.latestBlockNumber,
|
|
470
495
|
hash: syncSummary.latestBlockHash,
|
|
471
496
|
})),
|
|
472
|
-
this.l2BlockSource.getL2Tips().then(t => t.
|
|
497
|
+
this.l2BlockSource.getL2Tips().then(t => t.proposed),
|
|
473
498
|
this.p2pClient.getStatus().then(p2p => p2p.syncedToL2Block),
|
|
474
|
-
this.l1ToL2MessageSource.getL2Tips().then(t => t.
|
|
499
|
+
this.l1ToL2MessageSource.getL2Tips().then(t => t.proposed),
|
|
475
500
|
this.l2BlockSource.getPendingChainValidationStatus(),
|
|
476
501
|
] as const);
|
|
477
502
|
|
|
@@ -479,6 +504,7 @@ export class Sequencer extends (EventEmitter as new () => TypedEventEmitter<Sequ
|
|
|
479
504
|
|
|
480
505
|
// Handle zero as a special case, since the block hash won't match across services if we're changing the prefilled data for the genesis block,
|
|
481
506
|
// as the world state can compute the new genesis block hash, but other components use the hardcoded constant.
|
|
507
|
+
// TODO(palla/mbps): Fix the above. All components should be able to handle dynamic genesis block hashes.
|
|
482
508
|
const result =
|
|
483
509
|
(l2BlockSource.number === 0 && worldState.number === 0 && p2p.number === 0 && l1ToL2MessageSource.number === 0) ||
|
|
484
510
|
(worldState.hash === l2BlockSource.hash &&
|
|
@@ -494,10 +520,16 @@ export class Sequencer extends (EventEmitter as new () => TypedEventEmitter<Sequ
|
|
|
494
520
|
const blockNumber = worldState.number;
|
|
495
521
|
if (blockNumber < INITIAL_L2_BLOCK_NUM) {
|
|
496
522
|
const archive = new Fr((await this.worldState.getCommitted().getTreeInfo(MerkleTreeId.ARCHIVE)).root);
|
|
497
|
-
return {
|
|
523
|
+
return {
|
|
524
|
+
checkpointNumber: CheckpointNumber.ZERO,
|
|
525
|
+
blockNumber: BlockNumber.ZERO,
|
|
526
|
+
archive,
|
|
527
|
+
l1Timestamp,
|
|
528
|
+
pendingChainValidationStatus,
|
|
529
|
+
};
|
|
498
530
|
}
|
|
499
531
|
|
|
500
|
-
const block = await this.l2BlockSource.
|
|
532
|
+
const block = await this.l2BlockSource.getL2Block(blockNumber);
|
|
501
533
|
if (!block) {
|
|
502
534
|
// this shouldn't really happen because a moment ago we checked that all components were in sync
|
|
503
535
|
this.log.error(`Failed to get L2 block ${blockNumber} from the archiver with all components in sync`);
|
|
@@ -507,6 +539,7 @@ export class Sequencer extends (EventEmitter as new () => TypedEventEmitter<Sequ
|
|
|
507
539
|
return {
|
|
508
540
|
block,
|
|
509
541
|
blockNumber: block.number,
|
|
542
|
+
checkpointNumber: block.checkpointNumber,
|
|
510
543
|
archive: block.archive.root,
|
|
511
544
|
l1Timestamp,
|
|
512
545
|
pendingChainValidationStatus,
|
|
@@ -555,11 +588,12 @@ export class Sequencer extends (EventEmitter as new () => TypedEventEmitter<Sequ
|
|
|
555
588
|
* Tries to vote on slashing actions and governance when the sync check fails but we're past the max time for initializing a proposal.
|
|
556
589
|
* This allows the sequencer to participate in governance/slashing votes even when it cannot build blocks.
|
|
557
590
|
*/
|
|
591
|
+
@trackSpan('Seqeuencer.tryVoteWhenSyncFails', ({ slot }) => ({ [Attributes.SLOT_NUMBER]: slot }))
|
|
558
592
|
protected async tryVoteWhenSyncFails(args: { slot: SlotNumber; ts: bigint }): Promise<void> {
|
|
559
593
|
const { slot } = args;
|
|
560
594
|
|
|
561
595
|
// Prevent duplicate attempts in the same slot
|
|
562
|
-
if (this.
|
|
596
|
+
if (this.lastSlotForFallbackVote === slot) {
|
|
563
597
|
this.log.trace(`Already attempted to vote in slot ${slot} (skipping)`);
|
|
564
598
|
return;
|
|
565
599
|
}
|
|
@@ -591,7 +625,7 @@ export class Sequencer extends (EventEmitter as new () => TypedEventEmitter<Sequ
|
|
|
591
625
|
}
|
|
592
626
|
|
|
593
627
|
// Mark this slot as attempted
|
|
594
|
-
this.
|
|
628
|
+
this.lastSlotForFallbackVote = slot;
|
|
595
629
|
|
|
596
630
|
// Get a publisher for voting
|
|
597
631
|
const { attestorAddress, publisher } = await this.publisherFactory.create(proposer);
|
|
@@ -625,13 +659,61 @@ export class Sequencer extends (EventEmitter as new () => TypedEventEmitter<Sequ
|
|
|
625
659
|
await publisher.sendRequests();
|
|
626
660
|
}
|
|
627
661
|
|
|
662
|
+
/**
|
|
663
|
+
* Tries to vote on slashing actions and governance proposals when escape hatch is open.
|
|
664
|
+
* This allows the sequencer to participate in voting without performing checkpoint proposal work.
|
|
665
|
+
*/
|
|
666
|
+
@trackSpan('Sequencer.tryVoteWhenEscapeHatchOpen', ({ slot }) => ({ [Attributes.SLOT_NUMBER]: slot }))
|
|
667
|
+
protected async tryVoteWhenEscapeHatchOpen(args: {
|
|
668
|
+
slot: SlotNumber;
|
|
669
|
+
proposer: EthAddress | undefined;
|
|
670
|
+
}): Promise<void> {
|
|
671
|
+
const { slot, proposer } = args;
|
|
672
|
+
|
|
673
|
+
// Prevent duplicate attempts in the same slot
|
|
674
|
+
if (this.lastSlotForFallbackVote === slot) {
|
|
675
|
+
this.log.trace(`Already attempted to vote in slot ${slot} (escape hatch open, skipping)`);
|
|
676
|
+
return;
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
// Mark this slot as attempted
|
|
680
|
+
this.lastSlotForFallbackVote = slot;
|
|
681
|
+
|
|
682
|
+
const { attestorAddress, publisher } = await this.publisherFactory.create(proposer);
|
|
683
|
+
|
|
684
|
+
this.log.debug(`Escape hatch open for slot ${slot}, attempting vote-only actions`, { slot, attestorAddress });
|
|
685
|
+
|
|
686
|
+
const voter = new CheckpointVoter(
|
|
687
|
+
slot,
|
|
688
|
+
publisher,
|
|
689
|
+
attestorAddress,
|
|
690
|
+
this.validatorClient,
|
|
691
|
+
this.slasherClient,
|
|
692
|
+
this.l1Constants,
|
|
693
|
+
this.config,
|
|
694
|
+
this.metrics,
|
|
695
|
+
this.log,
|
|
696
|
+
);
|
|
697
|
+
|
|
698
|
+
const votesPromises = voter.enqueueVotes();
|
|
699
|
+
const votes = await Promise.all(votesPromises);
|
|
700
|
+
|
|
701
|
+
if (votes.every(p => !p)) {
|
|
702
|
+
this.log.debug(`No votes to enqueue for slot ${slot} (escape hatch open)`);
|
|
703
|
+
return;
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
this.log.info(`Voting in slot ${slot} (escape hatch open)`, { slot });
|
|
707
|
+
await publisher.sendRequests();
|
|
708
|
+
}
|
|
709
|
+
|
|
628
710
|
/**
|
|
629
711
|
* Considers invalidating a block if the pending chain is invalid. Depends on how long the invalid block
|
|
630
712
|
* has been there without being invalidated and whether the sequencer is in the committee or not. We always
|
|
631
713
|
* have the proposer try to invalidate, but if they fail, the sequencers in the committee are expected to try,
|
|
632
714
|
* and if they fail, any sequencer will try as well.
|
|
633
715
|
*/
|
|
634
|
-
protected async
|
|
716
|
+
protected async considerInvalidatingCheckpoint(
|
|
635
717
|
syncedTo: SequencerSyncCheckResult,
|
|
636
718
|
currentSlot: SlotNumber,
|
|
637
719
|
): Promise<void> {
|
|
@@ -640,18 +722,18 @@ export class Sequencer extends (EventEmitter as new () => TypedEventEmitter<Sequ
|
|
|
640
722
|
return;
|
|
641
723
|
}
|
|
642
724
|
|
|
643
|
-
const
|
|
644
|
-
const
|
|
645
|
-
const timeSinceChainInvalid = this.dateProvider.nowInSeconds() - Number(
|
|
725
|
+
const invalidCheckpointNumber = pendingChainValidationStatus.checkpoint.checkpointNumber;
|
|
726
|
+
const invalidCheckpointTimestamp = pendingChainValidationStatus.checkpoint.timestamp;
|
|
727
|
+
const timeSinceChainInvalid = this.dateProvider.nowInSeconds() - Number(invalidCheckpointTimestamp);
|
|
646
728
|
const ourValidatorAddresses = this.validatorClient.getValidatorAddresses();
|
|
647
729
|
|
|
648
730
|
const { secondsBeforeInvalidatingBlockAsCommitteeMember, secondsBeforeInvalidatingBlockAsNonCommitteeMember } =
|
|
649
731
|
this.config;
|
|
650
732
|
|
|
651
733
|
const logData = {
|
|
652
|
-
invalidL1Timestamp:
|
|
734
|
+
invalidL1Timestamp: invalidCheckpointTimestamp,
|
|
653
735
|
l1Timestamp,
|
|
654
|
-
|
|
736
|
+
invalidCheckpoint: pendingChainValidationStatus.checkpoint,
|
|
655
737
|
secondsBeforeInvalidatingBlockAsCommitteeMember,
|
|
656
738
|
secondsBeforeInvalidatingBlockAsNonCommitteeMember,
|
|
657
739
|
ourValidatorAddresses,
|
|
@@ -697,25 +779,25 @@ export class Sequencer extends (EventEmitter as new () => TypedEventEmitter<Sequ
|
|
|
697
779
|
|
|
698
780
|
const { publisher } = await this.publisherFactory.create(validatorToUse);
|
|
699
781
|
|
|
700
|
-
const
|
|
701
|
-
if (!
|
|
702
|
-
this.log.warn(`Failed to simulate invalidate
|
|
782
|
+
const invalidateCheckpoint = await publisher.simulateInvalidateCheckpoint(pendingChainValidationStatus);
|
|
783
|
+
if (!invalidateCheckpoint) {
|
|
784
|
+
this.log.warn(`Failed to simulate invalidate checkpoint`, logData);
|
|
703
785
|
return;
|
|
704
786
|
}
|
|
705
787
|
|
|
706
788
|
this.log.info(
|
|
707
789
|
invalidateAsCommitteeMember
|
|
708
|
-
? `Invalidating
|
|
709
|
-
: `Invalidating
|
|
790
|
+
? `Invalidating checkpoint ${invalidCheckpointNumber} as committee member`
|
|
791
|
+
: `Invalidating checkpoint ${invalidCheckpointNumber} as non-committee member`,
|
|
710
792
|
logData,
|
|
711
793
|
);
|
|
712
794
|
|
|
713
|
-
publisher.
|
|
795
|
+
publisher.enqueueInvalidateCheckpoint(invalidateCheckpoint);
|
|
714
796
|
|
|
715
797
|
if (!this.config.fishermanMode) {
|
|
716
798
|
await publisher.sendRequests();
|
|
717
799
|
} else {
|
|
718
|
-
this.log.info('Invalidating
|
|
800
|
+
this.log.info('Invalidating checkpoint in fisherman mode, clearing pending requests');
|
|
719
801
|
publisher.clearPendingRequests();
|
|
720
802
|
}
|
|
721
803
|
}
|
|
@@ -788,9 +870,10 @@ export class Sequencer extends (EventEmitter as new () => TypedEventEmitter<Sequ
|
|
|
788
870
|
}
|
|
789
871
|
|
|
790
872
|
type SequencerSyncCheckResult = {
|
|
791
|
-
block?:
|
|
873
|
+
block?: L2Block;
|
|
874
|
+
checkpointNumber: CheckpointNumber;
|
|
792
875
|
blockNumber: BlockNumber;
|
|
793
876
|
archive: Fr;
|
|
794
877
|
l1Timestamp: bigint;
|
|
795
|
-
pendingChainValidationStatus:
|
|
878
|
+
pendingChainValidationStatus: ValidateCheckpointResult;
|
|
796
879
|
};
|
package/src/test/index.ts
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import type { L1TxUtilsWithBlobs } from '@aztec/ethereum/l1-tx-utils-with-blobs';
|
|
2
2
|
import type { PublisherManager } from '@aztec/ethereum/publisher-manager';
|
|
3
3
|
import type { PublicProcessorFactory } from '@aztec/simulator/server';
|
|
4
|
-
import type { ValidatorClient } from '@aztec/validator-client';
|
|
4
|
+
import type { FullNodeCheckpointsBuilder, ValidatorClient } from '@aztec/validator-client';
|
|
5
5
|
|
|
6
6
|
import { SequencerClient } from '../client/sequencer-client.js';
|
|
7
7
|
import type { SequencerPublisherFactory } from '../publisher/sequencer-publisher-factory.js';
|
|
8
8
|
import type { SequencerPublisher } from '../publisher/sequencer-publisher.js';
|
|
9
|
-
import type { FullNodeCheckpointsBuilder } from '../sequencer/checkpoint_builder.js';
|
|
10
9
|
import { Sequencer } from '../sequencer/sequencer.js';
|
|
11
10
|
import type { SequencerTimetable } from '../sequencer/timetable.js';
|
|
12
11
|
|