@aztec/sequencer-client 0.73.0 → 0.75.0-commit.c03ba01a2a4122e43e90d5133ba017e54b90e9d2

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.
Files changed (88) hide show
  1. package/dest/client/index.js +0 -1
  2. package/dest/client/sequencer-client.js +55 -55
  3. package/dest/config.js +40 -42
  4. package/dest/global_variable_builder/global_builder.js +39 -26
  5. package/dest/global_variable_builder/index.js +0 -1
  6. package/dest/index.js +0 -1
  7. package/dest/publisher/config.js +49 -50
  8. package/dest/publisher/index.js +0 -1
  9. package/dest/publisher/sequencer-publisher-metrics.js +32 -23
  10. package/dest/publisher/sequencer-publisher.js +251 -253
  11. package/dest/sequencer/allowed.js +5 -12
  12. package/dest/sequencer/config.js +1 -2
  13. package/dest/sequencer/index.js +0 -1
  14. package/dest/sequencer/metrics.js +26 -18
  15. package/dest/sequencer/sequencer.js +648 -607
  16. package/dest/sequencer/timetable.js +33 -19
  17. package/dest/sequencer/utils.js +22 -32
  18. package/dest/slasher/factory.js +6 -5
  19. package/dest/slasher/index.js +0 -1
  20. package/dest/slasher/slasher_client.js +128 -122
  21. package/dest/test/index.js +4 -1
  22. package/dest/tx_validator/archive_cache.js +7 -7
  23. package/dest/tx_validator/gas_validator.js +76 -64
  24. package/dest/tx_validator/nullifier_cache.js +8 -8
  25. package/dest/tx_validator/phases_validator.js +27 -19
  26. package/dest/tx_validator/test_utils.js +1 -2
  27. package/dest/tx_validator/tx_validator_factory.js +5 -5
  28. package/package.json +23 -22
  29. package/src/client/sequencer-client.ts +23 -2
  30. package/src/config.ts +1 -1
  31. package/src/publisher/sequencer-publisher.ts +71 -91
  32. package/src/sequencer/sequencer.ts +29 -13
  33. package/src/slasher/factory.ts +3 -3
  34. package/src/slasher/slasher_client.ts +40 -31
  35. package/dest/client/index.d.ts +0 -2
  36. package/dest/client/index.d.ts.map +0 -1
  37. package/dest/client/sequencer-client.d.ts +0 -68
  38. package/dest/client/sequencer-client.d.ts.map +0 -1
  39. package/dest/config.d.ts +0 -37
  40. package/dest/config.d.ts.map +0 -1
  41. package/dest/global_variable_builder/global_builder.d.ts +0 -29
  42. package/dest/global_variable_builder/global_builder.d.ts.map +0 -1
  43. package/dest/global_variable_builder/index.d.ts +0 -2
  44. package/dest/global_variable_builder/index.d.ts.map +0 -1
  45. package/dest/index.d.ts +0 -8
  46. package/dest/index.d.ts.map +0 -1
  47. package/dest/publisher/config.d.ts +0 -38
  48. package/dest/publisher/config.d.ts.map +0 -1
  49. package/dest/publisher/index.d.ts +0 -2
  50. package/dest/publisher/index.d.ts.map +0 -1
  51. package/dest/publisher/sequencer-publisher-metrics.d.ts +0 -24
  52. package/dest/publisher/sequencer-publisher-metrics.d.ts.map +0 -1
  53. package/dest/publisher/sequencer-publisher.d.ts +0 -158
  54. package/dest/publisher/sequencer-publisher.d.ts.map +0 -1
  55. package/dest/sequencer/allowed.d.ts +0 -3
  56. package/dest/sequencer/allowed.d.ts.map +0 -1
  57. package/dest/sequencer/config.d.ts +0 -2
  58. package/dest/sequencer/config.d.ts.map +0 -1
  59. package/dest/sequencer/index.d.ts +0 -4
  60. package/dest/sequencer/index.d.ts.map +0 -1
  61. package/dest/sequencer/metrics.d.ts +0 -24
  62. package/dest/sequencer/metrics.d.ts.map +0 -1
  63. package/dest/sequencer/sequencer.d.ts +0 -172
  64. package/dest/sequencer/sequencer.d.ts.map +0 -1
  65. package/dest/sequencer/timetable.d.ts +0 -38
  66. package/dest/sequencer/timetable.d.ts.map +0 -1
  67. package/dest/sequencer/utils.d.ts +0 -47
  68. package/dest/sequencer/utils.d.ts.map +0 -1
  69. package/dest/slasher/factory.d.ts +0 -11
  70. package/dest/slasher/factory.d.ts.map +0 -1
  71. package/dest/slasher/index.d.ts +0 -3
  72. package/dest/slasher/index.d.ts.map +0 -1
  73. package/dest/slasher/slasher_client.d.ts +0 -127
  74. package/dest/slasher/slasher_client.d.ts.map +0 -1
  75. package/dest/test/index.d.ts +0 -17
  76. package/dest/test/index.d.ts.map +0 -1
  77. package/dest/tx_validator/archive_cache.d.ts +0 -14
  78. package/dest/tx_validator/archive_cache.d.ts.map +0 -1
  79. package/dest/tx_validator/gas_validator.d.ts +0 -12
  80. package/dest/tx_validator/gas_validator.d.ts.map +0 -1
  81. package/dest/tx_validator/nullifier_cache.d.ts +0 -16
  82. package/dest/tx_validator/nullifier_cache.d.ts.map +0 -1
  83. package/dest/tx_validator/phases_validator.d.ts +0 -11
  84. package/dest/tx_validator/phases_validator.d.ts.map +0 -1
  85. package/dest/tx_validator/test_utils.d.ts +0 -21
  86. package/dest/tx_validator/test_utils.d.ts.map +0 -1
  87. package/dest/tx_validator/tx_validator_factory.d.ts +0 -16
  88. package/dest/tx_validator/tx_validator_factory.d.ts.map +0 -1
@@ -1,46 +1,60 @@
1
- var _a;
1
+ import { Blob } from '@aztec/blob-lib';
2
2
  import { createBlobSinkClient } from '@aztec/blob-sink/client';
3
- import { ConsensusPayload, SignatureDomainSeparator, getHashedSignaturePayload, } from '@aztec/circuit-types';
3
+ import { ConsensusPayload, SignatureDomainSeparator, getHashedSignaturePayload } from '@aztec/circuit-types';
4
4
  import { EthAddress } from '@aztec/circuits.js';
5
- import { FormattedViemError, formatViemError, } from '@aztec/ethereum';
5
+ import { FormattedViemError, RollupContract, formatViemError } from '@aztec/ethereum';
6
6
  import { toHex } from '@aztec/foundation/bigint-buffer';
7
- import { Blob } from '@aztec/foundation/blob';
8
7
  import { createLogger } from '@aztec/foundation/log';
9
8
  import { Timer } from '@aztec/foundation/timer';
10
- import { EmpireBaseAbi, ForwarderAbi, RollupAbi } from '@aztec/l1-artifacts';
9
+ import { ForwarderAbi, RollupAbi } from '@aztec/l1-artifacts';
11
10
  import { getTelemetryClient } from '@aztec/telemetry-client';
12
11
  import pick from 'lodash.pick';
13
- import { encodeFunctionData, getAddress, getContract } from 'viem';
12
+ import { encodeFunctionData } from 'viem';
14
13
  import { SequencerPublisherMetrics } from './sequencer-publisher-metrics.js';
15
- export var VoteType;
16
- (function (VoteType) {
14
+ export var VoteType = /*#__PURE__*/ function(VoteType) {
17
15
  VoteType[VoteType["GOVERNANCE"] = 0] = "GOVERNANCE";
18
16
  VoteType[VoteType["SLASHING"] = 1] = "SLASHING";
19
- })(VoteType || (VoteType = {}));
17
+ return VoteType;
18
+ }({});
20
19
  export class SequencerPublisher {
21
- constructor(config, deps) {
22
- this.interrupted = false;
23
- this.governanceLog = createLogger('sequencer:publisher:governance');
24
- this.governancePayload = EthAddress.ZERO;
25
- this.slashingLog = createLogger('sequencer:publisher:slashing');
26
- this.getSlashPayload = undefined;
27
- this.myLastVotes = {
28
- [VoteType.GOVERNANCE]: 0n,
29
- [VoteType.SLASHING]: 0n,
30
- };
31
- this.log = createLogger('sequencer:publisher');
32
- this.requests = [];
20
+ interrupted = false;
21
+ metrics;
22
+ epochCache;
23
+ forwarderContract;
24
+ governanceLog = createLogger('sequencer:publisher:governance');
25
+ governanceProposerAddress;
26
+ governancePayload = EthAddress.ZERO;
27
+ slashingLog = createLogger('sequencer:publisher:slashing');
28
+ slashingProposerAddress;
29
+ getSlashPayload = undefined;
30
+ myLastVotes = {
31
+ [0]: 0n,
32
+ [1]: 0n
33
+ };
34
+ log = createLogger('sequencer:publisher');
35
+ ethereumSlotDuration;
36
+ blobSinkClient;
37
+ // @note - with blobs, the below estimate seems too large.
38
+ // Total used for full block from int_l1_pub e2e test: 1m (of which 86k is 1x blob)
39
+ // Total used for emptier block from above test: 429k (of which 84k is 1x blob)
40
+ static PROPOSE_GAS_GUESS = 12_000_000n;
41
+ static PROPOSE_AND_CLAIM_GAS_GUESS = this.PROPOSE_GAS_GUESS + 100_000n;
42
+ l1TxUtils;
43
+ rollupContract;
44
+ govProposerContract;
45
+ slashingProposerContract;
46
+ requests = [];
47
+ constructor(config, deps){
33
48
  this.ethereumSlotDuration = BigInt(config.ethereumSlotDuration);
34
49
  this.epochCache = deps.epochCache;
35
- if (config.l1Contracts.governanceProposerAddress) {
36
- this.governanceProposerAddress = EthAddress.fromString(config.l1Contracts.governanceProposerAddress.toString());
37
- }
38
50
  this.blobSinkClient = deps.blobSinkClient ?? createBlobSinkClient(config);
39
51
  const telemetry = deps.telemetry ?? getTelemetryClient();
40
52
  this.metrics = new SequencerPublisherMetrics(telemetry, 'SequencerPublisher');
41
53
  this.l1TxUtils = deps.l1TxUtils;
42
54
  this.rollupContract = deps.rollupContract;
43
55
  this.forwarderContract = deps.forwarderContract;
56
+ this.govProposerContract = deps.governanceProposerContract;
57
+ this.slashingProposerContract = deps.slashingProposerContract;
44
58
  }
45
59
  registerSlashPayloadGetter(callback) {
46
60
  this.getSlashPayload = callback;
@@ -64,31 +78,32 @@ export class SequencerPublisher {
64
78
  return this.epochCache.getEpochAndSlotNow().slot;
65
79
  }
66
80
  /**
67
- * Sends all requests that are still valid.
68
- * @returns one of:
69
- * - A receipt and stats if the tx succeeded
70
- * - a receipt and errorMsg if it failed on L1
71
- * - undefined if no valid requests are found OR the tx failed to send.
72
- */
73
- async sendRequests() {
74
- const requestsToProcess = [...this.requests];
81
+ * Sends all requests that are still valid.
82
+ * @returns one of:
83
+ * - A receipt and stats if the tx succeeded
84
+ * - a receipt and errorMsg if it failed on L1
85
+ * - undefined if no valid requests are found OR the tx failed to send.
86
+ */ async sendRequests() {
87
+ const requestsToProcess = [
88
+ ...this.requests
89
+ ];
75
90
  this.requests = [];
76
91
  if (this.interrupted) {
77
92
  return undefined;
78
93
  }
79
94
  const currentL2Slot = this.getCurrentL2Slot();
80
95
  this.log.debug(`Current L2 slot: ${currentL2Slot}`);
81
- const validRequests = requestsToProcess.filter(request => request.lastValidL2Slot >= currentL2Slot);
96
+ const validRequests = requestsToProcess.filter((request)=>request.lastValidL2Slot >= currentL2Slot);
82
97
  if (validRequests.length !== requestsToProcess.length) {
83
98
  this.log.warn(`Some requests were expired for slot ${currentL2Slot}`, {
84
- validRequests: validRequests.map(request => ({
85
- action: request.action,
86
- lastValidL2Slot: request.lastValidL2Slot,
87
- })),
88
- requests: requestsToProcess.map(request => ({
89
- action: request.action,
90
- lastValidL2Slot: request.lastValidL2Slot,
91
- })),
99
+ validRequests: validRequests.map((request)=>({
100
+ action: request.action,
101
+ lastValidL2Slot: request.lastValidL2Slot
102
+ })),
103
+ requests: requestsToProcess.map((request)=>({
104
+ action: request.action,
105
+ lastValidL2Slot: request.lastValidL2Slot
106
+ }))
92
107
  });
93
108
  }
94
109
  if (validRequests.length === 0) {
@@ -98,8 +113,8 @@ export class SequencerPublisher {
98
113
  // @note - we can only have one gas config and one blob config per bundle
99
114
  // find requests with gas and blob configs
100
115
  // See https://github.com/AztecProtocol/aztec-packages/issues/11513
101
- const gasConfigs = requestsToProcess.filter(request => request.gasConfig);
102
- const blobConfigs = requestsToProcess.filter(request => request.blobConfig);
116
+ const gasConfigs = requestsToProcess.filter((request)=>request.gasConfig);
117
+ const blobConfigs = requestsToProcess.filter((request)=>request.blobConfig);
103
118
  if (gasConfigs.length > 1 || blobConfigs.length > 1) {
104
119
  throw new Error('Multiple gas or blob configs found');
105
120
  }
@@ -107,96 +122,97 @@ export class SequencerPublisher {
107
122
  const blobConfig = blobConfigs[0]?.blobConfig;
108
123
  try {
109
124
  this.log.debug('Forwarding transactions', {
110
- validRequests: validRequests.map(request => request.action),
125
+ validRequests: validRequests.map((request)=>request.action)
111
126
  });
112
- const result = await this.forwarderContract.forward(validRequests.map(request => request.request), this.l1TxUtils, gasConfig, blobConfig);
127
+ const result = await this.forwarderContract.forward(validRequests.map((request)=>request.request), this.l1TxUtils, gasConfig, blobConfig, this.log);
113
128
  this.callbackBundledTransactions(validRequests, result);
114
129
  return result;
115
- }
116
- catch (err) {
117
- const { message, metaMessages } = formatViemError(err);
118
- this.log.error(`Failed to publish bundled transactions`, message, { metaMessages });
130
+ } catch (err) {
131
+ const viemError = formatViemError(err);
132
+ this.log.error(`Failed to publish bundled transactions`, viemError);
119
133
  return undefined;
120
134
  }
121
135
  }
122
136
  callbackBundledTransactions(requests, result) {
123
137
  const success = result?.receipt.status === 'success';
124
138
  const logger = success ? this.log.info : this.log.error;
125
- for (const request of requests) {
139
+ for (const request of requests){
126
140
  logger(`Bundled [${request.action}] transaction [${success ? 'succeeded' : 'failed'}]`);
127
141
  request.onResult?.(request.request, result);
128
142
  }
129
143
  }
130
144
  /**
131
- * @notice Will call `canProposeAtNextEthBlock` to make sure that it is possible to propose
132
- * @param tipArchive - The archive to check
133
- * @returns The slot and block number if it is possible to propose, undefined otherwise
134
- */
135
- canProposeAtNextEthBlock(tipArchive) {
136
- const ignoredErrors = ['SlotAlreadyInChain', 'InvalidProposer'];
137
- return this.rollupContract
138
- .canProposeAtNextEthBlock(tipArchive, this.getForwarderAddress().toString(), this.ethereumSlotDuration)
139
- .catch(err => {
140
- if (err instanceof FormattedViemError && ignoredErrors.find(e => err.message.includes(e))) {
145
+ * @notice Will call `canProposeAtNextEthBlock` to make sure that it is possible to propose
146
+ * @param tipArchive - The archive to check
147
+ * @returns The slot and block number if it is possible to propose, undefined otherwise
148
+ */ canProposeAtNextEthBlock(tipArchive) {
149
+ const ignoredErrors = [
150
+ 'SlotAlreadyInChain',
151
+ 'InvalidProposer',
152
+ 'InvalidArchive'
153
+ ];
154
+ return this.rollupContract.canProposeAtNextEthBlock(tipArchive, this.getForwarderAddress().toString(), this.ethereumSlotDuration).catch((err)=>{
155
+ if (err instanceof FormattedViemError && ignoredErrors.find((e)=>err.message.includes(e))) {
141
156
  this.log.debug(err.message);
142
- }
143
- else {
157
+ } else {
144
158
  this.log.error(err.name, err);
145
159
  }
146
160
  return undefined;
147
161
  });
148
162
  }
149
163
  /**
150
- * @returns The epoch that is currently claimable, undefined otherwise
151
- */
152
- getClaimableEpoch() {
153
- const acceptedErrors = ['Rollup__NoEpochToProve', 'Rollup__ProofRightAlreadyClaimed'];
154
- return this.rollupContract.getClaimableEpoch().catch(err => {
155
- if (acceptedErrors.find(e => err.message.includes(e))) {
164
+ * @returns The epoch that is currently claimable, undefined otherwise
165
+ */ getClaimableEpoch() {
166
+ const acceptedErrors = [
167
+ 'Rollup__NoEpochToProve',
168
+ 'Rollup__ProofRightAlreadyClaimed'
169
+ ];
170
+ return this.rollupContract.getClaimableEpoch().catch((err)=>{
171
+ if (acceptedErrors.find((e)=>err.message.includes(e))) {
156
172
  return undefined;
157
173
  }
158
174
  throw err;
159
175
  });
160
176
  }
161
177
  /**
162
- * @notice Will filter out invalid quotes according to L1
163
- * @param quotes - The quotes to filter
164
- * @returns The filtered quotes
165
- */
166
- filterValidQuotes(quotes) {
167
- return Promise.all(quotes.map(x => this.rollupContract
168
- // validate throws if the quote is not valid
178
+ * @notice Will filter out invalid quotes according to L1
179
+ * @param quotes - The quotes to filter
180
+ * @returns The filtered quotes
181
+ */ filterValidQuotes(quotes) {
182
+ return Promise.all(quotes.map((x)=>this.rollupContract// validate throws if the quote is not valid
169
183
  // else returns void
170
- .validateProofQuote(x.toViemArgs(), this.getForwarderAddress().toString(), this.ethereumSlotDuration)
171
- .then(() => x)
172
- .catch(err => {
173
- this.log.error(`Failed to validate proof quote`, err, { quote: x.toInspect() });
174
- return undefined;
175
- }))).then(quotes => quotes.filter((q) => !!q));
184
+ .validateProofQuote(x.toViemArgs(), this.getForwarderAddress().toString(), this.ethereumSlotDuration).then(()=>x).catch((err)=>{
185
+ this.log.error(`Failed to validate proof quote`, err, {
186
+ quote: x.toInspect()
187
+ });
188
+ return undefined;
189
+ }))).then((quotes)=>quotes.filter((q)=>!!q));
176
190
  }
177
191
  /**
178
- * @notice Will call `validateHeader` to make sure that it is possible to propose
179
- *
180
- * @dev Throws if unable to propose
181
- *
182
- * @param header - The header to propose
183
- * @param digest - The digest that attestations are signing over
184
- *
185
- */
186
- async validateBlockForSubmission(header, attestationData = {
192
+ * @notice Will call `validateHeader` to make sure that it is possible to propose
193
+ *
194
+ * @dev Throws if unable to propose
195
+ *
196
+ * @param header - The header to propose
197
+ * @param digest - The digest that attestations are signing over
198
+ *
199
+ */ async validateBlockForSubmission(header, attestationData = {
187
200
  digest: Buffer.alloc(32),
188
- signatures: [],
201
+ signatures: []
189
202
  }) {
190
203
  const ts = BigInt((await this.l1TxUtils.getBlock()).timestamp + this.ethereumSlotDuration);
191
- const formattedSignatures = attestationData.signatures.map(attest => attest.toViemSignature());
192
- const flags = { ignoreDA: true, ignoreSignatures: formattedSignatures.length == 0 };
204
+ const formattedSignatures = attestationData.signatures.map((attest)=>attest.toViemSignature());
205
+ const flags = {
206
+ ignoreDA: true,
207
+ ignoreSignatures: formattedSignatures.length == 0
208
+ };
193
209
  const args = [
194
210
  `0x${header.toBuffer().toString('hex')}`,
195
211
  formattedSignatures,
196
212
  `0x${attestationData.digest.toString('hex')}`,
197
213
  ts,
198
214
  `0x${header.contentCommitment.blobsHash.toString('hex')}`,
199
- flags,
215
+ flags
200
216
  ];
201
217
  await this.rollupContract.validateHeader(args, this.getForwarderAddress().toString());
202
218
  return ts;
@@ -205,106 +221,81 @@ export class SequencerPublisher {
205
221
  const committee = await this.rollupContract.getCurrentEpochCommittee();
206
222
  return committee.map(EthAddress.fromString);
207
223
  }
208
- /**
209
- * Enqueues a castVote transaction to cast a vote for a given slot number.
210
- * @param slotNumber - The slot number to cast a vote for.
211
- * @param timestamp - The timestamp of the slot to cast a vote for.
212
- * @param voteType - The type of vote to cast.
213
- * @returns True if the vote was successfully enqueued, false otherwise.
214
- */
215
- async enqueueCastVote(slotNumber, timestamp, voteType) {
216
- // @todo This function can be optimized by doing some of the computations locally instead of calling the L1 contracts
224
+ async enqueueCastVoteHelper(slotNumber, timestamp, voteType, payload, base) {
217
225
  if (this.myLastVotes[voteType] >= slotNumber) {
218
226
  return false;
219
227
  }
220
- const voteConfig = async () => {
221
- if (voteType === VoteType.GOVERNANCE) {
222
- if (this.governancePayload.equals(EthAddress.ZERO)) {
223
- return undefined;
224
- }
225
- if (!this.governanceProposerAddress) {
226
- return undefined;
227
- }
228
- return {
229
- payload: this.governancePayload,
230
- voteContractAddress: this.governanceProposerAddress,
231
- logger: this.governanceLog,
232
- };
233
- }
234
- else if (voteType === VoteType.SLASHING) {
235
- if (!this.getSlashPayload) {
236
- return undefined;
237
- }
238
- const slashingProposerAddress = await this.rollupContract.getSlashingProposerAddress();
239
- if (!slashingProposerAddress) {
240
- return undefined;
241
- }
242
- const slashPayload = await this.getSlashPayload(slotNumber);
243
- if (!slashPayload) {
244
- return undefined;
245
- }
246
- return {
247
- payload: slashPayload,
248
- voteContractAddress: slashingProposerAddress,
249
- logger: this.slashingLog,
250
- };
251
- }
252
- else {
253
- throw new Error('Invalid vote type');
254
- }
255
- };
256
- const vConfig = await voteConfig();
257
- if (!vConfig) {
228
+ if (payload.equals(EthAddress.ZERO)) {
258
229
  return false;
259
230
  }
260
- const { payload, voteContractAddress } = vConfig;
261
- const voteContract = getContract({
262
- address: getAddress(voteContractAddress.toString()),
263
- abi: EmpireBaseAbi,
264
- client: this.l1TxUtils.walletClient,
265
- });
266
- const [proposer, roundNumber] = await Promise.all([
231
+ const round = await base.computeRound(slotNumber);
232
+ const [proposer, roundInfo] = await Promise.all([
267
233
  this.rollupContract.getProposerAt(timestamp),
268
- voteContract.read.computeRound([slotNumber]),
234
+ base.getRoundInfo(this.rollupContract.address, round)
269
235
  ]);
270
236
  if (proposer.toLowerCase() !== this.getForwarderAddress().toString().toLowerCase()) {
271
237
  return false;
272
238
  }
273
- const [slotForLastVote] = await voteContract.read.rounds([this.rollupContract.address, roundNumber]);
274
- if (slotForLastVote >= slotNumber) {
239
+ if (roundInfo.lastVote >= slotNumber) {
275
240
  return false;
276
241
  }
277
242
  const cachedLastVote = this.myLastVotes[voteType];
278
243
  this.myLastVotes[voteType] = slotNumber;
279
244
  this.addRequest({
280
- action: voteType === VoteType.GOVERNANCE ? 'governance-vote' : 'slashing-vote',
281
- request: {
282
- to: voteContractAddress.toString(),
283
- data: encodeFunctionData({
284
- abi: EmpireBaseAbi,
285
- functionName: 'vote',
286
- args: [payload.toString()],
287
- }),
288
- },
245
+ action: voteType === 0 ? 'governance-vote' : 'slashing-vote',
246
+ request: base.createVoteRequest(payload.toString()),
289
247
  lastValidL2Slot: slotNumber,
290
- onResult: (_request, result) => {
248
+ onResult: (_request, result)=>{
291
249
  if (!result || result.receipt.status !== 'success') {
292
250
  this.myLastVotes[voteType] = cachedLastVote;
251
+ } else {
252
+ this.log.info(`Cast [${voteType}] vote for slot ${slotNumber}`);
293
253
  }
294
- else {
295
- this.log.info(`Cast ${voteType} vote for slot ${slotNumber}`);
296
- }
297
- },
254
+ }
298
255
  });
299
256
  return true;
300
257
  }
258
+ async getVoteConfig(slotNumber, voteType) {
259
+ if (voteType === 0) {
260
+ return {
261
+ payload: this.governancePayload,
262
+ base: this.govProposerContract
263
+ };
264
+ } else if (voteType === 1) {
265
+ if (!this.getSlashPayload) {
266
+ return undefined;
267
+ }
268
+ const slashPayload = await this.getSlashPayload(slotNumber);
269
+ if (!slashPayload) {
270
+ return undefined;
271
+ }
272
+ return {
273
+ payload: slashPayload,
274
+ base: this.slashingProposerContract
275
+ };
276
+ }
277
+ throw new Error('Unreachable: Invalid vote type');
278
+ }
301
279
  /**
302
- * Proposes a L2 block on L1.
303
- *
304
- * @param block - L2 block to propose.
305
- * @returns True if the tx has been enqueued, throws otherwise. See #9315
306
- */
307
- async enqueueProposeL2Block(block, attestations, txHashes, opts = {}) {
280
+ * Enqueues a castVote transaction to cast a vote for a given slot number.
281
+ * @param slotNumber - The slot number to cast a vote for.
282
+ * @param timestamp - The timestamp of the slot to cast a vote for.
283
+ * @param voteType - The type of vote to cast.
284
+ * @returns True if the vote was successfully enqueued, false otherwise.
285
+ */ async enqueueCastVote(slotNumber, timestamp, voteType) {
286
+ const voteConfig = await this.getVoteConfig(slotNumber, voteType);
287
+ if (!voteConfig) {
288
+ return false;
289
+ }
290
+ const { payload, base } = voteConfig;
291
+ return this.enqueueCastVoteHelper(slotNumber, timestamp, voteType, payload, base);
292
+ }
293
+ /**
294
+ * Proposes a L2 block on L1.
295
+ *
296
+ * @param block - L2 block to propose.
297
+ * @returns True if the tx has been enqueued, throws otherwise. See #9315
298
+ */ async enqueueProposeL2Block(block, attestations, txHashes, opts = {}) {
308
299
  const consensusPayload = new ConsensusPayload(block.header, block.archive.root, txHashes ?? []);
309
300
  const digest = await getHashedSignaturePayload(consensusPayload, SignatureDomainSeparator.blockAttestation);
310
301
  const blobs = await Blob.getBlobs(block.body.toBlobFields());
@@ -315,7 +306,7 @@ export class SequencerPublisher {
315
306
  body: block.body.toBuffer(),
316
307
  blobs,
317
308
  attestations,
318
- txHashes: txHashes ?? [],
309
+ txHashes: txHashes ?? []
319
310
  };
320
311
  // @note This will make sure that we are passing the checks for our header ASSUMING that the data is also made available
321
312
  // This means that we can avoid the simulation issues in later checks.
@@ -323,14 +314,13 @@ export class SequencerPublisher {
323
314
  // make time consistency checks break.
324
315
  const ts = await this.validateBlockForSubmission(block.header, {
325
316
  digest: digest.toBuffer(),
326
- signatures: attestations ?? [],
317
+ signatures: attestations ?? []
327
318
  });
328
319
  this.log.debug(`Submitting propose transaction`);
329
320
  await this.addProposeTx(block, proposeTxArgs, opts, ts);
330
321
  return true;
331
322
  }
332
- /** Enqueues a claimEpochProofRight transaction to submit a chosen prover quote for the previous epoch. */
333
- enqueueClaimEpochProofRight(proofQuote) {
323
+ /** Enqueues a claimEpochProofRight transaction to submit a chosen prover quote for the previous epoch. */ enqueueClaimEpochProofRight(proofQuote) {
334
324
  const timer = new Timer();
335
325
  this.addRequest({
336
326
  action: 'claim',
@@ -339,11 +329,13 @@ export class SequencerPublisher {
339
329
  data: encodeFunctionData({
340
330
  abi: RollupAbi,
341
331
  functionName: 'claimEpochProofRight',
342
- args: [proofQuote.toViemArgs()],
343
- }),
332
+ args: [
333
+ proofQuote.toViemArgs()
334
+ ]
335
+ })
344
336
  },
345
337
  lastValidL2Slot: this.getCurrentL2Slot(),
346
- onResult: (_request, result) => {
338
+ onResult: (_request, result)=>{
347
339
  if (!result) {
348
340
  return;
349
341
  }
@@ -355,66 +347,65 @@ export class SequencerPublisher {
355
347
  transactionHash: receipt.transactionHash,
356
348
  blobDataGas: 0n,
357
349
  blobGasUsed: 0n,
358
- ...pick(stats, 'calldataGas', 'calldataSize', 'sender'),
350
+ ...pick(stats, 'calldataGas', 'calldataSize', 'sender')
359
351
  };
360
352
  this.log.verbose(`Submitted claim epoch proof right to L1 rollup contract`, {
361
353
  ...publishStats,
362
- ...proofQuote.toInspect(),
354
+ ...proofQuote.toInspect()
363
355
  });
364
356
  this.metrics.recordClaimEpochProofRightTx(timer.ms(), publishStats);
365
- }
366
- else {
357
+ } else {
367
358
  this.metrics.recordFailedTx('claimEpochProofRight');
368
359
  // TODO: Get the error message from the reverted tx
369
360
  this.log.error(`Claim epoch proof right tx reverted`, {
370
361
  txHash: receipt.transactionHash,
371
- ...proofQuote.toInspect(),
362
+ ...proofQuote.toInspect()
372
363
  });
373
364
  }
374
- },
365
+ }
375
366
  });
376
367
  return true;
377
368
  }
378
369
  /**
379
- * Calling `interrupt` will cause any in progress call to `publishRollup` to return `false` asap.
380
- * Be warned, the call may return false even if the tx subsequently gets successfully mined.
381
- * In practice this shouldn't matter, as we'll only ever be calling `interrupt` when we know it's going to fail.
382
- * A call to `restart` is required before you can continue publishing.
383
- */
384
- interrupt() {
370
+ * Calling `interrupt` will cause any in progress call to `publishRollup` to return `false` asap.
371
+ * Be warned, the call may return false even if the tx subsequently gets successfully mined.
372
+ * In practice this shouldn't matter, as we'll only ever be calling `interrupt` when we know it's going to fail.
373
+ * A call to `restart` is required before you can continue publishing.
374
+ */ interrupt() {
385
375
  this.interrupted = true;
386
376
  this.l1TxUtils.interrupt();
387
377
  }
388
- /** Restarts the publisher after calling `interrupt`. */
389
- restart() {
378
+ /** Restarts the publisher after calling `interrupt`. */ restart() {
390
379
  this.interrupted = false;
391
380
  this.l1TxUtils.restart();
392
381
  }
393
382
  async prepareProposeTx(encodedData, timestamp) {
394
383
  const kzg = Blob.getViemKzgInstance();
395
384
  const blobInput = Blob.getEthBlobEvaluationInputs(encodedData.blobs);
396
- this.log.debug('Validating blob input', { blobInput });
397
- const blobEvaluationGas = await this.l1TxUtils
398
- .estimateGas(this.l1TxUtils.walletClient.account, {
385
+ this.log.debug('Validating blob input', {
386
+ blobInput
387
+ });
388
+ const blobEvaluationGas = await this.l1TxUtils.estimateGas(this.l1TxUtils.walletClient.account, {
399
389
  to: this.rollupContract.address,
400
390
  data: encodeFunctionData({
401
391
  abi: RollupAbi,
402
392
  functionName: 'validateBlobs',
403
- args: [blobInput],
404
- }),
393
+ args: [
394
+ blobInput
395
+ ]
396
+ })
405
397
  }, {}, {
406
- blobs: encodedData.blobs.map(b => b.data),
407
- kzg,
408
- })
409
- .catch(err => {
398
+ blobs: encodedData.blobs.map((b)=>b.data),
399
+ kzg
400
+ }).catch((err)=>{
410
401
  const { message, metaMessages } = formatViemError(err);
411
- this.log.error(`Failed to validate blobs`, message, { metaMessages });
402
+ this.log.error(`Failed to validate blobs`, message, {
403
+ metaMessages
404
+ });
412
405
  throw new Error('Failed to validate blobs');
413
406
  });
414
- const attestations = encodedData.attestations
415
- ? encodedData.attestations.map(attest => attest.toViemSignature())
416
- : [];
417
- const txHashes = encodedData.txHashes ? encodedData.txHashes.map(txHash => txHash.toString()) : [];
407
+ const attestations = encodedData.attestations ? encodedData.attestations.map((attest)=>attest.toViemSignature()) : [];
408
+ const txHashes = encodedData.txHashes ? encodedData.txHashes.map((txHash)=>txHash.toString()) : [];
418
409
  const args = [
419
410
  {
420
411
  header: `0x${encodedData.header.toString('hex')}`,
@@ -422,79 +413,92 @@ export class SequencerPublisher {
422
413
  oracleInput: {
423
414
  // We are currently not modifying these. See #9963
424
415
  feeAssetPriceModifier: 0n,
425
- provingCostModifier: 0n,
416
+ provingCostModifier: 0n
426
417
  },
427
418
  blockHash: `0x${encodedData.blockHash.toString('hex')}`,
428
- txHashes,
419
+ txHashes
429
420
  },
430
421
  attestations,
431
422
  // TODO(#9101): Extract blobs from beacon chain => calldata will only contain what's needed to verify blob and body input can be removed
432
423
  `0x${encodedData.body.toString('hex')}`,
433
- blobInput,
424
+ blobInput
434
425
  ];
435
426
  const rollupData = encodeFunctionData({
436
427
  abi: RollupAbi,
437
428
  functionName: 'propose',
438
- args,
429
+ args
439
430
  });
440
431
  const forwarderData = encodeFunctionData({
441
432
  abi: ForwarderAbi,
442
433
  functionName: 'forward',
443
- args: [[this.rollupContract.address], [rollupData]],
434
+ args: [
435
+ [
436
+ this.rollupContract.address
437
+ ],
438
+ [
439
+ rollupData
440
+ ]
441
+ ]
444
442
  });
445
- const simulationResult = await this.l1TxUtils
446
- .simulateGasUsed({
443
+ const simulationResult = await this.l1TxUtils.simulateGasUsed({
447
444
  to: this.getForwarderAddress().toString(),
448
445
  data: forwarderData,
449
- gas: _a.PROPOSE_GAS_GUESS,
446
+ gas: SequencerPublisher.PROPOSE_GAS_GUESS
450
447
  }, {
451
448
  // @note we add 1n to the timestamp because geth implementation doesn't like simulation timestamp to be equal to the current block timestamp
452
449
  time: timestamp + 1n,
453
450
  // @note reth should have a 30m gas limit per block but throws errors that this tx is beyond limit
454
- gasLimit: _a.PROPOSE_GAS_GUESS * 2n,
451
+ gasLimit: SequencerPublisher.PROPOSE_GAS_GUESS * 2n
455
452
  }, [
456
453
  {
457
454
  address: this.rollupContract.address,
458
455
  // @note we override checkBlob to false since blobs are not part simulate()
459
456
  stateDiff: [
460
457
  {
461
- slot: toHex(9n, true),
462
- value: toHex(0n, true),
463
- },
464
- ],
465
- },
458
+ slot: toHex(RollupContract.checkBlobStorageSlot, true),
459
+ value: toHex(0n, true)
460
+ }
461
+ ]
462
+ }
466
463
  ], {
467
464
  // @note fallback gas estimate to use if the node doesn't support simulation API
468
- fallbackGasEstimate: _a.PROPOSE_GAS_GUESS,
469
- })
470
- .catch(err => {
465
+ fallbackGasEstimate: SequencerPublisher.PROPOSE_GAS_GUESS
466
+ }).catch((err)=>{
471
467
  const { message, metaMessages } = formatViemError(err);
472
- this.log.error(`Failed to simulate gas used`, message, { metaMessages });
468
+ this.log.error(`Failed to simulate gas used`, message, {
469
+ metaMessages
470
+ });
473
471
  throw new Error('Failed to simulate gas used');
474
472
  });
475
- return { args, blobEvaluationGas, rollupData, simulationResult };
473
+ return {
474
+ args,
475
+ blobEvaluationGas,
476
+ rollupData,
477
+ simulationResult
478
+ };
476
479
  }
477
480
  async addProposeTx(block, encodedData, opts = {}, timestamp) {
478
481
  const timer = new Timer();
479
482
  const kzg = Blob.getViemKzgInstance();
480
483
  const { rollupData, simulationResult, blobEvaluationGas } = await this.prepareProposeTx(encodedData, timestamp);
481
484
  const startBlock = await this.l1TxUtils.getBlockNumber();
485
+ const blockHash = await block.hash();
482
486
  return this.addRequest({
483
487
  action: 'propose',
484
488
  request: {
485
489
  to: this.rollupContract.address,
486
- data: rollupData,
490
+ data: rollupData
487
491
  },
488
492
  lastValidL2Slot: block.header.globalVariables.slotNumber.toBigInt(),
489
493
  gasConfig: {
490
494
  ...opts,
491
- gasLimit: this.l1TxUtils.bumpGasLimit(simulationResult + blobEvaluationGas),
495
+ gasLimit: this.l1TxUtils.bumpGasLimit(simulationResult + blobEvaluationGas)
492
496
  },
493
497
  blobConfig: {
494
- blobs: encodedData.blobs.map(b => b.data),
495
- kzg,
498
+ blobs: encodedData.blobs.map((b)=>b.data),
499
+ kzg
496
500
  },
497
- onResult: (request, result) => {
501
+ onResult: (request, result)=>{
498
502
  if (!result) {
499
503
  return;
500
504
  }
@@ -512,44 +516,38 @@ export class SequencerPublisher {
512
516
  ...block.getStats(),
513
517
  eventName: 'rollup-published-to-l1',
514
518
  blobCount: encodedData.blobs.length,
515
- inclusionBlocks,
519
+ inclusionBlocks
516
520
  };
517
- this.log.verbose(`Published L2 block to L1 rollup contract`, { ...stats, ...block.getStats() });
521
+ this.log.verbose(`Published L2 block to L1 rollup contract`, {
522
+ ...stats,
523
+ ...block.getStats()
524
+ });
518
525
  this.metrics.recordProcessBlockTx(timer.ms(), publishStats);
519
526
  // Send the blobs to the blob sink
520
- this.sendBlobsToBlobSink(receipt.blockHash, encodedData.blobs).catch(_err => {
527
+ this.sendBlobsToBlobSink(receipt.blockHash, encodedData.blobs).catch((_err)=>{
521
528
  this.log.error('Failed to send blobs to blob sink');
522
529
  });
523
530
  return true;
524
- }
525
- else {
531
+ } else {
526
532
  this.metrics.recordFailedTx('process');
527
533
  this.log.error(`Rollup process tx reverted. ${errorMsg ?? 'No error message'}`, undefined, {
528
534
  ...block.getStats(),
529
535
  txHash: receipt.transactionHash,
530
- blockHash: block.hash().toString(),
531
- slotNumber: block.header.globalVariables.slotNumber.toBigInt(),
536
+ blockHash,
537
+ slotNumber: block.header.globalVariables.slotNumber.toBigInt()
532
538
  });
533
539
  }
534
- },
540
+ }
535
541
  });
536
542
  }
537
543
  /**
538
- * Send blobs to the blob sink
539
- *
540
- * If a blob sink url is configured, then we send blobs to the blob sink
541
- * - for now we use the blockHash as the identifier for the blobs;
542
- * In the future this will move to be the beacon block id - which takes a bit more work
543
- * to calculate and will need to be mocked in e2e tests
544
- */
545
- sendBlobsToBlobSink(blockHash, blobs) {
544
+ * Send blobs to the blob sink
545
+ *
546
+ * If a blob sink url is configured, then we send blobs to the blob sink
547
+ * - for now we use the blockHash as the identifier for the blobs;
548
+ * In the future this will move to be the beacon block id - which takes a bit more work
549
+ * to calculate and will need to be mocked in e2e tests
550
+ */ sendBlobsToBlobSink(blockHash, blobs) {
546
551
  return this.blobSinkClient.sendBlobsToBlobSink(blockHash, blobs);
547
552
  }
548
553
  }
549
- _a = SequencerPublisher;
550
- // @note - with blobs, the below estimate seems too large.
551
- // Total used for full block from int_l1_pub e2e test: 1m (of which 86k is 1x blob)
552
- // Total used for emptier block from above test: 429k (of which 84k is 1x blob)
553
- SequencerPublisher.PROPOSE_GAS_GUESS = 12000000n;
554
- SequencerPublisher.PROPOSE_AND_CLAIM_GAS_GUESS = _a.PROPOSE_GAS_GUESS + 100000n;
555
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2VxdWVuY2VyLXB1Ymxpc2hlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9wdWJsaXNoZXIvc2VxdWVuY2VyLXB1Ymxpc2hlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUEsT0FBTyxFQUFnQyxvQkFBb0IsRUFBRSxNQUFNLHlCQUF5QixDQUFDO0FBQzdGLE9BQU8sRUFDTCxnQkFBZ0IsRUFHaEIsd0JBQXdCLEVBRXhCLHlCQUF5QixHQUMxQixNQUFNLHNCQUFzQixDQUFDO0FBRTlCLE9BQU8sRUFBb0IsVUFBVSxFQUFFLE1BQU0sb0JBQW9CLENBQUM7QUFFbEUsT0FBTyxFQUNMLGtCQUFrQixFQVVsQixlQUFlLEdBQ2hCLE1BQU0saUJBQWlCLENBQUM7QUFDekIsT0FBTyxFQUFFLEtBQUssRUFBRSxNQUFNLGlDQUFpQyxDQUFDO0FBQ3hELE9BQU8sRUFBRSxJQUFJLEVBQUUsTUFBTSx3QkFBd0IsQ0FBQztBQUU5QyxPQUFPLEVBQWUsWUFBWSxFQUFFLE1BQU0sdUJBQXVCLENBQUM7QUFDbEUsT0FBTyxFQUFFLEtBQUssRUFBRSxNQUFNLHlCQUF5QixDQUFDO0FBQ2hELE9BQU8sRUFBRSxhQUFhLEVBQUUsWUFBWSxFQUFFLFNBQVMsRUFBRSxNQUFNLHFCQUFxQixDQUFDO0FBQzdFLE9BQU8sRUFBd0Isa0JBQWtCLEVBQUUsTUFBTSx5QkFBeUIsQ0FBQztBQUVuRixPQUFPLElBQUksTUFBTSxhQUFhLENBQUM7QUFDL0IsT0FBTyxFQUEyQixrQkFBa0IsRUFBRSxVQUFVLEVBQUUsV0FBVyxFQUFFLE1BQU0sTUFBTSxDQUFDO0FBRzVGLE9BQU8sRUFBRSx5QkFBeUIsRUFBRSxNQUFNLGtDQUFrQyxDQUFDO0FBb0I3RSxNQUFNLENBQU4sSUFBWSxRQUdYO0FBSEQsV0FBWSxRQUFRO0lBQ2xCLG1EQUFVLENBQUE7SUFDViwrQ0FBUSxDQUFBO0FBQ1YsQ0FBQyxFQUhXLFFBQVEsS0FBUixRQUFRLFFBR25CO0FBaUJELE1B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