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