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