@aztec/sequencer-client 3.0.3-rc.3 → 4.0.0-devnet.1-patch.0
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 +11 -11
- package/dest/client/sequencer-client.d.ts.map +1 -1
- package/dest/client/sequencer-client.js +29 -25
- package/dest/config.d.ts +7 -2
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +66 -28
- package/dest/global_variable_builder/global_builder.d.ts +19 -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 +3 -3
- package/dest/publisher/config.d.ts.map +1 -1
- package/dest/publisher/config.js +2 -2
- package/dest/publisher/sequencer-publisher-factory.d.ts +3 -3
- package/dest/publisher/sequencer-publisher-factory.d.ts.map +1 -1
- package/dest/publisher/sequencer-publisher-factory.js +1 -1
- 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 +43 -39
- package/dest/publisher/sequencer-publisher.d.ts.map +1 -1
- package/dest/publisher/sequencer-publisher.js +599 -130
- package/dest/sequencer/checkpoint_proposal_job.d.ts +102 -0
- package/dest/sequencer/checkpoint_proposal_job.d.ts.map +1 -0
- package/dest/sequencer/checkpoint_proposal_job.js +1191 -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 +22 -2
- package/dest/sequencer/metrics.d.ts.map +1 -1
- package/dest/sequencer/metrics.js +143 -70
- package/dest/sequencer/sequencer.d.ts +107 -129
- package/dest/sequencer/sequencer.d.ts.map +1 -1
- package/dest/sequencer/sequencer.js +677 -603
- package/dest/sequencer/timetable.d.ts +51 -14
- package/dest/sequencer/timetable.d.ts.map +1 -1
- package/dest/sequencer/timetable.js +145 -59
- 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 +3 -2
- 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 +220 -0
- package/dest/test/utils.d.ts +53 -0
- package/dest/test/utils.d.ts.map +1 -0
- package/dest/test/utils.js +103 -0
- package/package.json +30 -28
- package/src/client/sequencer-client.ts +27 -35
- package/src/config.ts +73 -27
- package/src/global_variable_builder/global_builder.ts +57 -51
- package/src/index.ts +1 -7
- package/src/publisher/config.ts +3 -3
- package/src/publisher/sequencer-publisher-factory.ts +3 -3
- package/src/publisher/sequencer-publisher-metrics.ts +19 -71
- package/src/publisher/sequencer-publisher.ts +284 -161
- package/src/sequencer/README.md +531 -0
- package/src/sequencer/checkpoint_proposal_job.ts +877 -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 +189 -77
- package/src/sequencer/sequencer.ts +416 -805
- package/src/sequencer/timetable.ts +175 -80
- package/src/sequencer/types.ts +6 -0
- package/src/sequencer/utils.ts +18 -9
- package/src/test/index.ts +2 -1
- package/src/test/mock_checkpoint_builder.ts +309 -0
- package/src/test/utils.ts +164 -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,11 +1,385 @@
|
|
|
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 { createBlobSinkClient } from '@aztec/blob-sink/client';
|
|
3
375
|
import { MULTI_CALL_3_ADDRESS, Multicall3, RollupContract } from '@aztec/ethereum/contracts';
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
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';
|
|
@@ -13,8 +387,8 @@ import { bufferToHex } from '@aztec/foundation/string';
|
|
|
13
387
|
import { Timer } from '@aztec/foundation/timer';
|
|
14
388
|
import { EmpireBaseAbi, ErrorsAbi, RollupAbi } from '@aztec/l1-artifacts';
|
|
15
389
|
import { encodeSlashConsensusVotes } from '@aztec/slasher';
|
|
16
|
-
import {
|
|
17
|
-
import { getTelemetryClient } from '@aztec/telemetry-client';
|
|
390
|
+
import { CommitteeAttestationsAndSigners } from '@aztec/stdlib/block';
|
|
391
|
+
import { getTelemetryClient, trackSpan } from '@aztec/telemetry-client';
|
|
18
392
|
import { encodeFunctionData, toHex } from 'viem';
|
|
19
393
|
import { SequencerPublisherMetrics } from './sequencer-publisher-metrics.js';
|
|
20
394
|
export const Actions = [
|
|
@@ -30,22 +404,40 @@ export const Actions = [
|
|
|
30
404
|
];
|
|
31
405
|
// Sorting for actions such that invalidations go before proposals, and proposals go before votes
|
|
32
406
|
export const compareActions = (a, b)=>Actions.indexOf(a) - Actions.indexOf(b);
|
|
407
|
+
_dec = trackSpan('SequencerPublisher.sendRequests'), _dec1 = trackSpan('SequencerPublisher.validateBlockHeader'), _dec2 = trackSpan('SequencerPublisher.validateCheckpointForSubmission');
|
|
33
408
|
export class SequencerPublisher {
|
|
34
409
|
config;
|
|
410
|
+
static{
|
|
411
|
+
({ e: [_initProto] } = _apply_decs_2203_r(this, [
|
|
412
|
+
[
|
|
413
|
+
_dec,
|
|
414
|
+
2,
|
|
415
|
+
"sendRequests"
|
|
416
|
+
],
|
|
417
|
+
[
|
|
418
|
+
_dec1,
|
|
419
|
+
2,
|
|
420
|
+
"validateBlockHeader"
|
|
421
|
+
],
|
|
422
|
+
[
|
|
423
|
+
_dec2,
|
|
424
|
+
2,
|
|
425
|
+
"validateCheckpointForSubmission"
|
|
426
|
+
]
|
|
427
|
+
], []));
|
|
428
|
+
}
|
|
35
429
|
interrupted;
|
|
36
430
|
metrics;
|
|
37
431
|
epochCache;
|
|
38
432
|
governanceLog;
|
|
39
433
|
slashingLog;
|
|
40
434
|
lastActions;
|
|
435
|
+
isPayloadEmptyCache;
|
|
41
436
|
log;
|
|
42
437
|
ethereumSlotDuration;
|
|
43
|
-
|
|
438
|
+
blobClient;
|
|
44
439
|
/** Address to use for simulations in fisherman mode (actual proposer's address) */ proposerAddressForSimulation;
|
|
45
|
-
|
|
46
|
-
// Total used for full block from int_l1_pub e2e test: 1m (of which 86k is 1x blob)
|
|
47
|
-
// Total used for emptier block from above test: 429k (of which 84k is 1x blob)
|
|
48
|
-
static PROPOSE_GAS_GUESS = 12_000_000n;
|
|
440
|
+
/** L1 fee analyzer for fisherman mode */ l1FeeAnalyzer;
|
|
49
441
|
// A CALL to a cold address is 2700 gas
|
|
50
442
|
static MULTICALL_OVERHEAD_GAS_GUESS = 5000n;
|
|
51
443
|
// Gas report for VotingWithSigTest shows a max gas of 100k, but we've seen it cost 700k+ in testnet
|
|
@@ -55,23 +447,24 @@ export class SequencerPublisher {
|
|
|
55
447
|
govProposerContract;
|
|
56
448
|
slashingProposerContract;
|
|
57
449
|
slashFactoryContract;
|
|
450
|
+
tracer;
|
|
58
451
|
requests;
|
|
59
452
|
constructor(config, deps){
|
|
60
453
|
this.config = config;
|
|
61
|
-
this.interrupted = false;
|
|
454
|
+
this.interrupted = (_initProto(this), false);
|
|
62
455
|
this.governanceLog = createLogger('sequencer:publisher:governance');
|
|
63
456
|
this.slashingLog = createLogger('sequencer:publisher:slashing');
|
|
64
457
|
this.lastActions = {};
|
|
458
|
+
this.isPayloadEmptyCache = new Map();
|
|
65
459
|
this.requests = [];
|
|
66
460
|
this.log = deps.log ?? createLogger('sequencer:publisher');
|
|
67
461
|
this.ethereumSlotDuration = BigInt(config.ethereumSlotDuration);
|
|
68
462
|
this.epochCache = deps.epochCache;
|
|
69
463
|
this.lastActions = deps.lastActions;
|
|
70
|
-
this.
|
|
71
|
-
logger: createLogger('sequencer:blob-sink:client')
|
|
72
|
-
});
|
|
464
|
+
this.blobClient = deps.blobClient;
|
|
73
465
|
const telemetry = deps.telemetry ?? getTelemetryClient();
|
|
74
466
|
this.metrics = deps.metrics ?? new SequencerPublisherMetrics(telemetry, 'SequencerPublisher');
|
|
467
|
+
this.tracer = telemetry.getTracer('SequencerPublisher');
|
|
75
468
|
this.l1TxUtils = deps.l1TxUtils;
|
|
76
469
|
this.rollupContract = deps.rollupContract;
|
|
77
470
|
this.govProposerContract = deps.governanceProposerContract;
|
|
@@ -82,6 +475,10 @@ export class SequencerPublisher {
|
|
|
82
475
|
this.slashingProposerContract = newSlashingProposer;
|
|
83
476
|
});
|
|
84
477
|
this.slashFactoryContract = deps.slashFactoryContract;
|
|
478
|
+
// Initialize L1 fee analyzer for fisherman mode
|
|
479
|
+
if (config.fishermanMode) {
|
|
480
|
+
this.l1FeeAnalyzer = new L1FeeAnalyzer(this.l1TxUtils.client, deps.dateProvider, createLogger('sequencer:publisher:fee-analyzer'));
|
|
481
|
+
}
|
|
85
482
|
}
|
|
86
483
|
getRollupContract() {
|
|
87
484
|
return this.rollupContract;
|
|
@@ -90,6 +487,11 @@ export class SequencerPublisher {
|
|
|
90
487
|
return this.l1TxUtils.getSenderAddress();
|
|
91
488
|
}
|
|
92
489
|
/**
|
|
490
|
+
* Gets the L1 fee analyzer instance (only available in fisherman mode)
|
|
491
|
+
*/ getL1FeeAnalyzer() {
|
|
492
|
+
return this.l1FeeAnalyzer;
|
|
493
|
+
}
|
|
494
|
+
/**
|
|
93
495
|
* Sets the proposer address to use for simulations in fisherman mode.
|
|
94
496
|
* @param proposerAddress - The actual proposer's address to use for balance lookups in simulations
|
|
95
497
|
*/ setProposerAddressForSimulation(proposerAddress) {
|
|
@@ -111,6 +513,46 @@ export class SequencerPublisher {
|
|
|
111
513
|
}
|
|
112
514
|
}
|
|
113
515
|
/**
|
|
516
|
+
* Analyzes L1 fees for the pending requests without sending them.
|
|
517
|
+
* This is used in fisherman mode to validate fee calculations.
|
|
518
|
+
* @param l2SlotNumber - The L2 slot number for this analysis
|
|
519
|
+
* @param onComplete - Optional callback to invoke when analysis completes (after block is mined)
|
|
520
|
+
* @returns The analysis result (incomplete until block mines), or undefined if no requests
|
|
521
|
+
*/ async analyzeL1Fees(l2SlotNumber, onComplete) {
|
|
522
|
+
if (!this.l1FeeAnalyzer) {
|
|
523
|
+
this.log.warn('L1 fee analyzer not available (not in fisherman mode)');
|
|
524
|
+
return undefined;
|
|
525
|
+
}
|
|
526
|
+
const requestsToAnalyze = [
|
|
527
|
+
...this.requests
|
|
528
|
+
];
|
|
529
|
+
if (requestsToAnalyze.length === 0) {
|
|
530
|
+
this.log.debug('No requests to analyze for L1 fees');
|
|
531
|
+
return undefined;
|
|
532
|
+
}
|
|
533
|
+
// Extract blob config from requests (if any)
|
|
534
|
+
const blobConfigs = requestsToAnalyze.filter((request)=>request.blobConfig).map((request)=>request.blobConfig);
|
|
535
|
+
const blobConfig = blobConfigs[0];
|
|
536
|
+
// Get gas configs
|
|
537
|
+
const gasConfigs = requestsToAnalyze.filter((request)=>request.gasConfig).map((request)=>request.gasConfig);
|
|
538
|
+
const gasLimits = gasConfigs.map((g)=>g?.gasLimit).filter((g)=>g !== undefined);
|
|
539
|
+
const gasLimit = gasLimits.length > 0 ? gasLimits.reduce((sum, g)=>sum + g, 0n) : 0n;
|
|
540
|
+
// Get the transaction requests
|
|
541
|
+
const l1Requests = requestsToAnalyze.map((r)=>r.request);
|
|
542
|
+
// Start the analysis
|
|
543
|
+
const analysisId = await this.l1FeeAnalyzer.startAnalysis(l2SlotNumber, gasLimit > 0n ? gasLimit : MAX_L1_TX_LIMIT, l1Requests, blobConfig, onComplete);
|
|
544
|
+
this.log.info('Started L1 fee analysis', {
|
|
545
|
+
analysisId,
|
|
546
|
+
l2SlotNumber: l2SlotNumber.toString(),
|
|
547
|
+
requestCount: requestsToAnalyze.length,
|
|
548
|
+
hasBlobConfig: !!blobConfig,
|
|
549
|
+
gasLimit: gasLimit.toString(),
|
|
550
|
+
actions: requestsToAnalyze.map((r)=>r.action)
|
|
551
|
+
});
|
|
552
|
+
// Return the analysis result (will be incomplete until block mines)
|
|
553
|
+
return this.l1FeeAnalyzer.getAnalysis(analysisId);
|
|
554
|
+
}
|
|
555
|
+
/**
|
|
114
556
|
* Sends all requests that are still valid.
|
|
115
557
|
* @returns one of:
|
|
116
558
|
* - A receipt and stats if the tx succeeded
|
|
@@ -121,7 +563,7 @@ export class SequencerPublisher {
|
|
|
121
563
|
...this.requests
|
|
122
564
|
];
|
|
123
565
|
this.requests = [];
|
|
124
|
-
if (this.interrupted) {
|
|
566
|
+
if (this.interrupted || requestsToProcess.length === 0) {
|
|
125
567
|
return undefined;
|
|
126
568
|
}
|
|
127
569
|
const currentL2Slot = this.getCurrentL2Slot();
|
|
@@ -156,7 +598,16 @@ export class SequencerPublisher {
|
|
|
156
598
|
const blobConfig = blobConfigs[0];
|
|
157
599
|
// Merge gasConfigs. Yields the sum of gasLimits, and the earliest txTimeoutAt, or undefined if no gasConfig sets them.
|
|
158
600
|
const gasLimits = gasConfigs.map((g)=>g?.gasLimit).filter((g)=>g !== undefined);
|
|
159
|
-
|
|
601
|
+
let gasLimit = gasLimits.length > 0 ? sumBigint(gasLimits) : undefined; // sum
|
|
602
|
+
// Cap at L1 block gas limit so the node accepts the tx ("gas limit too high" otherwise).
|
|
603
|
+
const maxGas = MAX_L1_TX_LIMIT;
|
|
604
|
+
if (gasLimit !== undefined && gasLimit > maxGas) {
|
|
605
|
+
this.log.debug('Capping bundled tx gas limit to L1 max', {
|
|
606
|
+
requested: gasLimit,
|
|
607
|
+
capped: maxGas
|
|
608
|
+
});
|
|
609
|
+
gasLimit = maxGas;
|
|
610
|
+
}
|
|
160
611
|
const txTimeoutAts = gasConfigs.map((g)=>g?.txTimeoutAt).filter((g)=>g !== undefined);
|
|
161
612
|
const txTimeoutAt = txTimeoutAts.length > 0 ? new Date(Math.min(...txTimeoutAts.map((g)=>g.getTime()))) : undefined; // earliest
|
|
162
613
|
const txConfig = {
|
|
@@ -231,7 +682,7 @@ export class SequencerPublisher {
|
|
|
231
682
|
'InvalidArchive'
|
|
232
683
|
];
|
|
233
684
|
return this.rollupContract.canProposeAtNextEthBlock(tipArchive.toBuffer(), msgSender.toString(), Number(this.ethereumSlotDuration), {
|
|
234
|
-
forcePendingCheckpointNumber: opts.
|
|
685
|
+
forcePendingCheckpointNumber: opts.forcePendingCheckpointNumber
|
|
235
686
|
}).catch((err)=>{
|
|
236
687
|
if (err instanceof FormattedViemError && ignoredErrors.find((e)=>err.message.includes(e))) {
|
|
237
688
|
this.log.warn(`Failed canProposeAtTime check with ${ignoredErrors.find((e)=>err.message.includes(e))}`, {
|
|
@@ -259,12 +710,11 @@ export class SequencerPublisher {
|
|
|
259
710
|
[],
|
|
260
711
|
Signature.empty().toViemSignature(),
|
|
261
712
|
`0x${'0'.repeat(64)}`,
|
|
262
|
-
header.
|
|
713
|
+
header.blobsHash.toString(),
|
|
263
714
|
flags
|
|
264
715
|
];
|
|
265
716
|
const ts = BigInt((await this.l1TxUtils.getBlock()).timestamp + this.ethereumSlotDuration);
|
|
266
|
-
const
|
|
267
|
-
const stateOverrides = await this.rollupContract.makePendingCheckpointNumberOverride(optsForcePendingCheckpointNumber);
|
|
717
|
+
const stateOverrides = await this.rollupContract.makePendingCheckpointNumberOverride(opts?.forcePendingCheckpointNumber);
|
|
268
718
|
let balance = 0n;
|
|
269
719
|
if (this.config.fishermanMode) {
|
|
270
720
|
// In fisherman mode, we can't know where the proposer is publishing from
|
|
@@ -291,34 +741,37 @@ export class SequencerPublisher {
|
|
|
291
741
|
this.log.debug(`Simulated validateHeader`);
|
|
292
742
|
}
|
|
293
743
|
/**
|
|
294
|
-
* Simulate making a call to invalidate a
|
|
295
|
-
* @param
|
|
296
|
-
*/ async
|
|
744
|
+
* Simulate making a call to invalidate a checkpoint with invalid attestations. Returns undefined if no need to invalidate.
|
|
745
|
+
* @param validationResult - The validation result indicating which checkpoint to invalidate (as returned by the archiver)
|
|
746
|
+
*/ async simulateInvalidateCheckpoint(validationResult) {
|
|
297
747
|
if (validationResult.valid) {
|
|
298
748
|
return undefined;
|
|
299
749
|
}
|
|
300
|
-
const { reason,
|
|
301
|
-
const
|
|
750
|
+
const { reason, checkpoint } = validationResult;
|
|
751
|
+
const checkpointNumber = checkpoint.checkpointNumber;
|
|
302
752
|
const logData = {
|
|
303
|
-
...
|
|
753
|
+
...checkpoint,
|
|
304
754
|
reason
|
|
305
755
|
};
|
|
306
|
-
const
|
|
307
|
-
if (
|
|
308
|
-
this.log.verbose(`Skipping
|
|
309
|
-
|
|
756
|
+
const currentCheckpointNumber = await this.rollupContract.getCheckpointNumber();
|
|
757
|
+
if (currentCheckpointNumber < checkpointNumber) {
|
|
758
|
+
this.log.verbose(`Skipping checkpoint ${checkpointNumber} invalidation since it has already been removed from the pending chain`, {
|
|
759
|
+
currentCheckpointNumber,
|
|
310
760
|
...logData
|
|
311
761
|
});
|
|
312
762
|
return undefined;
|
|
313
763
|
}
|
|
314
|
-
const request = this.
|
|
315
|
-
this.log.debug(`Simulating invalidate
|
|
764
|
+
const request = this.buildInvalidateCheckpointRequest(validationResult);
|
|
765
|
+
this.log.debug(`Simulating invalidate checkpoint ${checkpointNumber}`, {
|
|
316
766
|
...logData,
|
|
317
767
|
request
|
|
318
768
|
});
|
|
319
769
|
try {
|
|
320
|
-
const { gasUsed } = await this.l1TxUtils.simulate(request, undefined, undefined,
|
|
321
|
-
|
|
770
|
+
const { gasUsed } = await this.l1TxUtils.simulate(request, undefined, undefined, mergeAbis([
|
|
771
|
+
request.abi ?? [],
|
|
772
|
+
ErrorsAbi
|
|
773
|
+
]));
|
|
774
|
+
this.log.verbose(`Simulation for invalidate checkpoint ${checkpointNumber} succeeded`, {
|
|
322
775
|
...logData,
|
|
323
776
|
request,
|
|
324
777
|
gasUsed
|
|
@@ -326,88 +779,83 @@ export class SequencerPublisher {
|
|
|
326
779
|
return {
|
|
327
780
|
request,
|
|
328
781
|
gasUsed,
|
|
329
|
-
|
|
330
|
-
|
|
782
|
+
checkpointNumber,
|
|
783
|
+
forcePendingCheckpointNumber: CheckpointNumber(checkpointNumber - 1),
|
|
331
784
|
reason
|
|
332
785
|
};
|
|
333
786
|
} catch (err) {
|
|
334
787
|
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
|
|
788
|
+
// If the error is due to the checkpoint not being in the pending chain, and it was indeed removed by someone else,
|
|
789
|
+
// we can safely ignore it and return undefined so we go ahead with checkpoint building.
|
|
790
|
+
if (viemError.message?.includes('Rollup__CheckpointNotInPendingChain')) {
|
|
791
|
+
this.log.verbose(`Simulation for invalidate checkpoint ${checkpointNumber} failed due to checkpoint not being in pending chain`, {
|
|
339
792
|
...logData,
|
|
340
793
|
request,
|
|
341
794
|
error: viemError.message
|
|
342
795
|
});
|
|
343
|
-
const
|
|
344
|
-
if (
|
|
345
|
-
this.log.verbose(`
|
|
796
|
+
const latestPendingCheckpointNumber = await this.rollupContract.getCheckpointNumber();
|
|
797
|
+
if (latestPendingCheckpointNumber < checkpointNumber) {
|
|
798
|
+
this.log.verbose(`Checkpoint ${checkpointNumber} has already been invalidated`, {
|
|
346
799
|
...logData
|
|
347
800
|
});
|
|
348
801
|
return undefined;
|
|
349
802
|
} else {
|
|
350
|
-
this.log.error(`Simulation for invalidate ${
|
|
351
|
-
throw new Error(`Failed to simulate invalidate
|
|
803
|
+
this.log.error(`Simulation for invalidate checkpoint ${checkpointNumber} failed and it is still in pending chain`, viemError, logData);
|
|
804
|
+
throw new Error(`Failed to simulate invalidate checkpoint ${checkpointNumber} while it is still in pending chain`, {
|
|
352
805
|
cause: viemError
|
|
353
806
|
});
|
|
354
807
|
}
|
|
355
808
|
}
|
|
356
|
-
// Otherwise, throw. We cannot build the next
|
|
357
|
-
this.log.error(`Simulation for invalidate
|
|
358
|
-
throw new Error(`Failed to simulate invalidate
|
|
809
|
+
// Otherwise, throw. We cannot build the next checkpoint if we cannot invalidate the previous one.
|
|
810
|
+
this.log.error(`Simulation for invalidate checkpoint ${checkpointNumber} failed`, viemError, logData);
|
|
811
|
+
throw new Error(`Failed to simulate invalidate checkpoint ${checkpointNumber}`, {
|
|
359
812
|
cause: viemError
|
|
360
813
|
});
|
|
361
814
|
}
|
|
362
815
|
}
|
|
363
|
-
|
|
816
|
+
buildInvalidateCheckpointRequest(validationResult) {
|
|
364
817
|
if (validationResult.valid) {
|
|
365
|
-
throw new Error('Cannot invalidate a valid
|
|
818
|
+
throw new Error('Cannot invalidate a valid checkpoint');
|
|
366
819
|
}
|
|
367
|
-
const {
|
|
820
|
+
const { checkpoint, committee, reason } = validationResult;
|
|
368
821
|
const logData = {
|
|
369
|
-
...
|
|
822
|
+
...checkpoint,
|
|
370
823
|
reason
|
|
371
824
|
};
|
|
372
|
-
this.log.debug(`
|
|
825
|
+
this.log.debug(`Building invalidate checkpoint ${checkpoint.checkpointNumber} request`, logData);
|
|
373
826
|
const attestationsAndSigners = new CommitteeAttestationsAndSigners(validationResult.attestations).getPackedAttestations();
|
|
374
827
|
if (reason === 'invalid-attestation') {
|
|
375
|
-
return this.rollupContract.buildInvalidateBadAttestationRequest(
|
|
828
|
+
return this.rollupContract.buildInvalidateBadAttestationRequest(checkpoint.checkpointNumber, attestationsAndSigners, committee, validationResult.invalidIndex);
|
|
376
829
|
} else if (reason === 'insufficient-attestations') {
|
|
377
|
-
return this.rollupContract.buildInvalidateInsufficientAttestationsRequest(
|
|
830
|
+
return this.rollupContract.buildInvalidateInsufficientAttestationsRequest(checkpoint.checkpointNumber, attestationsAndSigners, committee);
|
|
378
831
|
} else {
|
|
379
832
|
const _ = reason;
|
|
380
833
|
throw new Error(`Unknown reason for invalidation`);
|
|
381
834
|
}
|
|
382
835
|
}
|
|
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) {
|
|
836
|
+
/** Simulates `propose` to make sure that the checkpoint is valid for submission */ async validateCheckpointForSubmission(checkpoint, attestationsAndSigners, attestationsAndSignersSignature, options) {
|
|
392
837
|
const ts = BigInt((await this.l1TxUtils.getBlock()).timestamp + this.ethereumSlotDuration);
|
|
838
|
+
// TODO(palla/mbps): This should not be needed, there's no flow where we propose with zero attestations. Or is there?
|
|
393
839
|
// If we have no attestations, we still need to provide the empty attestations
|
|
394
840
|
// so that the committee is recalculated correctly
|
|
395
|
-
const ignoreSignatures = attestationsAndSigners.attestations.length === 0;
|
|
396
|
-
if (ignoreSignatures) {
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
841
|
+
// const ignoreSignatures = attestationsAndSigners.attestations.length === 0;
|
|
842
|
+
// if (ignoreSignatures) {
|
|
843
|
+
// const { committee } = await this.epochCache.getCommittee(block.header.globalVariables.slotNumber);
|
|
844
|
+
// if (!committee) {
|
|
845
|
+
// this.log.warn(`No committee found for slot ${block.header.globalVariables.slotNumber}`);
|
|
846
|
+
// throw new Error(`No committee found for slot ${block.header.globalVariables.slotNumber}`);
|
|
847
|
+
// }
|
|
848
|
+
// attestationsAndSigners.attestations = committee.map(committeeMember =>
|
|
849
|
+
// CommitteeAttestation.fromAddress(committeeMember),
|
|
850
|
+
// );
|
|
851
|
+
// }
|
|
852
|
+
const blobFields = checkpoint.toBlobFields();
|
|
405
853
|
const blobs = getBlobsPerL1Block(blobFields);
|
|
406
854
|
const blobInput = getPrefixedEthBlobCommitments(blobs);
|
|
407
855
|
const args = [
|
|
408
856
|
{
|
|
409
|
-
header:
|
|
410
|
-
archive: toHex(
|
|
857
|
+
header: checkpoint.header.toViem(),
|
|
858
|
+
archive: toHex(checkpoint.archive.root.toBuffer()),
|
|
411
859
|
oracleInput: {
|
|
412
860
|
feeAssetPriceModifier: 0n
|
|
413
861
|
}
|
|
@@ -434,9 +882,16 @@ export class SequencerPublisher {
|
|
|
434
882
|
}
|
|
435
883
|
const round = await base.computeRound(slotNumber);
|
|
436
884
|
const roundInfo = await base.getRoundInfo(this.rollupContract.address, round);
|
|
885
|
+
if (roundInfo.quorumReached) {
|
|
886
|
+
return false;
|
|
887
|
+
}
|
|
437
888
|
if (roundInfo.lastSignalSlot >= slotNumber) {
|
|
438
889
|
return false;
|
|
439
890
|
}
|
|
891
|
+
if (await this.isPayloadEmpty(payload)) {
|
|
892
|
+
this.log.warn(`Skipping vote cast for payload with empty code`);
|
|
893
|
+
return false;
|
|
894
|
+
}
|
|
440
895
|
const cachedLastVote = this.lastActions[signalType];
|
|
441
896
|
this.lastActions[signalType] = slotNumber;
|
|
442
897
|
const action = signalType;
|
|
@@ -450,7 +905,10 @@ export class SequencerPublisher {
|
|
|
450
905
|
try {
|
|
451
906
|
await this.l1TxUtils.simulate(request, {
|
|
452
907
|
time: timestamp
|
|
453
|
-
}, [],
|
|
908
|
+
}, [], mergeAbis([
|
|
909
|
+
request.abi ?? [],
|
|
910
|
+
ErrorsAbi
|
|
911
|
+
]));
|
|
454
912
|
this.log.debug(`Simulation for ${action} at slot ${slotNumber} succeeded`, {
|
|
455
913
|
request
|
|
456
914
|
});
|
|
@@ -475,17 +933,27 @@ export class SequencerPublisher {
|
|
|
475
933
|
payload: payload.toString()
|
|
476
934
|
};
|
|
477
935
|
if (!success) {
|
|
478
|
-
this.log.error(`Signaling in
|
|
936
|
+
this.log.error(`Signaling in ${action} for ${payload} at slot ${slotNumber} in round ${round} failed`, logData);
|
|
479
937
|
this.lastActions[signalType] = cachedLastVote;
|
|
480
938
|
return false;
|
|
481
939
|
} else {
|
|
482
|
-
this.log.info(`Signaling in
|
|
940
|
+
this.log.info(`Signaling in ${action} for ${payload} at slot ${slotNumber} in round ${round} succeeded`, logData);
|
|
483
941
|
return true;
|
|
484
942
|
}
|
|
485
943
|
}
|
|
486
944
|
});
|
|
487
945
|
return true;
|
|
488
946
|
}
|
|
947
|
+
async isPayloadEmpty(payload) {
|
|
948
|
+
const key = payload.toString();
|
|
949
|
+
const cached = this.isPayloadEmptyCache.get(key);
|
|
950
|
+
if (cached) {
|
|
951
|
+
return cached;
|
|
952
|
+
}
|
|
953
|
+
const isEmpty = !await this.l1TxUtils.getCode(payload);
|
|
954
|
+
this.isPayloadEmptyCache.set(key, isEmpty);
|
|
955
|
+
return isEmpty;
|
|
956
|
+
}
|
|
489
957
|
/**
|
|
490
958
|
* Enqueues a governance castSignal transaction to cast a signal for a given slot number.
|
|
491
959
|
* @param slotNumber - The slot number to cast a signal for.
|
|
@@ -581,19 +1049,13 @@ export class SequencerPublisher {
|
|
|
581
1049
|
}
|
|
582
1050
|
return true;
|
|
583
1051
|
}
|
|
584
|
-
/**
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
* @param block - L2 block to propose.
|
|
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();
|
|
1052
|
+
/** Simulates and enqueues a proposal for a checkpoint on L1 */ async enqueueProposeCheckpoint(checkpoint, attestationsAndSigners, attestationsAndSignersSignature, opts = {}) {
|
|
1053
|
+
const checkpointHeader = checkpoint.header;
|
|
1054
|
+
const blobFields = checkpoint.toBlobFields();
|
|
592
1055
|
const blobs = getBlobsPerL1Block(blobFields);
|
|
593
1056
|
const proposeTxArgs = {
|
|
594
1057
|
header: checkpointHeader,
|
|
595
|
-
archive:
|
|
596
|
-
body: block.body.toBuffer(),
|
|
1058
|
+
archive: checkpoint.archive.root.toBuffer(),
|
|
597
1059
|
blobs,
|
|
598
1060
|
attestationsAndSigners,
|
|
599
1061
|
attestationsAndSignersSignature
|
|
@@ -605,36 +1067,35 @@ export class SequencerPublisher {
|
|
|
605
1067
|
// By simulation issue, I mean the fact that the block.timestamp is equal to the last block, not the next, which
|
|
606
1068
|
// make time consistency checks break.
|
|
607
1069
|
// 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.
|
|
1070
|
+
ts = await this.validateCheckpointForSubmission(checkpoint, attestationsAndSigners, attestationsAndSignersSignature, opts);
|
|
609
1071
|
} catch (err) {
|
|
610
|
-
this.log.error(`
|
|
611
|
-
...
|
|
612
|
-
slotNumber:
|
|
613
|
-
|
|
1072
|
+
this.log.error(`Checkpoint validation failed. ${err instanceof Error ? err.message : 'No error message'}`, err, {
|
|
1073
|
+
...checkpoint.getStats(),
|
|
1074
|
+
slotNumber: checkpoint.header.slotNumber,
|
|
1075
|
+
forcePendingCheckpointNumber: opts.forcePendingCheckpointNumber
|
|
614
1076
|
});
|
|
615
1077
|
throw err;
|
|
616
1078
|
}
|
|
617
|
-
this.log.verbose(`Enqueuing
|
|
618
|
-
...
|
|
1079
|
+
this.log.verbose(`Enqueuing checkpoint propose transaction`, {
|
|
1080
|
+
...checkpoint.toCheckpointInfo(),
|
|
619
1081
|
...opts
|
|
620
1082
|
});
|
|
621
|
-
await this.addProposeTx(
|
|
622
|
-
return true;
|
|
1083
|
+
await this.addProposeTx(checkpoint, proposeTxArgs, opts, ts);
|
|
623
1084
|
}
|
|
624
|
-
|
|
1085
|
+
enqueueInvalidateCheckpoint(request, opts = {}) {
|
|
625
1086
|
if (!request) {
|
|
626
1087
|
return;
|
|
627
1088
|
}
|
|
628
1089
|
// We issued the simulation against the rollup contract, so we need to account for the overhead of the multicall3
|
|
629
1090
|
const gasLimit = this.l1TxUtils.bumpGasLimit(BigInt(Math.ceil(Number(request.gasUsed) * 64 / 63)));
|
|
630
|
-
const { gasUsed,
|
|
1091
|
+
const { gasUsed, checkpointNumber } = request;
|
|
631
1092
|
const logData = {
|
|
632
1093
|
gasUsed,
|
|
633
|
-
|
|
1094
|
+
checkpointNumber,
|
|
634
1095
|
gasLimit,
|
|
635
1096
|
opts
|
|
636
1097
|
};
|
|
637
|
-
this.log.verbose(`Enqueuing invalidate
|
|
1098
|
+
this.log.verbose(`Enqueuing invalidate checkpoint request`, logData);
|
|
638
1099
|
this.addRequest({
|
|
639
1100
|
action: `invalidate-by-${request.reason}`,
|
|
640
1101
|
request: request.request,
|
|
@@ -646,12 +1107,12 @@ export class SequencerPublisher {
|
|
|
646
1107
|
checkSuccess: (_req, result)=>{
|
|
647
1108
|
const success = result && result.receipt && result.receipt.status === 'success' && tryExtractEvent(result.receipt.logs, this.rollupContract.address, RollupAbi, 'CheckpointInvalidated');
|
|
648
1109
|
if (!success) {
|
|
649
|
-
this.log.warn(`Invalidate
|
|
1110
|
+
this.log.warn(`Invalidate checkpoint ${request.checkpointNumber} failed`, {
|
|
650
1111
|
...result,
|
|
651
1112
|
...logData
|
|
652
1113
|
});
|
|
653
1114
|
} else {
|
|
654
|
-
this.log.info(`Invalidate
|
|
1115
|
+
this.log.info(`Invalidate checkpoint ${request.checkpointNumber} succeeded`, {
|
|
655
1116
|
...result,
|
|
656
1117
|
...logData
|
|
657
1118
|
});
|
|
@@ -674,27 +1135,37 @@ export class SequencerPublisher {
|
|
|
674
1135
|
this.lastActions[action] = slotNumber;
|
|
675
1136
|
this.log.debug(`Simulating ${action} for slot ${slotNumber}`, logData);
|
|
676
1137
|
let gasUsed;
|
|
1138
|
+
const simulateAbi = mergeAbis([
|
|
1139
|
+
request.abi ?? [],
|
|
1140
|
+
ErrorsAbi
|
|
1141
|
+
]);
|
|
677
1142
|
try {
|
|
678
1143
|
({ gasUsed } = await this.l1TxUtils.simulate(request, {
|
|
679
1144
|
time: timestamp
|
|
680
|
-
}, [],
|
|
1145
|
+
}, [], simulateAbi)); // TODO(palla/slash): Check the timestamp logic
|
|
681
1146
|
this.log.verbose(`Simulation for ${action} succeeded`, {
|
|
682
1147
|
...logData,
|
|
683
1148
|
request,
|
|
684
1149
|
gasUsed
|
|
685
1150
|
});
|
|
686
1151
|
} catch (err) {
|
|
687
|
-
const viemError = formatViemError(err);
|
|
1152
|
+
const viemError = formatViemError(err, simulateAbi);
|
|
688
1153
|
this.log.error(`Simulation for ${action} at ${slotNumber} failed`, viemError, logData);
|
|
689
1154
|
return false;
|
|
690
1155
|
}
|
|
691
1156
|
// We issued the simulation against the rollup contract, so we need to account for the overhead of the multicall3
|
|
692
1157
|
const gasLimit = this.l1TxUtils.bumpGasLimit(BigInt(Math.ceil(Number(gasUsed) * 64 / 63)));
|
|
693
1158
|
logData.gasLimit = gasLimit;
|
|
1159
|
+
// Store the ABI used for simulation on the request so Multicall3.forward can decode errors
|
|
1160
|
+
// when the tx is sent and a revert is diagnosed via simulation.
|
|
1161
|
+
const requestWithAbi = {
|
|
1162
|
+
...request,
|
|
1163
|
+
abi: simulateAbi
|
|
1164
|
+
};
|
|
694
1165
|
this.log.debug(`Enqueuing ${action}`, logData);
|
|
695
1166
|
this.addRequest({
|
|
696
1167
|
action,
|
|
697
|
-
request,
|
|
1168
|
+
request: requestWithAbi,
|
|
698
1169
|
gasConfig: {
|
|
699
1170
|
gasLimit
|
|
700
1171
|
},
|
|
@@ -801,8 +1272,7 @@ export class SequencerPublisher {
|
|
|
801
1272
|
args
|
|
802
1273
|
});
|
|
803
1274
|
// override the pending checkpoint number if requested
|
|
804
|
-
const
|
|
805
|
-
const forcePendingCheckpointNumberStateDiff = (optsForcePendingCheckpointNumber !== undefined ? await this.rollupContract.makePendingCheckpointNumberOverride(optsForcePendingCheckpointNumber) : []).flatMap((override)=>override.stateDiff ?? []);
|
|
1275
|
+
const forcePendingCheckpointNumberStateDiff = (options.forcePendingCheckpointNumber !== undefined ? await this.rollupContract.makePendingCheckpointNumberOverride(options.forcePendingCheckpointNumber) : []).flatMap((override)=>override.stateDiff ?? []);
|
|
806
1276
|
const stateOverrides = [
|
|
807
1277
|
{
|
|
808
1278
|
address: this.rollupContract.address,
|
|
@@ -826,7 +1296,7 @@ export class SequencerPublisher {
|
|
|
826
1296
|
const simulationResult = await this.l1TxUtils.simulate({
|
|
827
1297
|
to: this.rollupContract.address,
|
|
828
1298
|
data: rollupData,
|
|
829
|
-
gas:
|
|
1299
|
+
gas: MAX_L1_TX_LIMIT,
|
|
830
1300
|
...this.proposerAddressForSimulation && {
|
|
831
1301
|
from: this.proposerAddressForSimulation.toString()
|
|
832
1302
|
}
|
|
@@ -834,10 +1304,10 @@ export class SequencerPublisher {
|
|
|
834
1304
|
// @note we add 1n to the timestamp because geth implementation doesn't like simulation timestamp to be equal to the current block timestamp
|
|
835
1305
|
time: timestamp + 1n,
|
|
836
1306
|
// @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:
|
|
1307
|
+
gasLimit: MAX_L1_TX_LIMIT * 2n
|
|
838
1308
|
}, stateOverrides, RollupAbi, {
|
|
839
1309
|
// @note fallback gas estimate to use if the node doesn't support simulation API
|
|
840
|
-
fallbackGasEstimate:
|
|
1310
|
+
fallbackGasEstimate: MAX_L1_TX_LIMIT
|
|
841
1311
|
}).catch((err)=>{
|
|
842
1312
|
// In fisherman mode, we expect ValidatorSelection__MissingProposerSignature since fisherman doesn't have proposer signature
|
|
843
1313
|
const viemError = formatViemError(err);
|
|
@@ -845,7 +1315,7 @@ export class SequencerPublisher {
|
|
|
845
1315
|
this.log.debug(`Ignoring expected ValidatorSelection__MissingProposerSignature error in fisherman mode`);
|
|
846
1316
|
// Return a minimal simulation result with the fallback gas estimate
|
|
847
1317
|
return {
|
|
848
|
-
gasUsed:
|
|
1318
|
+
gasUsed: MAX_L1_TX_LIMIT,
|
|
849
1319
|
logs: []
|
|
850
1320
|
};
|
|
851
1321
|
}
|
|
@@ -857,24 +1327,25 @@ export class SequencerPublisher {
|
|
|
857
1327
|
simulationResult
|
|
858
1328
|
};
|
|
859
1329
|
}
|
|
860
|
-
async addProposeTx(
|
|
1330
|
+
async addProposeTx(checkpoint, encodedData, opts = {}, timestamp) {
|
|
1331
|
+
const slot = checkpoint.header.slotNumber;
|
|
861
1332
|
const timer = new Timer();
|
|
862
1333
|
const kzg = Blob.getViemKzgInstance();
|
|
863
1334
|
const { rollupData, simulationResult, blobEvaluationGas } = await this.prepareProposeTx(encodedData, timestamp, opts);
|
|
864
1335
|
const startBlock = await this.l1TxUtils.getBlockNumber();
|
|
865
1336
|
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
|
-
|
|
1337
|
+
// Send the blobs to the blob client preemptively. This helps in tests where the sequencer mistakingly thinks that the propose
|
|
1338
|
+
// tx fails but it does get mined. We make sure that the blobs are sent to the blob client regardless of the tx outcome.
|
|
1339
|
+
void Promise.resolve().then(()=>this.blobClient.sendBlobsToFilestore(encodedData.blobs).catch((_err)=>{
|
|
1340
|
+
this.log.error('Failed to send blobs to blob client');
|
|
1341
|
+
}));
|
|
871
1342
|
return this.addRequest({
|
|
872
1343
|
action: 'propose',
|
|
873
1344
|
request: {
|
|
874
1345
|
to: this.rollupContract.address,
|
|
875
1346
|
data: rollupData
|
|
876
1347
|
},
|
|
877
|
-
lastValidL2Slot:
|
|
1348
|
+
lastValidL2Slot: checkpoint.header.slotNumber,
|
|
878
1349
|
gasConfig: {
|
|
879
1350
|
...opts,
|
|
880
1351
|
gasLimit
|
|
@@ -902,25 +1373,23 @@ export class SequencerPublisher {
|
|
|
902
1373
|
calldataGas,
|
|
903
1374
|
calldataSize,
|
|
904
1375
|
sender,
|
|
905
|
-
...
|
|
1376
|
+
...checkpoint.getStats(),
|
|
906
1377
|
eventName: 'rollup-published-to-l1',
|
|
907
1378
|
blobCount: encodedData.blobs.length,
|
|
908
1379
|
inclusionBlocks
|
|
909
1380
|
};
|
|
910
|
-
this.log.info(`Published
|
|
1381
|
+
this.log.info(`Published checkpoint ${checkpoint.number} at slot ${slot} to rollup contract`, {
|
|
911
1382
|
...stats,
|
|
912
|
-
...
|
|
913
|
-
...receipt
|
|
1383
|
+
...checkpoint.getStats(),
|
|
1384
|
+
...pick(receipt, 'transactionHash', 'blockHash')
|
|
914
1385
|
});
|
|
915
1386
|
this.metrics.recordProcessBlockTx(timer.ms(), publishStats);
|
|
916
1387
|
return true;
|
|
917
1388
|
} else {
|
|
918
1389
|
this.metrics.recordFailedTx('process');
|
|
919
|
-
this.log.error(`
|
|
920
|
-
...
|
|
921
|
-
receipt
|
|
922
|
-
txHash: receipt.transactionHash,
|
|
923
|
-
slotNumber: block.header.globalVariables.slotNumber
|
|
1390
|
+
this.log.error(`Publishing checkpoint at slot ${slot} failed with ${errorMsg ?? 'no error message'}`, undefined, {
|
|
1391
|
+
...checkpoint.getStats(),
|
|
1392
|
+
...receipt
|
|
924
1393
|
});
|
|
925
1394
|
return false;
|
|
926
1395
|
}
|