@aztec/sequencer-client 3.0.3 → 3.9.9-nightly.20260312
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dest/client/sequencer-client.d.ts +32 -15
- package/dest/client/sequencer-client.d.ts.map +1 -1
- package/dest/client/sequencer-client.js +116 -27
- package/dest/config.d.ts +29 -4
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +103 -40
- package/dest/global_variable_builder/global_builder.d.ts +17 -11
- package/dest/global_variable_builder/global_builder.d.ts.map +1 -1
- package/dest/global_variable_builder/global_builder.js +48 -39
- package/dest/index.d.ts +2 -3
- package/dest/index.d.ts.map +1 -1
- package/dest/index.js +1 -2
- package/dest/publisher/config.d.ts +33 -19
- package/dest/publisher/config.d.ts.map +1 -1
- package/dest/publisher/config.js +102 -43
- package/dest/publisher/sequencer-publisher-factory.d.ts +13 -5
- package/dest/publisher/sequencer-publisher-factory.d.ts.map +1 -1
- package/dest/publisher/sequencer-publisher-factory.js +14 -3
- package/dest/publisher/sequencer-publisher-metrics.d.ts +3 -3
- package/dest/publisher/sequencer-publisher-metrics.d.ts.map +1 -1
- package/dest/publisher/sequencer-publisher-metrics.js +23 -86
- package/dest/publisher/sequencer-publisher.d.ts +58 -45
- package/dest/publisher/sequencer-publisher.d.ts.map +1 -1
- package/dest/publisher/sequencer-publisher.js +628 -139
- package/dest/sequencer/checkpoint_proposal_job.d.ts +100 -0
- package/dest/sequencer/checkpoint_proposal_job.d.ts.map +1 -0
- package/dest/sequencer/checkpoint_proposal_job.js +1234 -0
- package/dest/sequencer/checkpoint_voter.d.ts +35 -0
- package/dest/sequencer/checkpoint_voter.d.ts.map +1 -0
- package/dest/sequencer/checkpoint_voter.js +109 -0
- package/dest/sequencer/events.d.ts +46 -0
- package/dest/sequencer/events.d.ts.map +1 -0
- package/dest/sequencer/events.js +1 -0
- package/dest/sequencer/index.d.ts +4 -2
- package/dest/sequencer/index.d.ts.map +1 -1
- package/dest/sequencer/index.js +3 -1
- package/dest/sequencer/metrics.d.ts +37 -5
- package/dest/sequencer/metrics.d.ts.map +1 -1
- package/dest/sequencer/metrics.js +216 -72
- package/dest/sequencer/sequencer.d.ts +122 -131
- package/dest/sequencer/sequencer.d.ts.map +1 -1
- package/dest/sequencer/sequencer.js +705 -635
- package/dest/sequencer/timetable.d.ts +54 -16
- package/dest/sequencer/timetable.d.ts.map +1 -1
- package/dest/sequencer/timetable.js +147 -62
- package/dest/sequencer/types.d.ts +3 -0
- package/dest/sequencer/types.d.ts.map +1 -0
- package/dest/sequencer/types.js +1 -0
- package/dest/sequencer/utils.d.ts +14 -8
- package/dest/sequencer/utils.d.ts.map +1 -1
- package/dest/sequencer/utils.js +7 -4
- package/dest/test/index.d.ts +5 -6
- package/dest/test/index.d.ts.map +1 -1
- package/dest/test/mock_checkpoint_builder.d.ts +95 -0
- package/dest/test/mock_checkpoint_builder.d.ts.map +1 -0
- package/dest/test/mock_checkpoint_builder.js +233 -0
- package/dest/test/utils.d.ts +53 -0
- package/dest/test/utils.d.ts.map +1 -0
- package/dest/test/utils.js +104 -0
- package/package.json +30 -28
- package/src/client/sequencer-client.ts +154 -45
- package/src/config.ts +119 -45
- package/src/global_variable_builder/global_builder.ts +57 -51
- package/src/index.ts +1 -7
- package/src/publisher/config.ts +115 -46
- package/src/publisher/sequencer-publisher-factory.ts +26 -9
- package/src/publisher/sequencer-publisher-metrics.ts +19 -71
- package/src/publisher/sequencer-publisher.ts +334 -176
- package/src/sequencer/README.md +531 -0
- package/src/sequencer/checkpoint_proposal_job.ts +950 -0
- package/src/sequencer/checkpoint_voter.ts +130 -0
- package/src/sequencer/events.ts +27 -0
- package/src/sequencer/index.ts +3 -1
- package/src/sequencer/metrics.ts +267 -81
- package/src/sequencer/sequencer.ts +444 -834
- package/src/sequencer/timetable.ts +178 -83
- package/src/sequencer/types.ts +6 -0
- package/src/sequencer/utils.ts +18 -9
- package/src/test/index.ts +4 -5
- package/src/test/mock_checkpoint_builder.ts +325 -0
- package/src/test/utils.ts +167 -0
- package/dest/sequencer/block_builder.d.ts +0 -28
- package/dest/sequencer/block_builder.d.ts.map +0 -1
- package/dest/sequencer/block_builder.js +0 -127
- package/dest/tx_validator/nullifier_cache.d.ts +0 -14
- package/dest/tx_validator/nullifier_cache.d.ts.map +0 -1
- package/dest/tx_validator/nullifier_cache.js +0 -24
- package/dest/tx_validator/tx_validator_factory.d.ts +0 -18
- package/dest/tx_validator/tx_validator_factory.d.ts.map +0 -1
- package/dest/tx_validator/tx_validator_factory.js +0 -53
- package/src/sequencer/block_builder.ts +0 -214
- package/src/tx_validator/nullifier_cache.ts +0 -30
- package/src/tx_validator/tx_validator_factory.ts +0 -133
|
@@ -1,20 +1,395 @@
|
|
|
1
|
+
function applyDecs2203RFactory() {
|
|
2
|
+
function createAddInitializerMethod(initializers, decoratorFinishedRef) {
|
|
3
|
+
return function addInitializer(initializer) {
|
|
4
|
+
assertNotFinished(decoratorFinishedRef, "addInitializer");
|
|
5
|
+
assertCallable(initializer, "An initializer");
|
|
6
|
+
initializers.push(initializer);
|
|
7
|
+
};
|
|
8
|
+
}
|
|
9
|
+
function memberDec(dec, name, desc, initializers, kind, isStatic, isPrivate, metadata, value) {
|
|
10
|
+
var kindStr;
|
|
11
|
+
switch(kind){
|
|
12
|
+
case 1:
|
|
13
|
+
kindStr = "accessor";
|
|
14
|
+
break;
|
|
15
|
+
case 2:
|
|
16
|
+
kindStr = "method";
|
|
17
|
+
break;
|
|
18
|
+
case 3:
|
|
19
|
+
kindStr = "getter";
|
|
20
|
+
break;
|
|
21
|
+
case 4:
|
|
22
|
+
kindStr = "setter";
|
|
23
|
+
break;
|
|
24
|
+
default:
|
|
25
|
+
kindStr = "field";
|
|
26
|
+
}
|
|
27
|
+
var ctx = {
|
|
28
|
+
kind: kindStr,
|
|
29
|
+
name: isPrivate ? "#" + name : name,
|
|
30
|
+
static: isStatic,
|
|
31
|
+
private: isPrivate,
|
|
32
|
+
metadata: metadata
|
|
33
|
+
};
|
|
34
|
+
var decoratorFinishedRef = {
|
|
35
|
+
v: false
|
|
36
|
+
};
|
|
37
|
+
ctx.addInitializer = createAddInitializerMethod(initializers, decoratorFinishedRef);
|
|
38
|
+
var get, set;
|
|
39
|
+
if (kind === 0) {
|
|
40
|
+
if (isPrivate) {
|
|
41
|
+
get = desc.get;
|
|
42
|
+
set = desc.set;
|
|
43
|
+
} else {
|
|
44
|
+
get = function() {
|
|
45
|
+
return this[name];
|
|
46
|
+
};
|
|
47
|
+
set = function(v) {
|
|
48
|
+
this[name] = v;
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
} else if (kind === 2) {
|
|
52
|
+
get = function() {
|
|
53
|
+
return desc.value;
|
|
54
|
+
};
|
|
55
|
+
} else {
|
|
56
|
+
if (kind === 1 || kind === 3) {
|
|
57
|
+
get = function() {
|
|
58
|
+
return desc.get.call(this);
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
if (kind === 1 || kind === 4) {
|
|
62
|
+
set = function(v) {
|
|
63
|
+
desc.set.call(this, v);
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
ctx.access = get && set ? {
|
|
68
|
+
get: get,
|
|
69
|
+
set: set
|
|
70
|
+
} : get ? {
|
|
71
|
+
get: get
|
|
72
|
+
} : {
|
|
73
|
+
set: set
|
|
74
|
+
};
|
|
75
|
+
try {
|
|
76
|
+
return dec(value, ctx);
|
|
77
|
+
} finally{
|
|
78
|
+
decoratorFinishedRef.v = true;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
function assertNotFinished(decoratorFinishedRef, fnName) {
|
|
82
|
+
if (decoratorFinishedRef.v) {
|
|
83
|
+
throw new Error("attempted to call " + fnName + " after decoration was finished");
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
function assertCallable(fn, hint) {
|
|
87
|
+
if (typeof fn !== "function") {
|
|
88
|
+
throw new TypeError(hint + " must be a function");
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
function assertValidReturnValue(kind, value) {
|
|
92
|
+
var type = typeof value;
|
|
93
|
+
if (kind === 1) {
|
|
94
|
+
if (type !== "object" || value === null) {
|
|
95
|
+
throw new TypeError("accessor decorators must return an object with get, set, or init properties or void 0");
|
|
96
|
+
}
|
|
97
|
+
if (value.get !== undefined) {
|
|
98
|
+
assertCallable(value.get, "accessor.get");
|
|
99
|
+
}
|
|
100
|
+
if (value.set !== undefined) {
|
|
101
|
+
assertCallable(value.set, "accessor.set");
|
|
102
|
+
}
|
|
103
|
+
if (value.init !== undefined) {
|
|
104
|
+
assertCallable(value.init, "accessor.init");
|
|
105
|
+
}
|
|
106
|
+
} else if (type !== "function") {
|
|
107
|
+
var hint;
|
|
108
|
+
if (kind === 0) {
|
|
109
|
+
hint = "field";
|
|
110
|
+
} else if (kind === 10) {
|
|
111
|
+
hint = "class";
|
|
112
|
+
} else {
|
|
113
|
+
hint = "method";
|
|
114
|
+
}
|
|
115
|
+
throw new TypeError(hint + " decorators must return a function or void 0");
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
function applyMemberDec(ret, base, decInfo, name, kind, isStatic, isPrivate, initializers, metadata) {
|
|
119
|
+
var decs = decInfo[0];
|
|
120
|
+
var desc, init, value;
|
|
121
|
+
if (isPrivate) {
|
|
122
|
+
if (kind === 0 || kind === 1) {
|
|
123
|
+
desc = {
|
|
124
|
+
get: decInfo[3],
|
|
125
|
+
set: decInfo[4]
|
|
126
|
+
};
|
|
127
|
+
} else if (kind === 3) {
|
|
128
|
+
desc = {
|
|
129
|
+
get: decInfo[3]
|
|
130
|
+
};
|
|
131
|
+
} else if (kind === 4) {
|
|
132
|
+
desc = {
|
|
133
|
+
set: decInfo[3]
|
|
134
|
+
};
|
|
135
|
+
} else {
|
|
136
|
+
desc = {
|
|
137
|
+
value: decInfo[3]
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
} else if (kind !== 0) {
|
|
141
|
+
desc = Object.getOwnPropertyDescriptor(base, name);
|
|
142
|
+
}
|
|
143
|
+
if (kind === 1) {
|
|
144
|
+
value = {
|
|
145
|
+
get: desc.get,
|
|
146
|
+
set: desc.set
|
|
147
|
+
};
|
|
148
|
+
} else if (kind === 2) {
|
|
149
|
+
value = desc.value;
|
|
150
|
+
} else if (kind === 3) {
|
|
151
|
+
value = desc.get;
|
|
152
|
+
} else if (kind === 4) {
|
|
153
|
+
value = desc.set;
|
|
154
|
+
}
|
|
155
|
+
var newValue, get, set;
|
|
156
|
+
if (typeof decs === "function") {
|
|
157
|
+
newValue = memberDec(decs, name, desc, initializers, kind, isStatic, isPrivate, metadata, value);
|
|
158
|
+
if (newValue !== void 0) {
|
|
159
|
+
assertValidReturnValue(kind, newValue);
|
|
160
|
+
if (kind === 0) {
|
|
161
|
+
init = newValue;
|
|
162
|
+
} else if (kind === 1) {
|
|
163
|
+
init = newValue.init;
|
|
164
|
+
get = newValue.get || value.get;
|
|
165
|
+
set = newValue.set || value.set;
|
|
166
|
+
value = {
|
|
167
|
+
get: get,
|
|
168
|
+
set: set
|
|
169
|
+
};
|
|
170
|
+
} else {
|
|
171
|
+
value = newValue;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
} else {
|
|
175
|
+
for(var i = decs.length - 1; i >= 0; i--){
|
|
176
|
+
var dec = decs[i];
|
|
177
|
+
newValue = memberDec(dec, name, desc, initializers, kind, isStatic, isPrivate, metadata, value);
|
|
178
|
+
if (newValue !== void 0) {
|
|
179
|
+
assertValidReturnValue(kind, newValue);
|
|
180
|
+
var newInit;
|
|
181
|
+
if (kind === 0) {
|
|
182
|
+
newInit = newValue;
|
|
183
|
+
} else if (kind === 1) {
|
|
184
|
+
newInit = newValue.init;
|
|
185
|
+
get = newValue.get || value.get;
|
|
186
|
+
set = newValue.set || value.set;
|
|
187
|
+
value = {
|
|
188
|
+
get: get,
|
|
189
|
+
set: set
|
|
190
|
+
};
|
|
191
|
+
} else {
|
|
192
|
+
value = newValue;
|
|
193
|
+
}
|
|
194
|
+
if (newInit !== void 0) {
|
|
195
|
+
if (init === void 0) {
|
|
196
|
+
init = newInit;
|
|
197
|
+
} else if (typeof init === "function") {
|
|
198
|
+
init = [
|
|
199
|
+
init,
|
|
200
|
+
newInit
|
|
201
|
+
];
|
|
202
|
+
} else {
|
|
203
|
+
init.push(newInit);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
if (kind === 0 || kind === 1) {
|
|
210
|
+
if (init === void 0) {
|
|
211
|
+
init = function(instance, init) {
|
|
212
|
+
return init;
|
|
213
|
+
};
|
|
214
|
+
} else if (typeof init !== "function") {
|
|
215
|
+
var ownInitializers = init;
|
|
216
|
+
init = function(instance, init) {
|
|
217
|
+
var value = init;
|
|
218
|
+
for(var i = 0; i < ownInitializers.length; i++){
|
|
219
|
+
value = ownInitializers[i].call(instance, value);
|
|
220
|
+
}
|
|
221
|
+
return value;
|
|
222
|
+
};
|
|
223
|
+
} else {
|
|
224
|
+
var originalInitializer = init;
|
|
225
|
+
init = function(instance, init) {
|
|
226
|
+
return originalInitializer.call(instance, init);
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
ret.push(init);
|
|
230
|
+
}
|
|
231
|
+
if (kind !== 0) {
|
|
232
|
+
if (kind === 1) {
|
|
233
|
+
desc.get = value.get;
|
|
234
|
+
desc.set = value.set;
|
|
235
|
+
} else if (kind === 2) {
|
|
236
|
+
desc.value = value;
|
|
237
|
+
} else if (kind === 3) {
|
|
238
|
+
desc.get = value;
|
|
239
|
+
} else if (kind === 4) {
|
|
240
|
+
desc.set = value;
|
|
241
|
+
}
|
|
242
|
+
if (isPrivate) {
|
|
243
|
+
if (kind === 1) {
|
|
244
|
+
ret.push(function(instance, args) {
|
|
245
|
+
return value.get.call(instance, args);
|
|
246
|
+
});
|
|
247
|
+
ret.push(function(instance, args) {
|
|
248
|
+
return value.set.call(instance, args);
|
|
249
|
+
});
|
|
250
|
+
} else if (kind === 2) {
|
|
251
|
+
ret.push(value);
|
|
252
|
+
} else {
|
|
253
|
+
ret.push(function(instance, args) {
|
|
254
|
+
return value.call(instance, args);
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
} else {
|
|
258
|
+
Object.defineProperty(base, name, desc);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
function applyMemberDecs(Class, decInfos, metadata) {
|
|
263
|
+
var ret = [];
|
|
264
|
+
var protoInitializers;
|
|
265
|
+
var staticInitializers;
|
|
266
|
+
var existingProtoNonFields = new Map();
|
|
267
|
+
var existingStaticNonFields = new Map();
|
|
268
|
+
for(var i = 0; i < decInfos.length; i++){
|
|
269
|
+
var decInfo = decInfos[i];
|
|
270
|
+
if (!Array.isArray(decInfo)) continue;
|
|
271
|
+
var kind = decInfo[1];
|
|
272
|
+
var name = decInfo[2];
|
|
273
|
+
var isPrivate = decInfo.length > 3;
|
|
274
|
+
var isStatic = kind >= 5;
|
|
275
|
+
var base;
|
|
276
|
+
var initializers;
|
|
277
|
+
if (isStatic) {
|
|
278
|
+
base = Class;
|
|
279
|
+
kind = kind - 5;
|
|
280
|
+
staticInitializers = staticInitializers || [];
|
|
281
|
+
initializers = staticInitializers;
|
|
282
|
+
} else {
|
|
283
|
+
base = Class.prototype;
|
|
284
|
+
protoInitializers = protoInitializers || [];
|
|
285
|
+
initializers = protoInitializers;
|
|
286
|
+
}
|
|
287
|
+
if (kind !== 0 && !isPrivate) {
|
|
288
|
+
var existingNonFields = isStatic ? existingStaticNonFields : existingProtoNonFields;
|
|
289
|
+
var existingKind = existingNonFields.get(name) || 0;
|
|
290
|
+
if (existingKind === true || existingKind === 3 && kind !== 4 || existingKind === 4 && kind !== 3) {
|
|
291
|
+
throw new Error("Attempted to decorate a public method/accessor that has the same name as a previously decorated public method/accessor. This is not currently supported by the decorators plugin. Property name was: " + name);
|
|
292
|
+
} else if (!existingKind && kind > 2) {
|
|
293
|
+
existingNonFields.set(name, kind);
|
|
294
|
+
} else {
|
|
295
|
+
existingNonFields.set(name, true);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
applyMemberDec(ret, base, decInfo, name, kind, isStatic, isPrivate, initializers, metadata);
|
|
299
|
+
}
|
|
300
|
+
pushInitializers(ret, protoInitializers);
|
|
301
|
+
pushInitializers(ret, staticInitializers);
|
|
302
|
+
return ret;
|
|
303
|
+
}
|
|
304
|
+
function pushInitializers(ret, initializers) {
|
|
305
|
+
if (initializers) {
|
|
306
|
+
ret.push(function(instance) {
|
|
307
|
+
for(var i = 0; i < initializers.length; i++){
|
|
308
|
+
initializers[i].call(instance);
|
|
309
|
+
}
|
|
310
|
+
return instance;
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
function applyClassDecs(targetClass, classDecs, metadata) {
|
|
315
|
+
if (classDecs.length > 0) {
|
|
316
|
+
var initializers = [];
|
|
317
|
+
var newClass = targetClass;
|
|
318
|
+
var name = targetClass.name;
|
|
319
|
+
for(var i = classDecs.length - 1; i >= 0; i--){
|
|
320
|
+
var decoratorFinishedRef = {
|
|
321
|
+
v: false
|
|
322
|
+
};
|
|
323
|
+
try {
|
|
324
|
+
var nextNewClass = classDecs[i](newClass, {
|
|
325
|
+
kind: "class",
|
|
326
|
+
name: name,
|
|
327
|
+
addInitializer: createAddInitializerMethod(initializers, decoratorFinishedRef),
|
|
328
|
+
metadata
|
|
329
|
+
});
|
|
330
|
+
} finally{
|
|
331
|
+
decoratorFinishedRef.v = true;
|
|
332
|
+
}
|
|
333
|
+
if (nextNewClass !== undefined) {
|
|
334
|
+
assertValidReturnValue(10, nextNewClass);
|
|
335
|
+
newClass = nextNewClass;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
return [
|
|
339
|
+
defineMetadata(newClass, metadata),
|
|
340
|
+
function() {
|
|
341
|
+
for(var i = 0; i < initializers.length; i++){
|
|
342
|
+
initializers[i].call(newClass);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
];
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
function defineMetadata(Class, metadata) {
|
|
349
|
+
return Object.defineProperty(Class, Symbol.metadata || Symbol.for("Symbol.metadata"), {
|
|
350
|
+
configurable: true,
|
|
351
|
+
enumerable: true,
|
|
352
|
+
value: metadata
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
return function applyDecs2203R(targetClass, memberDecs, classDecs, parentClass) {
|
|
356
|
+
if (parentClass !== void 0) {
|
|
357
|
+
var parentMetadata = parentClass[Symbol.metadata || Symbol.for("Symbol.metadata")];
|
|
358
|
+
}
|
|
359
|
+
var metadata = Object.create(parentMetadata === void 0 ? null : parentMetadata);
|
|
360
|
+
var e = applyMemberDecs(targetClass, memberDecs, metadata);
|
|
361
|
+
if (!classDecs.length) defineMetadata(targetClass, metadata);
|
|
362
|
+
return {
|
|
363
|
+
e: e,
|
|
364
|
+
get c () {
|
|
365
|
+
return applyClassDecs(targetClass, classDecs, metadata);
|
|
366
|
+
}
|
|
367
|
+
};
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
function _apply_decs_2203_r(targetClass, memberDecs, classDecs, parentClass) {
|
|
371
|
+
return (_apply_decs_2203_r = applyDecs2203RFactory())(targetClass, memberDecs, classDecs, parentClass);
|
|
372
|
+
}
|
|
373
|
+
var _dec, _dec1, _dec2, _initProto;
|
|
1
374
|
import { Blob, getBlobsPerL1Block, getPrefixedEthBlobCommitments } from '@aztec/blob-lib';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { WEI_CONST } from '@aztec/ethereum/l1-tx-utils';
|
|
5
|
-
import { FormattedViemError, formatViemError, tryExtractEvent } from '@aztec/ethereum/utils';
|
|
375
|
+
import { FeeAssetPriceOracle, MULTI_CALL_3_ADDRESS, Multicall3, RollupContract } from '@aztec/ethereum/contracts';
|
|
376
|
+
import { L1FeeAnalyzer } from '@aztec/ethereum/l1-fee-analysis';
|
|
377
|
+
import { MAX_L1_TX_LIMIT, WEI_CONST } from '@aztec/ethereum/l1-tx-utils';
|
|
378
|
+
import { FormattedViemError, formatViemError, mergeAbis, tryExtractEvent } from '@aztec/ethereum/utils';
|
|
6
379
|
import { sumBigint } from '@aztec/foundation/bigint';
|
|
7
380
|
import { toHex as toPaddedHex } from '@aztec/foundation/bigint-buffer';
|
|
8
|
-
import {
|
|
381
|
+
import { CheckpointNumber, SlotNumber } from '@aztec/foundation/branded-types';
|
|
382
|
+
import { pick } from '@aztec/foundation/collection';
|
|
9
383
|
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
10
384
|
import { Signature } from '@aztec/foundation/eth-signature';
|
|
11
385
|
import { createLogger } from '@aztec/foundation/log';
|
|
386
|
+
import { makeBackoff, retry } from '@aztec/foundation/retry';
|
|
12
387
|
import { bufferToHex } from '@aztec/foundation/string';
|
|
13
388
|
import { Timer } from '@aztec/foundation/timer';
|
|
14
389
|
import { EmpireBaseAbi, ErrorsAbi, RollupAbi } from '@aztec/l1-artifacts';
|
|
15
390
|
import { encodeSlashConsensusVotes } from '@aztec/slasher';
|
|
16
|
-
import {
|
|
17
|
-
import { getTelemetryClient } from '@aztec/telemetry-client';
|
|
391
|
+
import { CommitteeAttestationsAndSigners } from '@aztec/stdlib/block';
|
|
392
|
+
import { getTelemetryClient, trackSpan } from '@aztec/telemetry-client';
|
|
18
393
|
import { encodeFunctionData, toHex } from 'viem';
|
|
19
394
|
import { SequencerPublisherMetrics } from './sequencer-publisher-metrics.js';
|
|
20
395
|
export const Actions = [
|
|
@@ -30,22 +405,42 @@ export const Actions = [
|
|
|
30
405
|
];
|
|
31
406
|
// Sorting for actions such that invalidations go before proposals, and proposals go before votes
|
|
32
407
|
export const compareActions = (a, b)=>Actions.indexOf(a) - Actions.indexOf(b);
|
|
408
|
+
_dec = trackSpan('SequencerPublisher.sendRequests'), _dec1 = trackSpan('SequencerPublisher.validateBlockHeader'), _dec2 = trackSpan('SequencerPublisher.validateCheckpointForSubmission');
|
|
33
409
|
export class SequencerPublisher {
|
|
34
410
|
config;
|
|
411
|
+
static{
|
|
412
|
+
({ e: [_initProto] } = _apply_decs_2203_r(this, [
|
|
413
|
+
[
|
|
414
|
+
_dec,
|
|
415
|
+
2,
|
|
416
|
+
"sendRequests"
|
|
417
|
+
],
|
|
418
|
+
[
|
|
419
|
+
_dec1,
|
|
420
|
+
2,
|
|
421
|
+
"validateBlockHeader"
|
|
422
|
+
],
|
|
423
|
+
[
|
|
424
|
+
_dec2,
|
|
425
|
+
2,
|
|
426
|
+
"validateCheckpointForSubmission"
|
|
427
|
+
]
|
|
428
|
+
], []));
|
|
429
|
+
}
|
|
35
430
|
interrupted;
|
|
36
431
|
metrics;
|
|
37
432
|
epochCache;
|
|
38
433
|
governanceLog;
|
|
39
434
|
slashingLog;
|
|
40
435
|
lastActions;
|
|
436
|
+
isPayloadEmptyCache;
|
|
437
|
+
payloadProposedCache;
|
|
41
438
|
log;
|
|
42
439
|
ethereumSlotDuration;
|
|
43
|
-
|
|
440
|
+
blobClient;
|
|
44
441
|
/** Address to use for simulations in fisherman mode (actual proposer's address) */ proposerAddressForSimulation;
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
// Total used for emptier block from above test: 429k (of which 84k is 1x blob)
|
|
48
|
-
static PROPOSE_GAS_GUESS = 12_000_000n;
|
|
442
|
+
/** L1 fee analyzer for fisherman mode */ l1FeeAnalyzer;
|
|
443
|
+
/** Fee asset price oracle for computing price modifiers from Uniswap V4 */ feeAssetPriceOracle;
|
|
49
444
|
// A CALL to a cold address is 2700 gas
|
|
50
445
|
static MULTICALL_OVERHEAD_GAS_GUESS = 5000n;
|
|
51
446
|
// Gas report for VotingWithSigTest shows a max gas of 100k, but we've seen it cost 700k+ in testnet
|
|
@@ -55,23 +450,25 @@ export class SequencerPublisher {
|
|
|
55
450
|
govProposerContract;
|
|
56
451
|
slashingProposerContract;
|
|
57
452
|
slashFactoryContract;
|
|
453
|
+
tracer;
|
|
58
454
|
requests;
|
|
59
455
|
constructor(config, deps){
|
|
60
456
|
this.config = config;
|
|
61
|
-
this.interrupted = false;
|
|
457
|
+
this.interrupted = (_initProto(this), false);
|
|
62
458
|
this.governanceLog = createLogger('sequencer:publisher:governance');
|
|
63
459
|
this.slashingLog = createLogger('sequencer:publisher:slashing');
|
|
64
460
|
this.lastActions = {};
|
|
461
|
+
this.isPayloadEmptyCache = new Map();
|
|
462
|
+
this.payloadProposedCache = new Set();
|
|
65
463
|
this.requests = [];
|
|
66
464
|
this.log = deps.log ?? createLogger('sequencer:publisher');
|
|
67
465
|
this.ethereumSlotDuration = BigInt(config.ethereumSlotDuration);
|
|
68
466
|
this.epochCache = deps.epochCache;
|
|
69
467
|
this.lastActions = deps.lastActions;
|
|
70
|
-
this.
|
|
71
|
-
logger: createLogger('sequencer:blob-sink:client')
|
|
72
|
-
});
|
|
468
|
+
this.blobClient = deps.blobClient;
|
|
73
469
|
const telemetry = deps.telemetry ?? getTelemetryClient();
|
|
74
470
|
this.metrics = deps.metrics ?? new SequencerPublisherMetrics(telemetry, 'SequencerPublisher');
|
|
471
|
+
this.tracer = telemetry.getTracer('SequencerPublisher');
|
|
75
472
|
this.l1TxUtils = deps.l1TxUtils;
|
|
76
473
|
this.rollupContract = deps.rollupContract;
|
|
77
474
|
this.govProposerContract = deps.governanceProposerContract;
|
|
@@ -82,14 +479,31 @@ export class SequencerPublisher {
|
|
|
82
479
|
this.slashingProposerContract = newSlashingProposer;
|
|
83
480
|
});
|
|
84
481
|
this.slashFactoryContract = deps.slashFactoryContract;
|
|
482
|
+
// Initialize L1 fee analyzer for fisherman mode
|
|
483
|
+
if (config.fishermanMode) {
|
|
484
|
+
this.l1FeeAnalyzer = new L1FeeAnalyzer(this.l1TxUtils.client, deps.dateProvider, createLogger('sequencer:publisher:fee-analyzer'));
|
|
485
|
+
}
|
|
486
|
+
// Initialize fee asset price oracle
|
|
487
|
+
this.feeAssetPriceOracle = new FeeAssetPriceOracle(this.l1TxUtils.client, this.rollupContract, createLogger('sequencer:publisher:price-oracle'));
|
|
85
488
|
}
|
|
86
489
|
getRollupContract() {
|
|
87
490
|
return this.rollupContract;
|
|
88
491
|
}
|
|
492
|
+
/**
|
|
493
|
+
* Gets the fee asset price modifier from the oracle.
|
|
494
|
+
* Returns 0n if the oracle query fails.
|
|
495
|
+
*/ getFeeAssetPriceModifier() {
|
|
496
|
+
return this.feeAssetPriceOracle.computePriceModifier();
|
|
497
|
+
}
|
|
89
498
|
getSenderAddress() {
|
|
90
499
|
return this.l1TxUtils.getSenderAddress();
|
|
91
500
|
}
|
|
92
501
|
/**
|
|
502
|
+
* Gets the L1 fee analyzer instance (only available in fisherman mode)
|
|
503
|
+
*/ getL1FeeAnalyzer() {
|
|
504
|
+
return this.l1FeeAnalyzer;
|
|
505
|
+
}
|
|
506
|
+
/**
|
|
93
507
|
* Sets the proposer address to use for simulations in fisherman mode.
|
|
94
508
|
* @param proposerAddress - The actual proposer's address to use for balance lookups in simulations
|
|
95
509
|
*/ setProposerAddressForSimulation(proposerAddress) {
|
|
@@ -111,6 +525,46 @@ export class SequencerPublisher {
|
|
|
111
525
|
}
|
|
112
526
|
}
|
|
113
527
|
/**
|
|
528
|
+
* Analyzes L1 fees for the pending requests without sending them.
|
|
529
|
+
* This is used in fisherman mode to validate fee calculations.
|
|
530
|
+
* @param l2SlotNumber - The L2 slot number for this analysis
|
|
531
|
+
* @param onComplete - Optional callback to invoke when analysis completes (after block is mined)
|
|
532
|
+
* @returns The analysis result (incomplete until block mines), or undefined if no requests
|
|
533
|
+
*/ async analyzeL1Fees(l2SlotNumber, onComplete) {
|
|
534
|
+
if (!this.l1FeeAnalyzer) {
|
|
535
|
+
this.log.warn('L1 fee analyzer not available (not in fisherman mode)');
|
|
536
|
+
return undefined;
|
|
537
|
+
}
|
|
538
|
+
const requestsToAnalyze = [
|
|
539
|
+
...this.requests
|
|
540
|
+
];
|
|
541
|
+
if (requestsToAnalyze.length === 0) {
|
|
542
|
+
this.log.debug('No requests to analyze for L1 fees');
|
|
543
|
+
return undefined;
|
|
544
|
+
}
|
|
545
|
+
// Extract blob config from requests (if any)
|
|
546
|
+
const blobConfigs = requestsToAnalyze.filter((request)=>request.blobConfig).map((request)=>request.blobConfig);
|
|
547
|
+
const blobConfig = blobConfigs[0];
|
|
548
|
+
// Get gas configs
|
|
549
|
+
const gasConfigs = requestsToAnalyze.filter((request)=>request.gasConfig).map((request)=>request.gasConfig);
|
|
550
|
+
const gasLimits = gasConfigs.map((g)=>g?.gasLimit).filter((g)=>g !== undefined);
|
|
551
|
+
const gasLimit = gasLimits.length > 0 ? gasLimits.reduce((sum, g)=>sum + g, 0n) : 0n;
|
|
552
|
+
// Get the transaction requests
|
|
553
|
+
const l1Requests = requestsToAnalyze.map((r)=>r.request);
|
|
554
|
+
// Start the analysis
|
|
555
|
+
const analysisId = await this.l1FeeAnalyzer.startAnalysis(l2SlotNumber, gasLimit > 0n ? gasLimit : MAX_L1_TX_LIMIT, l1Requests, blobConfig, onComplete);
|
|
556
|
+
this.log.info('Started L1 fee analysis', {
|
|
557
|
+
analysisId,
|
|
558
|
+
l2SlotNumber: l2SlotNumber.toString(),
|
|
559
|
+
requestCount: requestsToAnalyze.length,
|
|
560
|
+
hasBlobConfig: !!blobConfig,
|
|
561
|
+
gasLimit: gasLimit.toString(),
|
|
562
|
+
actions: requestsToAnalyze.map((r)=>r.action)
|
|
563
|
+
});
|
|
564
|
+
// Return the analysis result (will be incomplete until block mines)
|
|
565
|
+
return this.l1FeeAnalyzer.getAnalysis(analysisId);
|
|
566
|
+
}
|
|
567
|
+
/**
|
|
114
568
|
* Sends all requests that are still valid.
|
|
115
569
|
* @returns one of:
|
|
116
570
|
* - A receipt and stats if the tx succeeded
|
|
@@ -121,7 +575,7 @@ export class SequencerPublisher {
|
|
|
121
575
|
...this.requests
|
|
122
576
|
];
|
|
123
577
|
this.requests = [];
|
|
124
|
-
if (this.interrupted) {
|
|
578
|
+
if (this.interrupted || requestsToProcess.length === 0) {
|
|
125
579
|
return undefined;
|
|
126
580
|
}
|
|
127
581
|
const currentL2Slot = this.getCurrentL2Slot();
|
|
@@ -156,7 +610,16 @@ export class SequencerPublisher {
|
|
|
156
610
|
const blobConfig = blobConfigs[0];
|
|
157
611
|
// Merge gasConfigs. Yields the sum of gasLimits, and the earliest txTimeoutAt, or undefined if no gasConfig sets them.
|
|
158
612
|
const gasLimits = gasConfigs.map((g)=>g?.gasLimit).filter((g)=>g !== undefined);
|
|
159
|
-
|
|
613
|
+
let gasLimit = gasLimits.length > 0 ? sumBigint(gasLimits) : undefined; // sum
|
|
614
|
+
// Cap at L1 block gas limit so the node accepts the tx ("gas limit too high" otherwise).
|
|
615
|
+
const maxGas = MAX_L1_TX_LIMIT;
|
|
616
|
+
if (gasLimit !== undefined && gasLimit > maxGas) {
|
|
617
|
+
this.log.debug('Capping bundled tx gas limit to L1 max', {
|
|
618
|
+
requested: gasLimit,
|
|
619
|
+
capped: maxGas
|
|
620
|
+
});
|
|
621
|
+
gasLimit = maxGas;
|
|
622
|
+
}
|
|
160
623
|
const txTimeoutAts = gasConfigs.map((g)=>g?.txTimeoutAt).filter((g)=>g !== undefined);
|
|
161
624
|
const txTimeoutAt = txTimeoutAts.length > 0 ? new Date(Math.min(...txTimeoutAts.map((g)=>g.getTime()))) : undefined; // earliest
|
|
162
625
|
const txConfig = {
|
|
@@ -231,7 +694,7 @@ export class SequencerPublisher {
|
|
|
231
694
|
'InvalidArchive'
|
|
232
695
|
];
|
|
233
696
|
return this.rollupContract.canProposeAtNextEthBlock(tipArchive.toBuffer(), msgSender.toString(), Number(this.ethereumSlotDuration), {
|
|
234
|
-
forcePendingCheckpointNumber: opts.
|
|
697
|
+
forcePendingCheckpointNumber: opts.forcePendingCheckpointNumber
|
|
235
698
|
}).catch((err)=>{
|
|
236
699
|
if (err instanceof FormattedViemError && ignoredErrors.find((e)=>err.message.includes(e))) {
|
|
237
700
|
this.log.warn(`Failed canProposeAtTime check with ${ignoredErrors.find((e)=>err.message.includes(e))}`, {
|
|
@@ -259,12 +722,11 @@ export class SequencerPublisher {
|
|
|
259
722
|
[],
|
|
260
723
|
Signature.empty().toViemSignature(),
|
|
261
724
|
`0x${'0'.repeat(64)}`,
|
|
262
|
-
header.
|
|
725
|
+
header.blobsHash.toString(),
|
|
263
726
|
flags
|
|
264
727
|
];
|
|
265
728
|
const ts = BigInt((await this.l1TxUtils.getBlock()).timestamp + this.ethereumSlotDuration);
|
|
266
|
-
const
|
|
267
|
-
const stateOverrides = await this.rollupContract.makePendingCheckpointNumberOverride(optsForcePendingCheckpointNumber);
|
|
729
|
+
const stateOverrides = await this.rollupContract.makePendingCheckpointNumberOverride(opts?.forcePendingCheckpointNumber);
|
|
268
730
|
let balance = 0n;
|
|
269
731
|
if (this.config.fishermanMode) {
|
|
270
732
|
// In fisherman mode, we can't know where the proposer is publishing from
|
|
@@ -291,34 +753,37 @@ export class SequencerPublisher {
|
|
|
291
753
|
this.log.debug(`Simulated validateHeader`);
|
|
292
754
|
}
|
|
293
755
|
/**
|
|
294
|
-
* Simulate making a call to invalidate a
|
|
295
|
-
* @param
|
|
296
|
-
*/ async
|
|
756
|
+
* Simulate making a call to invalidate a checkpoint with invalid attestations. Returns undefined if no need to invalidate.
|
|
757
|
+
* @param validationResult - The validation result indicating which checkpoint to invalidate (as returned by the archiver)
|
|
758
|
+
*/ async simulateInvalidateCheckpoint(validationResult) {
|
|
297
759
|
if (validationResult.valid) {
|
|
298
760
|
return undefined;
|
|
299
761
|
}
|
|
300
|
-
const { reason,
|
|
301
|
-
const
|
|
762
|
+
const { reason, checkpoint } = validationResult;
|
|
763
|
+
const checkpointNumber = checkpoint.checkpointNumber;
|
|
302
764
|
const logData = {
|
|
303
|
-
...
|
|
765
|
+
...checkpoint,
|
|
304
766
|
reason
|
|
305
767
|
};
|
|
306
|
-
const
|
|
307
|
-
if (
|
|
308
|
-
this.log.verbose(`Skipping
|
|
309
|
-
|
|
768
|
+
const currentCheckpointNumber = await this.rollupContract.getCheckpointNumber();
|
|
769
|
+
if (currentCheckpointNumber < checkpointNumber) {
|
|
770
|
+
this.log.verbose(`Skipping checkpoint ${checkpointNumber} invalidation since it has already been removed from the pending chain`, {
|
|
771
|
+
currentCheckpointNumber,
|
|
310
772
|
...logData
|
|
311
773
|
});
|
|
312
774
|
return undefined;
|
|
313
775
|
}
|
|
314
|
-
const request = this.
|
|
315
|
-
this.log.debug(`Simulating invalidate
|
|
776
|
+
const request = this.buildInvalidateCheckpointRequest(validationResult);
|
|
777
|
+
this.log.debug(`Simulating invalidate checkpoint ${checkpointNumber}`, {
|
|
316
778
|
...logData,
|
|
317
779
|
request
|
|
318
780
|
});
|
|
319
781
|
try {
|
|
320
|
-
const { gasUsed } = await this.l1TxUtils.simulate(request, undefined, undefined,
|
|
321
|
-
|
|
782
|
+
const { gasUsed } = await this.l1TxUtils.simulate(request, undefined, undefined, mergeAbis([
|
|
783
|
+
request.abi ?? [],
|
|
784
|
+
ErrorsAbi
|
|
785
|
+
]));
|
|
786
|
+
this.log.verbose(`Simulation for invalidate checkpoint ${checkpointNumber} succeeded`, {
|
|
322
787
|
...logData,
|
|
323
788
|
request,
|
|
324
789
|
gasUsed
|
|
@@ -326,90 +791,71 @@ export class SequencerPublisher {
|
|
|
326
791
|
return {
|
|
327
792
|
request,
|
|
328
793
|
gasUsed,
|
|
329
|
-
|
|
330
|
-
|
|
794
|
+
checkpointNumber,
|
|
795
|
+
forcePendingCheckpointNumber: CheckpointNumber(checkpointNumber - 1),
|
|
331
796
|
reason
|
|
332
797
|
};
|
|
333
798
|
} catch (err) {
|
|
334
799
|
const viemError = formatViemError(err);
|
|
335
|
-
// If the error is due to the
|
|
336
|
-
// we can safely ignore it and return undefined so we go ahead with
|
|
337
|
-
if (viemError.message?.includes('
|
|
338
|
-
this.log.verbose(`Simulation for invalidate
|
|
800
|
+
// If the error is due to the checkpoint not being in the pending chain, and it was indeed removed by someone else,
|
|
801
|
+
// we can safely ignore it and return undefined so we go ahead with checkpoint building.
|
|
802
|
+
if (viemError.message?.includes('Rollup__CheckpointNotInPendingChain')) {
|
|
803
|
+
this.log.verbose(`Simulation for invalidate checkpoint ${checkpointNumber} failed due to checkpoint not being in pending chain`, {
|
|
339
804
|
...logData,
|
|
340
805
|
request,
|
|
341
806
|
error: viemError.message
|
|
342
807
|
});
|
|
343
|
-
const
|
|
344
|
-
if (
|
|
345
|
-
this.log.verbose(`
|
|
808
|
+
const latestPendingCheckpointNumber = await this.rollupContract.getCheckpointNumber();
|
|
809
|
+
if (latestPendingCheckpointNumber < checkpointNumber) {
|
|
810
|
+
this.log.verbose(`Checkpoint ${checkpointNumber} has already been invalidated`, {
|
|
346
811
|
...logData
|
|
347
812
|
});
|
|
348
813
|
return undefined;
|
|
349
814
|
} else {
|
|
350
|
-
this.log.error(`Simulation for invalidate ${
|
|
351
|
-
throw new Error(`Failed to simulate invalidate
|
|
815
|
+
this.log.error(`Simulation for invalidate checkpoint ${checkpointNumber} failed and it is still in pending chain`, viemError, logData);
|
|
816
|
+
throw new Error(`Failed to simulate invalidate checkpoint ${checkpointNumber} while it is still in pending chain`, {
|
|
352
817
|
cause: viemError
|
|
353
818
|
});
|
|
354
819
|
}
|
|
355
820
|
}
|
|
356
|
-
// Otherwise, throw. We cannot build the next
|
|
357
|
-
this.log.error(`Simulation for invalidate
|
|
358
|
-
throw new Error(`Failed to simulate invalidate
|
|
821
|
+
// Otherwise, throw. We cannot build the next checkpoint if we cannot invalidate the previous one.
|
|
822
|
+
this.log.error(`Simulation for invalidate checkpoint ${checkpointNumber} failed`, viemError, logData);
|
|
823
|
+
throw new Error(`Failed to simulate invalidate checkpoint ${checkpointNumber}`, {
|
|
359
824
|
cause: viemError
|
|
360
825
|
});
|
|
361
826
|
}
|
|
362
827
|
}
|
|
363
|
-
|
|
828
|
+
buildInvalidateCheckpointRequest(validationResult) {
|
|
364
829
|
if (validationResult.valid) {
|
|
365
|
-
throw new Error('Cannot invalidate a valid
|
|
830
|
+
throw new Error('Cannot invalidate a valid checkpoint');
|
|
366
831
|
}
|
|
367
|
-
const {
|
|
832
|
+
const { checkpoint, committee, reason } = validationResult;
|
|
368
833
|
const logData = {
|
|
369
|
-
...
|
|
834
|
+
...checkpoint,
|
|
370
835
|
reason
|
|
371
836
|
};
|
|
372
|
-
this.log.debug(`
|
|
837
|
+
this.log.debug(`Building invalidate checkpoint ${checkpoint.checkpointNumber} request`, logData);
|
|
373
838
|
const attestationsAndSigners = new CommitteeAttestationsAndSigners(validationResult.attestations).getPackedAttestations();
|
|
374
839
|
if (reason === 'invalid-attestation') {
|
|
375
|
-
return this.rollupContract.buildInvalidateBadAttestationRequest(
|
|
840
|
+
return this.rollupContract.buildInvalidateBadAttestationRequest(checkpoint.checkpointNumber, attestationsAndSigners, committee, validationResult.invalidIndex);
|
|
376
841
|
} else if (reason === 'insufficient-attestations') {
|
|
377
|
-
return this.rollupContract.buildInvalidateInsufficientAttestationsRequest(
|
|
842
|
+
return this.rollupContract.buildInvalidateInsufficientAttestationsRequest(checkpoint.checkpointNumber, attestationsAndSigners, committee);
|
|
378
843
|
} else {
|
|
379
844
|
const _ = reason;
|
|
380
845
|
throw new Error(`Unknown reason for invalidation`);
|
|
381
846
|
}
|
|
382
847
|
}
|
|
383
|
-
/**
|
|
384
|
-
* @notice Will simulate `propose` to make sure that the block is valid for submission
|
|
385
|
-
*
|
|
386
|
-
* @dev Throws if unable to propose
|
|
387
|
-
*
|
|
388
|
-
* @param block - The block to propose
|
|
389
|
-
* @param attestationData - The block's attestation data
|
|
390
|
-
*
|
|
391
|
-
*/ async validateBlockForSubmission(block, attestationsAndSigners, attestationsAndSignersSignature, options) {
|
|
848
|
+
/** Simulates `propose` to make sure that the checkpoint is valid for submission */ async validateCheckpointForSubmission(checkpoint, attestationsAndSigners, attestationsAndSignersSignature, options) {
|
|
392
849
|
const ts = BigInt((await this.l1TxUtils.getBlock()).timestamp + this.ethereumSlotDuration);
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
const ignoreSignatures = attestationsAndSigners.attestations.length === 0;
|
|
396
|
-
if (ignoreSignatures) {
|
|
397
|
-
const { committee } = await this.epochCache.getCommittee(block.header.globalVariables.slotNumber);
|
|
398
|
-
if (!committee) {
|
|
399
|
-
this.log.warn(`No committee found for slot ${block.header.globalVariables.slotNumber}`);
|
|
400
|
-
throw new Error(`No committee found for slot ${block.header.globalVariables.slotNumber}`);
|
|
401
|
-
}
|
|
402
|
-
attestationsAndSigners.attestations = committee.map((committeeMember)=>CommitteeAttestation.fromAddress(committeeMember));
|
|
403
|
-
}
|
|
404
|
-
const blobFields = block.getCheckpointBlobFields();
|
|
405
|
-
const blobs = getBlobsPerL1Block(blobFields);
|
|
850
|
+
const blobFields = checkpoint.toBlobFields();
|
|
851
|
+
const blobs = await getBlobsPerL1Block(blobFields);
|
|
406
852
|
const blobInput = getPrefixedEthBlobCommitments(blobs);
|
|
407
853
|
const args = [
|
|
408
854
|
{
|
|
409
|
-
header:
|
|
410
|
-
archive: toHex(
|
|
855
|
+
header: checkpoint.header.toViem(),
|
|
856
|
+
archive: toHex(checkpoint.archive.root.toBuffer()),
|
|
411
857
|
oracleInput: {
|
|
412
|
-
feeAssetPriceModifier:
|
|
858
|
+
feeAssetPriceModifier: checkpoint.feeAssetPriceModifier
|
|
413
859
|
}
|
|
414
860
|
},
|
|
415
861
|
attestationsAndSigners.getPackedAttestations(),
|
|
@@ -434,9 +880,38 @@ export class SequencerPublisher {
|
|
|
434
880
|
}
|
|
435
881
|
const round = await base.computeRound(slotNumber);
|
|
436
882
|
const roundInfo = await base.getRoundInfo(this.rollupContract.address, round);
|
|
883
|
+
if (roundInfo.quorumReached) {
|
|
884
|
+
return false;
|
|
885
|
+
}
|
|
437
886
|
if (roundInfo.lastSignalSlot >= slotNumber) {
|
|
438
887
|
return false;
|
|
439
888
|
}
|
|
889
|
+
if (await this.isPayloadEmpty(payload)) {
|
|
890
|
+
this.log.warn(`Skipping vote cast for payload with empty code`);
|
|
891
|
+
return false;
|
|
892
|
+
}
|
|
893
|
+
// Check if payload was already submitted to governance
|
|
894
|
+
const cacheKey = payload.toString();
|
|
895
|
+
if (!this.payloadProposedCache.has(cacheKey)) {
|
|
896
|
+
try {
|
|
897
|
+
const l1StartBlock = await this.rollupContract.getL1StartBlock();
|
|
898
|
+
const proposed = await retry(()=>base.hasPayloadBeenProposed(payload.toString(), l1StartBlock), 'Check if payload was proposed', makeBackoff([
|
|
899
|
+
0,
|
|
900
|
+
1,
|
|
901
|
+
2
|
|
902
|
+
]), this.log, true);
|
|
903
|
+
if (proposed) {
|
|
904
|
+
this.payloadProposedCache.add(cacheKey);
|
|
905
|
+
}
|
|
906
|
+
} catch (err) {
|
|
907
|
+
this.log.warn(`Failed to check if payload ${payload} was proposed after retries, skipping signal`, err);
|
|
908
|
+
return false;
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
if (this.payloadProposedCache.has(cacheKey)) {
|
|
912
|
+
this.log.info(`Payload ${payload} was already proposed to governance, stopping signals`);
|
|
913
|
+
return false;
|
|
914
|
+
}
|
|
440
915
|
const cachedLastVote = this.lastActions[signalType];
|
|
441
916
|
this.lastActions[signalType] = slotNumber;
|
|
442
917
|
const action = signalType;
|
|
@@ -450,7 +925,10 @@ export class SequencerPublisher {
|
|
|
450
925
|
try {
|
|
451
926
|
await this.l1TxUtils.simulate(request, {
|
|
452
927
|
time: timestamp
|
|
453
|
-
}, [],
|
|
928
|
+
}, [], mergeAbis([
|
|
929
|
+
request.abi ?? [],
|
|
930
|
+
ErrorsAbi
|
|
931
|
+
]));
|
|
454
932
|
this.log.debug(`Simulation for ${action} at slot ${slotNumber} succeeded`, {
|
|
455
933
|
request
|
|
456
934
|
});
|
|
@@ -475,17 +953,27 @@ export class SequencerPublisher {
|
|
|
475
953
|
payload: payload.toString()
|
|
476
954
|
};
|
|
477
955
|
if (!success) {
|
|
478
|
-
this.log.error(`Signaling in
|
|
956
|
+
this.log.error(`Signaling in ${action} for ${payload} at slot ${slotNumber} in round ${round} failed`, logData);
|
|
479
957
|
this.lastActions[signalType] = cachedLastVote;
|
|
480
958
|
return false;
|
|
481
959
|
} else {
|
|
482
|
-
this.log.info(`Signaling in
|
|
960
|
+
this.log.info(`Signaling in ${action} for ${payload} at slot ${slotNumber} in round ${round} succeeded`, logData);
|
|
483
961
|
return true;
|
|
484
962
|
}
|
|
485
963
|
}
|
|
486
964
|
});
|
|
487
965
|
return true;
|
|
488
966
|
}
|
|
967
|
+
async isPayloadEmpty(payload) {
|
|
968
|
+
const key = payload.toString();
|
|
969
|
+
const cached = this.isPayloadEmptyCache.get(key);
|
|
970
|
+
if (cached) {
|
|
971
|
+
return cached;
|
|
972
|
+
}
|
|
973
|
+
const isEmpty = !await this.l1TxUtils.getCode(payload);
|
|
974
|
+
this.isPayloadEmptyCache.set(key, isEmpty);
|
|
975
|
+
return isEmpty;
|
|
976
|
+
}
|
|
489
977
|
/**
|
|
490
978
|
* Enqueues a governance castSignal transaction to cast a signal for a given slot number.
|
|
491
979
|
* @param slotNumber - The slot number to cast a signal for.
|
|
@@ -581,22 +1069,17 @@ export class SequencerPublisher {
|
|
|
581
1069
|
}
|
|
582
1070
|
return true;
|
|
583
1071
|
}
|
|
584
|
-
/**
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
* @returns True if the tx has been enqueued, throws otherwise. See #9315
|
|
589
|
-
*/ async enqueueProposeL2Block(block, attestationsAndSigners, attestationsAndSignersSignature, opts = {}) {
|
|
590
|
-
const checkpointHeader = block.getCheckpointHeader();
|
|
591
|
-
const blobFields = block.getCheckpointBlobFields();
|
|
592
|
-
const blobs = getBlobsPerL1Block(blobFields);
|
|
1072
|
+
/** Simulates and enqueues a proposal for a checkpoint on L1 */ async enqueueProposeCheckpoint(checkpoint, attestationsAndSigners, attestationsAndSignersSignature, opts = {}) {
|
|
1073
|
+
const checkpointHeader = checkpoint.header;
|
|
1074
|
+
const blobFields = checkpoint.toBlobFields();
|
|
1075
|
+
const blobs = await getBlobsPerL1Block(blobFields);
|
|
593
1076
|
const proposeTxArgs = {
|
|
594
1077
|
header: checkpointHeader,
|
|
595
|
-
archive:
|
|
596
|
-
body: block.body.toBuffer(),
|
|
1078
|
+
archive: checkpoint.archive.root.toBuffer(),
|
|
597
1079
|
blobs,
|
|
598
1080
|
attestationsAndSigners,
|
|
599
|
-
attestationsAndSignersSignature
|
|
1081
|
+
attestationsAndSignersSignature,
|
|
1082
|
+
feeAssetPriceModifier: checkpoint.feeAssetPriceModifier
|
|
600
1083
|
};
|
|
601
1084
|
let ts;
|
|
602
1085
|
try {
|
|
@@ -605,36 +1088,35 @@ export class SequencerPublisher {
|
|
|
605
1088
|
// By simulation issue, I mean the fact that the block.timestamp is equal to the last block, not the next, which
|
|
606
1089
|
// make time consistency checks break.
|
|
607
1090
|
// TODO(palla): Check whether we're validating twice, once here and once within addProposeTx, since we call simulateProposeTx in both places.
|
|
608
|
-
ts = await this.
|
|
1091
|
+
ts = await this.validateCheckpointForSubmission(checkpoint, attestationsAndSigners, attestationsAndSignersSignature, opts);
|
|
609
1092
|
} catch (err) {
|
|
610
|
-
this.log.error(`
|
|
611
|
-
...
|
|
612
|
-
slotNumber:
|
|
613
|
-
|
|
1093
|
+
this.log.error(`Checkpoint validation failed. ${err instanceof Error ? err.message : 'No error message'}`, err, {
|
|
1094
|
+
...checkpoint.getStats(),
|
|
1095
|
+
slotNumber: checkpoint.header.slotNumber,
|
|
1096
|
+
forcePendingCheckpointNumber: opts.forcePendingCheckpointNumber
|
|
614
1097
|
});
|
|
615
1098
|
throw err;
|
|
616
1099
|
}
|
|
617
|
-
this.log.verbose(`Enqueuing
|
|
618
|
-
...
|
|
1100
|
+
this.log.verbose(`Enqueuing checkpoint propose transaction`, {
|
|
1101
|
+
...checkpoint.toCheckpointInfo(),
|
|
619
1102
|
...opts
|
|
620
1103
|
});
|
|
621
|
-
await this.addProposeTx(
|
|
622
|
-
return true;
|
|
1104
|
+
await this.addProposeTx(checkpoint, proposeTxArgs, opts, ts);
|
|
623
1105
|
}
|
|
624
|
-
|
|
1106
|
+
enqueueInvalidateCheckpoint(request, opts = {}) {
|
|
625
1107
|
if (!request) {
|
|
626
1108
|
return;
|
|
627
1109
|
}
|
|
628
1110
|
// We issued the simulation against the rollup contract, so we need to account for the overhead of the multicall3
|
|
629
1111
|
const gasLimit = this.l1TxUtils.bumpGasLimit(BigInt(Math.ceil(Number(request.gasUsed) * 64 / 63)));
|
|
630
|
-
const { gasUsed,
|
|
1112
|
+
const { gasUsed, checkpointNumber } = request;
|
|
631
1113
|
const logData = {
|
|
632
1114
|
gasUsed,
|
|
633
|
-
|
|
1115
|
+
checkpointNumber,
|
|
634
1116
|
gasLimit,
|
|
635
1117
|
opts
|
|
636
1118
|
};
|
|
637
|
-
this.log.verbose(`Enqueuing invalidate
|
|
1119
|
+
this.log.verbose(`Enqueuing invalidate checkpoint request`, logData);
|
|
638
1120
|
this.addRequest({
|
|
639
1121
|
action: `invalidate-by-${request.reason}`,
|
|
640
1122
|
request: request.request,
|
|
@@ -646,12 +1128,12 @@ export class SequencerPublisher {
|
|
|
646
1128
|
checkSuccess: (_req, result)=>{
|
|
647
1129
|
const success = result && result.receipt && result.receipt.status === 'success' && tryExtractEvent(result.receipt.logs, this.rollupContract.address, RollupAbi, 'CheckpointInvalidated');
|
|
648
1130
|
if (!success) {
|
|
649
|
-
this.log.warn(`Invalidate
|
|
1131
|
+
this.log.warn(`Invalidate checkpoint ${request.checkpointNumber} failed`, {
|
|
650
1132
|
...result,
|
|
651
1133
|
...logData
|
|
652
1134
|
});
|
|
653
1135
|
} else {
|
|
654
|
-
this.log.info(`Invalidate
|
|
1136
|
+
this.log.info(`Invalidate checkpoint ${request.checkpointNumber} succeeded`, {
|
|
655
1137
|
...result,
|
|
656
1138
|
...logData
|
|
657
1139
|
});
|
|
@@ -674,27 +1156,37 @@ export class SequencerPublisher {
|
|
|
674
1156
|
this.lastActions[action] = slotNumber;
|
|
675
1157
|
this.log.debug(`Simulating ${action} for slot ${slotNumber}`, logData);
|
|
676
1158
|
let gasUsed;
|
|
1159
|
+
const simulateAbi = mergeAbis([
|
|
1160
|
+
request.abi ?? [],
|
|
1161
|
+
ErrorsAbi
|
|
1162
|
+
]);
|
|
677
1163
|
try {
|
|
678
1164
|
({ gasUsed } = await this.l1TxUtils.simulate(request, {
|
|
679
1165
|
time: timestamp
|
|
680
|
-
}, [],
|
|
1166
|
+
}, [], simulateAbi)); // TODO(palla/slash): Check the timestamp logic
|
|
681
1167
|
this.log.verbose(`Simulation for ${action} succeeded`, {
|
|
682
1168
|
...logData,
|
|
683
1169
|
request,
|
|
684
1170
|
gasUsed
|
|
685
1171
|
});
|
|
686
1172
|
} catch (err) {
|
|
687
|
-
const viemError = formatViemError(err);
|
|
1173
|
+
const viemError = formatViemError(err, simulateAbi);
|
|
688
1174
|
this.log.error(`Simulation for ${action} at ${slotNumber} failed`, viemError, logData);
|
|
689
1175
|
return false;
|
|
690
1176
|
}
|
|
691
1177
|
// We issued the simulation against the rollup contract, so we need to account for the overhead of the multicall3
|
|
692
1178
|
const gasLimit = this.l1TxUtils.bumpGasLimit(BigInt(Math.ceil(Number(gasUsed) * 64 / 63)));
|
|
693
1179
|
logData.gasLimit = gasLimit;
|
|
1180
|
+
// Store the ABI used for simulation on the request so Multicall3.forward can decode errors
|
|
1181
|
+
// when the tx is sent and a revert is diagnosed via simulation.
|
|
1182
|
+
const requestWithAbi = {
|
|
1183
|
+
...request,
|
|
1184
|
+
abi: simulateAbi
|
|
1185
|
+
};
|
|
694
1186
|
this.log.debug(`Enqueuing ${action}`, logData);
|
|
695
1187
|
this.addRequest({
|
|
696
1188
|
action,
|
|
697
|
-
request,
|
|
1189
|
+
request: requestWithAbi,
|
|
698
1190
|
gasConfig: {
|
|
699
1191
|
gasLimit
|
|
700
1192
|
},
|
|
@@ -772,8 +1264,7 @@ export class SequencerPublisher {
|
|
|
772
1264
|
header: encodedData.header.toViem(),
|
|
773
1265
|
archive: toHex(encodedData.archive),
|
|
774
1266
|
oracleInput: {
|
|
775
|
-
|
|
776
|
-
feeAssetPriceModifier: 0n
|
|
1267
|
+
feeAssetPriceModifier: encodedData.feeAssetPriceModifier
|
|
777
1268
|
}
|
|
778
1269
|
},
|
|
779
1270
|
encodedData.attestationsAndSigners.getPackedAttestations(),
|
|
@@ -801,8 +1292,7 @@ export class SequencerPublisher {
|
|
|
801
1292
|
args
|
|
802
1293
|
});
|
|
803
1294
|
// override the pending checkpoint number if requested
|
|
804
|
-
const
|
|
805
|
-
const forcePendingCheckpointNumberStateDiff = (optsForcePendingCheckpointNumber !== undefined ? await this.rollupContract.makePendingCheckpointNumberOverride(optsForcePendingCheckpointNumber) : []).flatMap((override)=>override.stateDiff ?? []);
|
|
1295
|
+
const forcePendingCheckpointNumberStateDiff = (options.forcePendingCheckpointNumber !== undefined ? await this.rollupContract.makePendingCheckpointNumberOverride(options.forcePendingCheckpointNumber) : []).flatMap((override)=>override.stateDiff ?? []);
|
|
806
1296
|
const stateOverrides = [
|
|
807
1297
|
{
|
|
808
1298
|
address: this.rollupContract.address,
|
|
@@ -826,7 +1316,7 @@ export class SequencerPublisher {
|
|
|
826
1316
|
const simulationResult = await this.l1TxUtils.simulate({
|
|
827
1317
|
to: this.rollupContract.address,
|
|
828
1318
|
data: rollupData,
|
|
829
|
-
gas:
|
|
1319
|
+
gas: MAX_L1_TX_LIMIT,
|
|
830
1320
|
...this.proposerAddressForSimulation && {
|
|
831
1321
|
from: this.proposerAddressForSimulation.toString()
|
|
832
1322
|
}
|
|
@@ -834,10 +1324,10 @@ export class SequencerPublisher {
|
|
|
834
1324
|
// @note we add 1n to the timestamp because geth implementation doesn't like simulation timestamp to be equal to the current block timestamp
|
|
835
1325
|
time: timestamp + 1n,
|
|
836
1326
|
// @note reth should have a 30m gas limit per block but throws errors that this tx is beyond limit so we increase here
|
|
837
|
-
gasLimit:
|
|
1327
|
+
gasLimit: MAX_L1_TX_LIMIT * 2n
|
|
838
1328
|
}, stateOverrides, RollupAbi, {
|
|
839
1329
|
// @note fallback gas estimate to use if the node doesn't support simulation API
|
|
840
|
-
fallbackGasEstimate:
|
|
1330
|
+
fallbackGasEstimate: MAX_L1_TX_LIMIT
|
|
841
1331
|
}).catch((err)=>{
|
|
842
1332
|
// In fisherman mode, we expect ValidatorSelection__MissingProposerSignature since fisherman doesn't have proposer signature
|
|
843
1333
|
const viemError = formatViemError(err);
|
|
@@ -845,7 +1335,7 @@ export class SequencerPublisher {
|
|
|
845
1335
|
this.log.debug(`Ignoring expected ValidatorSelection__MissingProposerSignature error in fisherman mode`);
|
|
846
1336
|
// Return a minimal simulation result with the fallback gas estimate
|
|
847
1337
|
return {
|
|
848
|
-
gasUsed:
|
|
1338
|
+
gasUsed: MAX_L1_TX_LIMIT,
|
|
849
1339
|
logs: []
|
|
850
1340
|
};
|
|
851
1341
|
}
|
|
@@ -857,24 +1347,25 @@ export class SequencerPublisher {
|
|
|
857
1347
|
simulationResult
|
|
858
1348
|
};
|
|
859
1349
|
}
|
|
860
|
-
async addProposeTx(
|
|
1350
|
+
async addProposeTx(checkpoint, encodedData, opts = {}, timestamp) {
|
|
1351
|
+
const slot = checkpoint.header.slotNumber;
|
|
861
1352
|
const timer = new Timer();
|
|
862
1353
|
const kzg = Blob.getViemKzgInstance();
|
|
863
1354
|
const { rollupData, simulationResult, blobEvaluationGas } = await this.prepareProposeTx(encodedData, timestamp, opts);
|
|
864
1355
|
const startBlock = await this.l1TxUtils.getBlockNumber();
|
|
865
1356
|
const gasLimit = this.l1TxUtils.bumpGasLimit(BigInt(Math.ceil(Number(simulationResult.gasUsed) * 64 / 63)) + blobEvaluationGas + SequencerPublisher.MULTICALL_OVERHEAD_GAS_GUESS);
|
|
866
|
-
// Send the blobs to the blob
|
|
867
|
-
// tx fails but it does get mined. We make sure that the blobs are sent to the blob
|
|
868
|
-
void this.
|
|
869
|
-
|
|
870
|
-
|
|
1357
|
+
// Send the blobs to the blob client preemptively. This helps in tests where the sequencer mistakingly thinks that the propose
|
|
1358
|
+
// tx fails but it does get mined. We make sure that the blobs are sent to the blob client regardless of the tx outcome.
|
|
1359
|
+
void Promise.resolve().then(()=>this.blobClient.sendBlobsToFilestore(encodedData.blobs).catch((_err)=>{
|
|
1360
|
+
this.log.error('Failed to send blobs to blob client');
|
|
1361
|
+
}));
|
|
871
1362
|
return this.addRequest({
|
|
872
1363
|
action: 'propose',
|
|
873
1364
|
request: {
|
|
874
1365
|
to: this.rollupContract.address,
|
|
875
1366
|
data: rollupData
|
|
876
1367
|
},
|
|
877
|
-
lastValidL2Slot:
|
|
1368
|
+
lastValidL2Slot: checkpoint.header.slotNumber,
|
|
878
1369
|
gasConfig: {
|
|
879
1370
|
...opts,
|
|
880
1371
|
gasLimit
|
|
@@ -902,25 +1393,23 @@ export class SequencerPublisher {
|
|
|
902
1393
|
calldataGas,
|
|
903
1394
|
calldataSize,
|
|
904
1395
|
sender,
|
|
905
|
-
...
|
|
1396
|
+
...checkpoint.getStats(),
|
|
906
1397
|
eventName: 'rollup-published-to-l1',
|
|
907
1398
|
blobCount: encodedData.blobs.length,
|
|
908
1399
|
inclusionBlocks
|
|
909
1400
|
};
|
|
910
|
-
this.log.info(`Published
|
|
1401
|
+
this.log.info(`Published checkpoint ${checkpoint.number} at slot ${slot} to rollup contract`, {
|
|
911
1402
|
...stats,
|
|
912
|
-
...
|
|
913
|
-
...receipt
|
|
1403
|
+
...checkpoint.getStats(),
|
|
1404
|
+
...pick(receipt, 'transactionHash', 'blockHash')
|
|
914
1405
|
});
|
|
915
1406
|
this.metrics.recordProcessBlockTx(timer.ms(), publishStats);
|
|
916
1407
|
return true;
|
|
917
1408
|
} else {
|
|
918
1409
|
this.metrics.recordFailedTx('process');
|
|
919
|
-
this.log.error(`
|
|
920
|
-
...
|
|
921
|
-
receipt
|
|
922
|
-
txHash: receipt.transactionHash,
|
|
923
|
-
slotNumber: block.header.globalVariables.slotNumber
|
|
1410
|
+
this.log.error(`Publishing checkpoint at slot ${slot} failed with ${errorMsg ?? 'no error message'}`, undefined, {
|
|
1411
|
+
...checkpoint.getStats(),
|
|
1412
|
+
...receipt
|
|
924
1413
|
});
|
|
925
1414
|
return false;
|
|
926
1415
|
}
|