@aztec/sequencer-client 0.0.1-commit.03f7ef2 → 0.0.1-commit.0658669b3
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 +15 -11
- package/dest/client/sequencer-client.d.ts.map +1 -1
- package/dest/client/sequencer-client.js +15 -4
- package/dest/config.d.ts +3 -4
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +22 -12
- package/dest/global_variable_builder/global_builder.d.ts +5 -7
- package/dest/global_variable_builder/global_builder.d.ts.map +1 -1
- package/dest/global_variable_builder/global_builder.js +13 -13
- 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 +35 -17
- package/dest/publisher/config.d.ts.map +1 -1
- package/dest/publisher/config.js +106 -42
- package/dest/publisher/index.d.ts +2 -1
- package/dest/publisher/index.d.ts.map +1 -1
- package/dest/publisher/l1_tx_failed_store/factory.d.ts +11 -0
- package/dest/publisher/l1_tx_failed_store/factory.d.ts.map +1 -0
- package/dest/publisher/l1_tx_failed_store/factory.js +22 -0
- package/dest/publisher/l1_tx_failed_store/failed_tx_store.d.ts +59 -0
- package/dest/publisher/l1_tx_failed_store/failed_tx_store.d.ts.map +1 -0
- package/dest/publisher/l1_tx_failed_store/failed_tx_store.js +1 -0
- package/dest/publisher/l1_tx_failed_store/file_store_failed_tx_store.d.ts +15 -0
- package/dest/publisher/l1_tx_failed_store/file_store_failed_tx_store.d.ts.map +1 -0
- package/dest/publisher/l1_tx_failed_store/file_store_failed_tx_store.js +34 -0
- package/dest/publisher/l1_tx_failed_store/index.d.ts +4 -0
- package/dest/publisher/l1_tx_failed_store/index.d.ts.map +1 -0
- package/dest/publisher/l1_tx_failed_store/index.js +2 -0
- package/dest/publisher/sequencer-publisher-factory.d.ts +12 -4
- package/dest/publisher/sequencer-publisher-factory.d.ts.map +1 -1
- package/dest/publisher/sequencer-publisher-factory.js +13 -2
- package/dest/publisher/sequencer-publisher-metrics.d.ts +1 -1
- 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 +40 -25
- package/dest/publisher/sequencer-publisher.d.ts.map +1 -1
- package/dest/publisher/sequencer-publisher.js +740 -100
- package/dest/sequencer/checkpoint_proposal_job.d.ts +40 -12
- package/dest/sequencer/checkpoint_proposal_job.d.ts.map +1 -1
- package/dest/sequencer/checkpoint_proposal_job.js +643 -64
- package/dest/sequencer/checkpoint_voter.d.ts +3 -2
- package/dest/sequencer/checkpoint_voter.d.ts.map +1 -1
- package/dest/sequencer/checkpoint_voter.js +34 -10
- package/dest/sequencer/index.d.ts +1 -3
- package/dest/sequencer/index.d.ts.map +1 -1
- package/dest/sequencer/index.js +0 -2
- package/dest/sequencer/metrics.d.ts +19 -7
- package/dest/sequencer/metrics.d.ts.map +1 -1
- package/dest/sequencer/metrics.js +131 -141
- package/dest/sequencer/sequencer.d.ts +38 -18
- package/dest/sequencer/sequencer.d.ts.map +1 -1
- package/dest/sequencer/sequencer.js +513 -66
- package/dest/sequencer/timetable.d.ts +1 -4
- package/dest/sequencer/timetable.d.ts.map +1 -1
- package/dest/sequencer/timetable.js +1 -4
- package/dest/test/index.d.ts +4 -7
- package/dest/test/index.d.ts.map +1 -1
- package/dest/test/mock_checkpoint_builder.d.ts +25 -11
- package/dest/test/mock_checkpoint_builder.d.ts.map +1 -1
- package/dest/test/mock_checkpoint_builder.js +52 -9
- package/dest/test/utils.d.ts +13 -9
- package/dest/test/utils.d.ts.map +1 -1
- package/dest/test/utils.js +27 -17
- package/package.json +30 -28
- package/src/client/sequencer-client.ts +29 -12
- package/src/config.ts +31 -19
- package/src/global_variable_builder/global_builder.ts +14 -14
- package/src/index.ts +1 -9
- package/src/publisher/config.ts +121 -43
- package/src/publisher/index.ts +3 -0
- package/src/publisher/l1_tx_failed_store/factory.ts +32 -0
- package/src/publisher/l1_tx_failed_store/failed_tx_store.ts +55 -0
- package/src/publisher/l1_tx_failed_store/file_store_failed_tx_store.ts +46 -0
- package/src/publisher/l1_tx_failed_store/index.ts +3 -0
- package/src/publisher/sequencer-publisher-factory.ts +24 -7
- package/src/publisher/sequencer-publisher-metrics.ts +17 -69
- package/src/publisher/sequencer-publisher.ts +361 -130
- package/src/sequencer/checkpoint_proposal_job.ts +316 -93
- package/src/sequencer/checkpoint_voter.ts +32 -7
- package/src/sequencer/index.ts +0 -2
- package/src/sequencer/metrics.ts +132 -148
- package/src/sequencer/sequencer.ts +159 -68
- package/src/sequencer/timetable.ts +6 -5
- package/src/test/index.ts +3 -6
- package/src/test/mock_checkpoint_builder.ts +102 -29
- package/src/test/utils.ts +58 -28
- package/dest/sequencer/block_builder.d.ts +0 -26
- package/dest/sequencer/block_builder.d.ts.map +0 -1
- package/dest/sequencer/block_builder.js +0 -129
- package/dest/sequencer/checkpoint_builder.d.ts +0 -63
- package/dest/sequencer/checkpoint_builder.d.ts.map +0 -1
- package/dest/sequencer/checkpoint_builder.js +0 -131
- 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 -217
- package/src/sequencer/checkpoint_builder.ts +0 -217
- package/src/tx_validator/nullifier_cache.ts +0 -30
- package/src/tx_validator/tx_validator_factory.ts +0 -133
|
@@ -63,42 +63,431 @@ function _ts_dispose_resources(env) {
|
|
|
63
63
|
return next();
|
|
64
64
|
})(env);
|
|
65
65
|
}
|
|
66
|
+
function applyDecs2203RFactory() {
|
|
67
|
+
function createAddInitializerMethod(initializers, decoratorFinishedRef) {
|
|
68
|
+
return function addInitializer(initializer) {
|
|
69
|
+
assertNotFinished(decoratorFinishedRef, "addInitializer");
|
|
70
|
+
assertCallable(initializer, "An initializer");
|
|
71
|
+
initializers.push(initializer);
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
function memberDec(dec, name, desc, initializers, kind, isStatic, isPrivate, metadata, value) {
|
|
75
|
+
var kindStr;
|
|
76
|
+
switch(kind){
|
|
77
|
+
case 1:
|
|
78
|
+
kindStr = "accessor";
|
|
79
|
+
break;
|
|
80
|
+
case 2:
|
|
81
|
+
kindStr = "method";
|
|
82
|
+
break;
|
|
83
|
+
case 3:
|
|
84
|
+
kindStr = "getter";
|
|
85
|
+
break;
|
|
86
|
+
case 4:
|
|
87
|
+
kindStr = "setter";
|
|
88
|
+
break;
|
|
89
|
+
default:
|
|
90
|
+
kindStr = "field";
|
|
91
|
+
}
|
|
92
|
+
var ctx = {
|
|
93
|
+
kind: kindStr,
|
|
94
|
+
name: isPrivate ? "#" + name : name,
|
|
95
|
+
static: isStatic,
|
|
96
|
+
private: isPrivate,
|
|
97
|
+
metadata: metadata
|
|
98
|
+
};
|
|
99
|
+
var decoratorFinishedRef = {
|
|
100
|
+
v: false
|
|
101
|
+
};
|
|
102
|
+
ctx.addInitializer = createAddInitializerMethod(initializers, decoratorFinishedRef);
|
|
103
|
+
var get, set;
|
|
104
|
+
if (kind === 0) {
|
|
105
|
+
if (isPrivate) {
|
|
106
|
+
get = desc.get;
|
|
107
|
+
set = desc.set;
|
|
108
|
+
} else {
|
|
109
|
+
get = function() {
|
|
110
|
+
return this[name];
|
|
111
|
+
};
|
|
112
|
+
set = function(v) {
|
|
113
|
+
this[name] = v;
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
} else if (kind === 2) {
|
|
117
|
+
get = function() {
|
|
118
|
+
return desc.value;
|
|
119
|
+
};
|
|
120
|
+
} else {
|
|
121
|
+
if (kind === 1 || kind === 3) {
|
|
122
|
+
get = function() {
|
|
123
|
+
return desc.get.call(this);
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
if (kind === 1 || kind === 4) {
|
|
127
|
+
set = function(v) {
|
|
128
|
+
desc.set.call(this, v);
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
ctx.access = get && set ? {
|
|
133
|
+
get: get,
|
|
134
|
+
set: set
|
|
135
|
+
} : get ? {
|
|
136
|
+
get: get
|
|
137
|
+
} : {
|
|
138
|
+
set: set
|
|
139
|
+
};
|
|
140
|
+
try {
|
|
141
|
+
return dec(value, ctx);
|
|
142
|
+
} finally{
|
|
143
|
+
decoratorFinishedRef.v = true;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
function assertNotFinished(decoratorFinishedRef, fnName) {
|
|
147
|
+
if (decoratorFinishedRef.v) {
|
|
148
|
+
throw new Error("attempted to call " + fnName + " after decoration was finished");
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
function assertCallable(fn, hint) {
|
|
152
|
+
if (typeof fn !== "function") {
|
|
153
|
+
throw new TypeError(hint + " must be a function");
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
function assertValidReturnValue(kind, value) {
|
|
157
|
+
var type = typeof value;
|
|
158
|
+
if (kind === 1) {
|
|
159
|
+
if (type !== "object" || value === null) {
|
|
160
|
+
throw new TypeError("accessor decorators must return an object with get, set, or init properties or void 0");
|
|
161
|
+
}
|
|
162
|
+
if (value.get !== undefined) {
|
|
163
|
+
assertCallable(value.get, "accessor.get");
|
|
164
|
+
}
|
|
165
|
+
if (value.set !== undefined) {
|
|
166
|
+
assertCallable(value.set, "accessor.set");
|
|
167
|
+
}
|
|
168
|
+
if (value.init !== undefined) {
|
|
169
|
+
assertCallable(value.init, "accessor.init");
|
|
170
|
+
}
|
|
171
|
+
} else if (type !== "function") {
|
|
172
|
+
var hint;
|
|
173
|
+
if (kind === 0) {
|
|
174
|
+
hint = "field";
|
|
175
|
+
} else if (kind === 10) {
|
|
176
|
+
hint = "class";
|
|
177
|
+
} else {
|
|
178
|
+
hint = "method";
|
|
179
|
+
}
|
|
180
|
+
throw new TypeError(hint + " decorators must return a function or void 0");
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
function applyMemberDec(ret, base, decInfo, name, kind, isStatic, isPrivate, initializers, metadata) {
|
|
184
|
+
var decs = decInfo[0];
|
|
185
|
+
var desc, init, value;
|
|
186
|
+
if (isPrivate) {
|
|
187
|
+
if (kind === 0 || kind === 1) {
|
|
188
|
+
desc = {
|
|
189
|
+
get: decInfo[3],
|
|
190
|
+
set: decInfo[4]
|
|
191
|
+
};
|
|
192
|
+
} else if (kind === 3) {
|
|
193
|
+
desc = {
|
|
194
|
+
get: decInfo[3]
|
|
195
|
+
};
|
|
196
|
+
} else if (kind === 4) {
|
|
197
|
+
desc = {
|
|
198
|
+
set: decInfo[3]
|
|
199
|
+
};
|
|
200
|
+
} else {
|
|
201
|
+
desc = {
|
|
202
|
+
value: decInfo[3]
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
} else if (kind !== 0) {
|
|
206
|
+
desc = Object.getOwnPropertyDescriptor(base, name);
|
|
207
|
+
}
|
|
208
|
+
if (kind === 1) {
|
|
209
|
+
value = {
|
|
210
|
+
get: desc.get,
|
|
211
|
+
set: desc.set
|
|
212
|
+
};
|
|
213
|
+
} else if (kind === 2) {
|
|
214
|
+
value = desc.value;
|
|
215
|
+
} else if (kind === 3) {
|
|
216
|
+
value = desc.get;
|
|
217
|
+
} else if (kind === 4) {
|
|
218
|
+
value = desc.set;
|
|
219
|
+
}
|
|
220
|
+
var newValue, get, set;
|
|
221
|
+
if (typeof decs === "function") {
|
|
222
|
+
newValue = memberDec(decs, name, desc, initializers, kind, isStatic, isPrivate, metadata, value);
|
|
223
|
+
if (newValue !== void 0) {
|
|
224
|
+
assertValidReturnValue(kind, newValue);
|
|
225
|
+
if (kind === 0) {
|
|
226
|
+
init = newValue;
|
|
227
|
+
} else if (kind === 1) {
|
|
228
|
+
init = newValue.init;
|
|
229
|
+
get = newValue.get || value.get;
|
|
230
|
+
set = newValue.set || value.set;
|
|
231
|
+
value = {
|
|
232
|
+
get: get,
|
|
233
|
+
set: set
|
|
234
|
+
};
|
|
235
|
+
} else {
|
|
236
|
+
value = newValue;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
} else {
|
|
240
|
+
for(var i = decs.length - 1; i >= 0; i--){
|
|
241
|
+
var dec = decs[i];
|
|
242
|
+
newValue = memberDec(dec, name, desc, initializers, kind, isStatic, isPrivate, metadata, value);
|
|
243
|
+
if (newValue !== void 0) {
|
|
244
|
+
assertValidReturnValue(kind, newValue);
|
|
245
|
+
var newInit;
|
|
246
|
+
if (kind === 0) {
|
|
247
|
+
newInit = newValue;
|
|
248
|
+
} else if (kind === 1) {
|
|
249
|
+
newInit = newValue.init;
|
|
250
|
+
get = newValue.get || value.get;
|
|
251
|
+
set = newValue.set || value.set;
|
|
252
|
+
value = {
|
|
253
|
+
get: get,
|
|
254
|
+
set: set
|
|
255
|
+
};
|
|
256
|
+
} else {
|
|
257
|
+
value = newValue;
|
|
258
|
+
}
|
|
259
|
+
if (newInit !== void 0) {
|
|
260
|
+
if (init === void 0) {
|
|
261
|
+
init = newInit;
|
|
262
|
+
} else if (typeof init === "function") {
|
|
263
|
+
init = [
|
|
264
|
+
init,
|
|
265
|
+
newInit
|
|
266
|
+
];
|
|
267
|
+
} else {
|
|
268
|
+
init.push(newInit);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
if (kind === 0 || kind === 1) {
|
|
275
|
+
if (init === void 0) {
|
|
276
|
+
init = function(instance, init) {
|
|
277
|
+
return init;
|
|
278
|
+
};
|
|
279
|
+
} else if (typeof init !== "function") {
|
|
280
|
+
var ownInitializers = init;
|
|
281
|
+
init = function(instance, init) {
|
|
282
|
+
var value = init;
|
|
283
|
+
for(var i = 0; i < ownInitializers.length; i++){
|
|
284
|
+
value = ownInitializers[i].call(instance, value);
|
|
285
|
+
}
|
|
286
|
+
return value;
|
|
287
|
+
};
|
|
288
|
+
} else {
|
|
289
|
+
var originalInitializer = init;
|
|
290
|
+
init = function(instance, init) {
|
|
291
|
+
return originalInitializer.call(instance, init);
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
ret.push(init);
|
|
295
|
+
}
|
|
296
|
+
if (kind !== 0) {
|
|
297
|
+
if (kind === 1) {
|
|
298
|
+
desc.get = value.get;
|
|
299
|
+
desc.set = value.set;
|
|
300
|
+
} else if (kind === 2) {
|
|
301
|
+
desc.value = value;
|
|
302
|
+
} else if (kind === 3) {
|
|
303
|
+
desc.get = value;
|
|
304
|
+
} else if (kind === 4) {
|
|
305
|
+
desc.set = value;
|
|
306
|
+
}
|
|
307
|
+
if (isPrivate) {
|
|
308
|
+
if (kind === 1) {
|
|
309
|
+
ret.push(function(instance, args) {
|
|
310
|
+
return value.get.call(instance, args);
|
|
311
|
+
});
|
|
312
|
+
ret.push(function(instance, args) {
|
|
313
|
+
return value.set.call(instance, args);
|
|
314
|
+
});
|
|
315
|
+
} else if (kind === 2) {
|
|
316
|
+
ret.push(value);
|
|
317
|
+
} else {
|
|
318
|
+
ret.push(function(instance, args) {
|
|
319
|
+
return value.call(instance, args);
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
} else {
|
|
323
|
+
Object.defineProperty(base, name, desc);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
function applyMemberDecs(Class, decInfos, metadata) {
|
|
328
|
+
var ret = [];
|
|
329
|
+
var protoInitializers;
|
|
330
|
+
var staticInitializers;
|
|
331
|
+
var existingProtoNonFields = new Map();
|
|
332
|
+
var existingStaticNonFields = new Map();
|
|
333
|
+
for(var i = 0; i < decInfos.length; i++){
|
|
334
|
+
var decInfo = decInfos[i];
|
|
335
|
+
if (!Array.isArray(decInfo)) continue;
|
|
336
|
+
var kind = decInfo[1];
|
|
337
|
+
var name = decInfo[2];
|
|
338
|
+
var isPrivate = decInfo.length > 3;
|
|
339
|
+
var isStatic = kind >= 5;
|
|
340
|
+
var base;
|
|
341
|
+
var initializers;
|
|
342
|
+
if (isStatic) {
|
|
343
|
+
base = Class;
|
|
344
|
+
kind = kind - 5;
|
|
345
|
+
staticInitializers = staticInitializers || [];
|
|
346
|
+
initializers = staticInitializers;
|
|
347
|
+
} else {
|
|
348
|
+
base = Class.prototype;
|
|
349
|
+
protoInitializers = protoInitializers || [];
|
|
350
|
+
initializers = protoInitializers;
|
|
351
|
+
}
|
|
352
|
+
if (kind !== 0 && !isPrivate) {
|
|
353
|
+
var existingNonFields = isStatic ? existingStaticNonFields : existingProtoNonFields;
|
|
354
|
+
var existingKind = existingNonFields.get(name) || 0;
|
|
355
|
+
if (existingKind === true || existingKind === 3 && kind !== 4 || existingKind === 4 && kind !== 3) {
|
|
356
|
+
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);
|
|
357
|
+
} else if (!existingKind && kind > 2) {
|
|
358
|
+
existingNonFields.set(name, kind);
|
|
359
|
+
} else {
|
|
360
|
+
existingNonFields.set(name, true);
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
applyMemberDec(ret, base, decInfo, name, kind, isStatic, isPrivate, initializers, metadata);
|
|
364
|
+
}
|
|
365
|
+
pushInitializers(ret, protoInitializers);
|
|
366
|
+
pushInitializers(ret, staticInitializers);
|
|
367
|
+
return ret;
|
|
368
|
+
}
|
|
369
|
+
function pushInitializers(ret, initializers) {
|
|
370
|
+
if (initializers) {
|
|
371
|
+
ret.push(function(instance) {
|
|
372
|
+
for(var i = 0; i < initializers.length; i++){
|
|
373
|
+
initializers[i].call(instance);
|
|
374
|
+
}
|
|
375
|
+
return instance;
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
function applyClassDecs(targetClass, classDecs, metadata) {
|
|
380
|
+
if (classDecs.length > 0) {
|
|
381
|
+
var initializers = [];
|
|
382
|
+
var newClass = targetClass;
|
|
383
|
+
var name = targetClass.name;
|
|
384
|
+
for(var i = classDecs.length - 1; i >= 0; i--){
|
|
385
|
+
var decoratorFinishedRef = {
|
|
386
|
+
v: false
|
|
387
|
+
};
|
|
388
|
+
try {
|
|
389
|
+
var nextNewClass = classDecs[i](newClass, {
|
|
390
|
+
kind: "class",
|
|
391
|
+
name: name,
|
|
392
|
+
addInitializer: createAddInitializerMethod(initializers, decoratorFinishedRef),
|
|
393
|
+
metadata
|
|
394
|
+
});
|
|
395
|
+
} finally{
|
|
396
|
+
decoratorFinishedRef.v = true;
|
|
397
|
+
}
|
|
398
|
+
if (nextNewClass !== undefined) {
|
|
399
|
+
assertValidReturnValue(10, nextNewClass);
|
|
400
|
+
newClass = nextNewClass;
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
return [
|
|
404
|
+
defineMetadata(newClass, metadata),
|
|
405
|
+
function() {
|
|
406
|
+
for(var i = 0; i < initializers.length; i++){
|
|
407
|
+
initializers[i].call(newClass);
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
];
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
function defineMetadata(Class, metadata) {
|
|
414
|
+
return Object.defineProperty(Class, Symbol.metadata || Symbol.for("Symbol.metadata"), {
|
|
415
|
+
configurable: true,
|
|
416
|
+
enumerable: true,
|
|
417
|
+
value: metadata
|
|
418
|
+
});
|
|
419
|
+
}
|
|
420
|
+
return function applyDecs2203R(targetClass, memberDecs, classDecs, parentClass) {
|
|
421
|
+
if (parentClass !== void 0) {
|
|
422
|
+
var parentMetadata = parentClass[Symbol.metadata || Symbol.for("Symbol.metadata")];
|
|
423
|
+
}
|
|
424
|
+
var metadata = Object.create(parentMetadata === void 0 ? null : parentMetadata);
|
|
425
|
+
var e = applyMemberDecs(targetClass, memberDecs, metadata);
|
|
426
|
+
if (!classDecs.length) defineMetadata(targetClass, metadata);
|
|
427
|
+
return {
|
|
428
|
+
e: e,
|
|
429
|
+
get c () {
|
|
430
|
+
return applyClassDecs(targetClass, classDecs, metadata);
|
|
431
|
+
}
|
|
432
|
+
};
|
|
433
|
+
};
|
|
434
|
+
}
|
|
435
|
+
function _apply_decs_2203_r(targetClass, memberDecs, classDecs, parentClass) {
|
|
436
|
+
return (_apply_decs_2203_r = applyDecs2203RFactory())(targetClass, memberDecs, classDecs, parentClass);
|
|
437
|
+
}
|
|
438
|
+
var _dec, _dec1, _dec2, _dec3, _dec4, _dec5, _dec6, _dec7, _initProto;
|
|
439
|
+
import { NUM_CHECKPOINT_END_MARKER_FIELDS, getNumBlockEndBlobFields } from '@aztec/blob-lib/encoding';
|
|
66
440
|
import { BLOBS_PER_CHECKPOINT, FIELDS_PER_BLOB } from '@aztec/constants';
|
|
67
|
-
import { BlockNumber } from '@aztec/foundation/branded-types';
|
|
441
|
+
import { BlockNumber, IndexWithinCheckpoint } from '@aztec/foundation/branded-types';
|
|
68
442
|
import { randomInt } from '@aztec/foundation/crypto/random';
|
|
69
443
|
import { Signature } from '@aztec/foundation/eth-signature';
|
|
70
444
|
import { filter } from '@aztec/foundation/iterator';
|
|
445
|
+
import { createLogger } from '@aztec/foundation/log';
|
|
71
446
|
import { sleep, sleepUntil } from '@aztec/foundation/sleep';
|
|
72
447
|
import { Timer } from '@aztec/foundation/timer';
|
|
73
|
-
import { unfreeze } from '@aztec/foundation/types';
|
|
448
|
+
import { isErrorClass, unfreeze } from '@aztec/foundation/types';
|
|
74
449
|
import { CommitteeAttestationsAndSigners, MaliciousCommitteeAttestationsAndSigners } from '@aztec/stdlib/block';
|
|
75
450
|
import { getSlotStartBuildTimestamp } from '@aztec/stdlib/epoch-helpers';
|
|
76
451
|
import { Gas } from '@aztec/stdlib/gas';
|
|
77
|
-
import {
|
|
452
|
+
import { NoValidTxsError } from '@aztec/stdlib/interfaces/server';
|
|
453
|
+
import { computeInHashFromL1ToL2Messages } from '@aztec/stdlib/messaging';
|
|
454
|
+
import { orderAttestations, trimAttestations } from '@aztec/stdlib/p2p';
|
|
78
455
|
import { AttestationTimeoutError } from '@aztec/stdlib/validators';
|
|
456
|
+
import { Attributes, trackSpan } from '@aztec/telemetry-client';
|
|
457
|
+
import { DutyAlreadySignedError, SlashingProtectionError } from '@aztec/validator-ha-signer/errors';
|
|
79
458
|
import { CheckpointVoter } from './checkpoint_voter.js';
|
|
80
459
|
import { SequencerInterruptedError } from './errors.js';
|
|
81
460
|
import { SequencerState } from './utils.js';
|
|
82
461
|
/** How much time to sleep while waiting for min transactions to accumulate for a block */ const TXS_POLLING_MS = 500;
|
|
462
|
+
_dec = trackSpan('CheckpointProposalJob.execute'), _dec1 = trackSpan('CheckpointProposalJob.proposeCheckpoint', function() {
|
|
463
|
+
return {
|
|
464
|
+
// nullish operator needed for tests
|
|
465
|
+
[Attributes.COINBASE]: this.validatorClient.getCoinbaseForAttestor(this.attestorAddress)?.toString(),
|
|
466
|
+
[Attributes.SLOT_NUMBER]: this.slot
|
|
467
|
+
};
|
|
468
|
+
}), _dec2 = trackSpan('CheckpointProposalJob.buildBlocksForCheckpoint'), _dec3 = trackSpan('CheckpointProposalJob.waitUntilNextSubslot'), _dec4 = trackSpan('CheckpointProposalJob.buildSingleBlock'), _dec5 = trackSpan('CheckpointProposalJob.waitForMinTxs'), _dec6 = trackSpan('CheckpointProposalJob.waitForAttestations'), _dec7 = trackSpan('CheckpointProposalJob.waitUntilTimeInSlot');
|
|
83
469
|
/**
|
|
84
470
|
* Handles the execution of a checkpoint proposal after the initial preparation phase.
|
|
85
471
|
* This includes building blocks, collecting attestations, and publishing the checkpoint to L1,
|
|
86
472
|
* as well as enqueueing votes for slashing and governance proposals. This class is created from
|
|
87
473
|
* the Sequencer once the check for being the proposer for the slot has succeeded.
|
|
88
474
|
*/ export class CheckpointProposalJob {
|
|
475
|
+
epoch;
|
|
89
476
|
slot;
|
|
90
477
|
checkpointNumber;
|
|
91
478
|
syncedToBlockNumber;
|
|
92
479
|
proposer;
|
|
93
480
|
publisher;
|
|
94
481
|
attestorAddress;
|
|
95
|
-
|
|
482
|
+
invalidateCheckpoint;
|
|
96
483
|
validatorClient;
|
|
97
484
|
globalsBuilder;
|
|
98
485
|
p2pClient;
|
|
99
486
|
worldState;
|
|
100
487
|
l1ToL2MessageSource;
|
|
488
|
+
l2BlockSource;
|
|
101
489
|
checkpointsBuilder;
|
|
490
|
+
blockSink;
|
|
102
491
|
l1Constants;
|
|
103
492
|
config;
|
|
104
493
|
timetable;
|
|
@@ -108,22 +497,70 @@ import { SequencerState } from './utils.js';
|
|
|
108
497
|
metrics;
|
|
109
498
|
eventEmitter;
|
|
110
499
|
setStateFn;
|
|
500
|
+
tracer;
|
|
501
|
+
static{
|
|
502
|
+
({ e: [_initProto] } = _apply_decs_2203_r(this, [
|
|
503
|
+
[
|
|
504
|
+
_dec,
|
|
505
|
+
2,
|
|
506
|
+
"execute"
|
|
507
|
+
],
|
|
508
|
+
[
|
|
509
|
+
_dec1,
|
|
510
|
+
2,
|
|
511
|
+
"proposeCheckpoint"
|
|
512
|
+
],
|
|
513
|
+
[
|
|
514
|
+
_dec2,
|
|
515
|
+
2,
|
|
516
|
+
"buildBlocksForCheckpoint"
|
|
517
|
+
],
|
|
518
|
+
[
|
|
519
|
+
_dec3,
|
|
520
|
+
2,
|
|
521
|
+
"waitUntilNextSubslot"
|
|
522
|
+
],
|
|
523
|
+
[
|
|
524
|
+
_dec4,
|
|
525
|
+
2,
|
|
526
|
+
"buildSingleBlock"
|
|
527
|
+
],
|
|
528
|
+
[
|
|
529
|
+
_dec5,
|
|
530
|
+
2,
|
|
531
|
+
"waitForMinTxs"
|
|
532
|
+
],
|
|
533
|
+
[
|
|
534
|
+
_dec6,
|
|
535
|
+
2,
|
|
536
|
+
"waitForAttestations"
|
|
537
|
+
],
|
|
538
|
+
[
|
|
539
|
+
_dec7,
|
|
540
|
+
2,
|
|
541
|
+
"waitUntilTimeInSlot"
|
|
542
|
+
]
|
|
543
|
+
], []));
|
|
544
|
+
}
|
|
111
545
|
log;
|
|
112
|
-
constructor(slot, checkpointNumber, syncedToBlockNumber, // TODO(palla/mbps): Can we remove the proposer in favor of attestorAddress? Need to check fisherman-node flows.
|
|
113
|
-
proposer, publisher, attestorAddress,
|
|
546
|
+
constructor(epoch, slot, checkpointNumber, syncedToBlockNumber, // TODO(palla/mbps): Can we remove the proposer in favor of attestorAddress? Need to check fisherman-node flows.
|
|
547
|
+
proposer, publisher, attestorAddress, invalidateCheckpoint, validatorClient, globalsBuilder, p2pClient, worldState, l1ToL2MessageSource, l2BlockSource, checkpointsBuilder, blockSink, l1Constants, config, timetable, slasherClient, epochCache, dateProvider, metrics, eventEmitter, setStateFn, tracer, bindings){
|
|
548
|
+
this.epoch = epoch;
|
|
114
549
|
this.slot = slot;
|
|
115
550
|
this.checkpointNumber = checkpointNumber;
|
|
116
551
|
this.syncedToBlockNumber = syncedToBlockNumber;
|
|
117
552
|
this.proposer = proposer;
|
|
118
553
|
this.publisher = publisher;
|
|
119
554
|
this.attestorAddress = attestorAddress;
|
|
120
|
-
this.
|
|
555
|
+
this.invalidateCheckpoint = invalidateCheckpoint;
|
|
121
556
|
this.validatorClient = validatorClient;
|
|
122
557
|
this.globalsBuilder = globalsBuilder;
|
|
123
558
|
this.p2pClient = p2pClient;
|
|
124
559
|
this.worldState = worldState;
|
|
125
560
|
this.l1ToL2MessageSource = l1ToL2MessageSource;
|
|
561
|
+
this.l2BlockSource = l2BlockSource;
|
|
126
562
|
this.checkpointsBuilder = checkpointsBuilder;
|
|
563
|
+
this.blockSink = blockSink;
|
|
127
564
|
this.l1Constants = l1Constants;
|
|
128
565
|
this.config = config;
|
|
129
566
|
this.timetable = timetable;
|
|
@@ -133,7 +570,12 @@ import { SequencerState } from './utils.js';
|
|
|
133
570
|
this.metrics = metrics;
|
|
134
571
|
this.eventEmitter = eventEmitter;
|
|
135
572
|
this.setStateFn = setStateFn;
|
|
136
|
-
this.
|
|
573
|
+
this.tracer = tracer;
|
|
574
|
+
_initProto(this);
|
|
575
|
+
this.log = createLogger('sequencer:checkpoint-proposal', {
|
|
576
|
+
...bindings,
|
|
577
|
+
instanceId: `slot-${slot}`
|
|
578
|
+
});
|
|
137
579
|
}
|
|
138
580
|
/**
|
|
139
581
|
* Executes the checkpoint proposal job.
|
|
@@ -147,6 +589,9 @@ import { SequencerState } from './utils.js';
|
|
|
147
589
|
const checkpoint = await this.proposeCheckpoint();
|
|
148
590
|
// Wait until the voting promises have resolved, so all requests are enqueued (not sent)
|
|
149
591
|
await Promise.all(votesPromises);
|
|
592
|
+
if (checkpoint) {
|
|
593
|
+
this.metrics.recordCheckpointProposalSuccess();
|
|
594
|
+
}
|
|
150
595
|
// Do not post anything to L1 if we are fishermen, but do perform L1 fee analysis
|
|
151
596
|
if (this.config.fishermanMode) {
|
|
152
597
|
await this.handleCheckpointEndAsFisherman(checkpoint);
|
|
@@ -185,26 +630,50 @@ import { SequencerState } from './utils.js';
|
|
|
185
630
|
// Start the checkpoint
|
|
186
631
|
this.setStateFn(SequencerState.INITIALIZING_CHECKPOINT, this.slot);
|
|
187
632
|
this.metrics.incOpenSlot(this.slot, this.proposer?.toString() ?? 'unknown');
|
|
188
|
-
// Enqueues
|
|
189
|
-
if (this.
|
|
190
|
-
this.publisher.
|
|
633
|
+
// Enqueues checkpoint invalidation (constant for the whole slot)
|
|
634
|
+
if (this.invalidateCheckpoint && !this.config.skipInvalidateBlockAsProposer) {
|
|
635
|
+
this.publisher.enqueueInvalidateCheckpoint(this.invalidateCheckpoint);
|
|
191
636
|
}
|
|
192
637
|
// Create checkpoint builder for the slot
|
|
193
638
|
const checkpointGlobalVariables = await this.globalsBuilder.buildCheckpointGlobalVariables(coinbase, feeRecipient, this.slot);
|
|
194
|
-
// Collect L1 to L2 messages for the checkpoint
|
|
639
|
+
// Collect L1 to L2 messages for the checkpoint and compute their hash
|
|
195
640
|
const l1ToL2Messages = await this.l1ToL2MessageSource.getL1ToL2Messages(this.checkpointNumber);
|
|
641
|
+
const inHash = computeInHashFromL1ToL2Messages(l1ToL2Messages);
|
|
642
|
+
// Collect the out hashes of all the checkpoints before this one in the same epoch
|
|
643
|
+
const previousCheckpointOutHashes = (await this.l2BlockSource.getCheckpointsDataForEpoch(this.epoch)).filter((c)=>c.checkpointNumber < this.checkpointNumber).map((c)=>c.checkpointOutHash);
|
|
644
|
+
// Get the fee asset price modifier from the oracle
|
|
645
|
+
const feeAssetPriceModifier = await this.publisher.getFeeAssetPriceModifier();
|
|
196
646
|
const fork = _ts_add_disposable_resource(env, await this.worldState.fork(this.syncedToBlockNumber, {
|
|
197
647
|
closeDelayMs: 12_000
|
|
198
|
-
}),
|
|
648
|
+
}), true);
|
|
199
649
|
// Create checkpoint builder for the entire slot
|
|
200
|
-
const checkpointBuilder = await this.checkpointsBuilder.startCheckpoint(this.checkpointNumber, checkpointGlobalVariables, l1ToL2Messages, fork);
|
|
650
|
+
const checkpointBuilder = await this.checkpointsBuilder.startCheckpoint(this.checkpointNumber, checkpointGlobalVariables, feeAssetPriceModifier, l1ToL2Messages, previousCheckpointOutHashes, fork, this.log.getBindings());
|
|
201
651
|
// Options for the validator client when creating block and checkpoint proposals
|
|
202
652
|
const blockProposalOptions = {
|
|
203
653
|
publishFullTxs: !!this.config.publishTxsWithProposals,
|
|
204
654
|
broadcastInvalidBlockProposal: this.config.broadcastInvalidBlockProposal
|
|
205
655
|
};
|
|
206
|
-
|
|
207
|
-
|
|
656
|
+
const checkpointProposalOptions = {
|
|
657
|
+
publishFullTxs: !!this.config.publishTxsWithProposals,
|
|
658
|
+
broadcastInvalidCheckpointProposal: this.config.broadcastInvalidBlockProposal
|
|
659
|
+
};
|
|
660
|
+
let blocksInCheckpoint = [];
|
|
661
|
+
let blockPendingBroadcast = undefined;
|
|
662
|
+
const checkpointBuildTimer = new Timer();
|
|
663
|
+
try {
|
|
664
|
+
// Main loop: build blocks for the checkpoint
|
|
665
|
+
const result = await this.buildBlocksForCheckpoint(checkpointBuilder, checkpointGlobalVariables.timestamp, inHash, blockProposalOptions);
|
|
666
|
+
blocksInCheckpoint = result.blocksInCheckpoint;
|
|
667
|
+
blockPendingBroadcast = result.blockPendingBroadcast;
|
|
668
|
+
} catch (err) {
|
|
669
|
+
// These errors are expected in HA mode, so we yield and let another HA node handle the slot
|
|
670
|
+
// The only distinction between the 2 errors is SlashingProtectionError throws when the payload is different,
|
|
671
|
+
// which is normal for block building (may have picked different txs)
|
|
672
|
+
if (this.handleHASigningError(err, 'Block proposal')) {
|
|
673
|
+
return undefined;
|
|
674
|
+
}
|
|
675
|
+
throw err;
|
|
676
|
+
}
|
|
208
677
|
if (blocksInCheckpoint.length === 0) {
|
|
209
678
|
this.log.warn(`No blocks were built for slot ${this.slot}`, {
|
|
210
679
|
slot: this.slot
|
|
@@ -214,10 +683,21 @@ import { SequencerState } from './utils.js';
|
|
|
214
683
|
});
|
|
215
684
|
return undefined;
|
|
216
685
|
}
|
|
686
|
+
const minBlocksForCheckpoint = this.config.minBlocksForCheckpoint;
|
|
687
|
+
if (minBlocksForCheckpoint !== undefined && blocksInCheckpoint.length < minBlocksForCheckpoint) {
|
|
688
|
+
this.log.warn(`Checkpoint has fewer blocks than minimum (${blocksInCheckpoint.length} < ${minBlocksForCheckpoint}), skipping proposal`, {
|
|
689
|
+
slot: this.slot,
|
|
690
|
+
blocksBuilt: blocksInCheckpoint.length,
|
|
691
|
+
minBlocksForCheckpoint
|
|
692
|
+
});
|
|
693
|
+
return undefined;
|
|
694
|
+
}
|
|
217
695
|
// Assemble and broadcast the checkpoint proposal, including the last block that was not
|
|
218
696
|
// broadcasted yet, and wait to collect the committee attestations.
|
|
219
697
|
this.setStateFn(SequencerState.ASSEMBLING_CHECKPOINT, this.slot);
|
|
220
698
|
const checkpoint = await checkpointBuilder.completeCheckpoint();
|
|
699
|
+
// Record checkpoint-level build metrics
|
|
700
|
+
this.metrics.recordCheckpointBuild(checkpointBuildTimer.ms(), blocksInCheckpoint.length, checkpoint.getStats().txCount, Number(checkpoint.header.totalManaUsed.toBigInt()));
|
|
221
701
|
// Do not collect attestations nor publish to L1 in fisherman mode
|
|
222
702
|
if (this.config.fishermanMode) {
|
|
223
703
|
this.log.info(`Built checkpoint for slot ${this.slot} with ${blocksInCheckpoint.length} blocks. ` + `Skipping proposal in fisherman mode.`, {
|
|
@@ -228,49 +708,80 @@ import { SequencerState } from './utils.js';
|
|
|
228
708
|
this.metrics.recordCheckpointSuccess();
|
|
229
709
|
return checkpoint;
|
|
230
710
|
}
|
|
231
|
-
//
|
|
232
|
-
const
|
|
711
|
+
// Include the block pending broadcast in the checkpoint proposal if any
|
|
712
|
+
const lastBlock = blockPendingBroadcast && {
|
|
713
|
+
blockHeader: blockPendingBroadcast.block.header,
|
|
714
|
+
indexWithinCheckpoint: blockPendingBroadcast.block.indexWithinCheckpoint,
|
|
715
|
+
txs: blockPendingBroadcast.txs
|
|
716
|
+
};
|
|
717
|
+
// Create the checkpoint proposal and broadcast it
|
|
718
|
+
const proposal = await this.validatorClient.createCheckpointProposal(checkpoint.header, checkpoint.archive.root, feeAssetPriceModifier, lastBlock, this.proposer, checkpointProposalOptions);
|
|
233
719
|
const blockProposedAt = this.dateProvider.now();
|
|
234
|
-
await this.p2pClient.
|
|
720
|
+
await this.p2pClient.broadcastCheckpointProposal(proposal);
|
|
235
721
|
this.setStateFn(SequencerState.COLLECTING_ATTESTATIONS, this.slot);
|
|
236
722
|
const attestations = await this.waitForAttestations(proposal);
|
|
237
723
|
const blockAttestedAt = this.dateProvider.now();
|
|
238
|
-
this.metrics.
|
|
724
|
+
this.metrics.recordCheckpointAttestationDelay(blockAttestedAt - blockProposedAt);
|
|
239
725
|
// Proposer must sign over the attestations before pushing them to L1
|
|
240
726
|
const signer = this.proposer ?? this.publisher.getSenderAddress();
|
|
241
|
-
|
|
727
|
+
let attestationsSignature;
|
|
728
|
+
try {
|
|
729
|
+
attestationsSignature = await this.validatorClient.signAttestationsAndSigners(attestations, signer, this.slot, this.checkpointNumber);
|
|
730
|
+
} catch (err) {
|
|
731
|
+
// We shouldn't really get here since we yield to another HA node
|
|
732
|
+
// as soon as we see these errors when creating block or checkpoint proposals.
|
|
733
|
+
if (this.handleHASigningError(err, 'Attestations signature')) {
|
|
734
|
+
return undefined;
|
|
735
|
+
}
|
|
736
|
+
throw err;
|
|
737
|
+
}
|
|
242
738
|
// Enqueue publishing the checkpoint to L1
|
|
243
739
|
this.setStateFn(SequencerState.PUBLISHING_CHECKPOINT, this.slot);
|
|
244
740
|
const aztecSlotDuration = this.l1Constants.slotDuration;
|
|
245
741
|
const slotStartBuildTimestamp = this.getSlotStartBuildTimestamp();
|
|
246
742
|
const txTimeoutAt = new Date((slotStartBuildTimestamp + aztecSlotDuration) * 1000);
|
|
743
|
+
// If we have been configured to potentially skip publishing checkpoint then roll the dice here
|
|
744
|
+
if (this.config.skipPublishingCheckpointsPercent !== undefined && this.config.skipPublishingCheckpointsPercent > 0) {
|
|
745
|
+
const result = Math.max(0, randomInt(100));
|
|
746
|
+
if (result < this.config.skipPublishingCheckpointsPercent) {
|
|
747
|
+
this.log.warn(`Skipping publishing proposal for checkpoint ${checkpoint.number}. Configured percentage: ${this.config.skipPublishingCheckpointsPercent}, generated value: ${result}`);
|
|
748
|
+
return checkpoint;
|
|
749
|
+
}
|
|
750
|
+
}
|
|
247
751
|
await this.publisher.enqueueProposeCheckpoint(checkpoint, attestations, attestationsSignature, {
|
|
248
752
|
txTimeoutAt,
|
|
249
|
-
|
|
753
|
+
forcePendingCheckpointNumber: this.invalidateCheckpoint?.forcePendingCheckpointNumber
|
|
250
754
|
});
|
|
251
755
|
return checkpoint;
|
|
252
756
|
} catch (e) {
|
|
253
757
|
env.error = e;
|
|
254
758
|
env.hasError = true;
|
|
255
759
|
} finally{
|
|
256
|
-
_ts_dispose_resources(env);
|
|
760
|
+
const result = _ts_dispose_resources(env);
|
|
761
|
+
if (result) await result;
|
|
257
762
|
}
|
|
258
763
|
} catch (err) {
|
|
764
|
+
if (err && (err instanceof DutyAlreadySignedError || err instanceof SlashingProtectionError)) {
|
|
765
|
+
// swallow this error. It's already been logged by a function deeper in the stack
|
|
766
|
+
return undefined;
|
|
767
|
+
}
|
|
259
768
|
this.log.error(`Error building checkpoint at slot ${this.slot}`, err);
|
|
260
769
|
return undefined;
|
|
261
770
|
}
|
|
262
771
|
}
|
|
263
772
|
/**
|
|
264
773
|
* Builds blocks for a checkpoint within the current slot.
|
|
265
|
-
*/ async buildBlocksForCheckpoint(checkpointBuilder, timestamp, blockProposalOptions) {
|
|
774
|
+
*/ async buildBlocksForCheckpoint(checkpointBuilder, timestamp, inHash, blockProposalOptions) {
|
|
266
775
|
const blocksInCheckpoint = [];
|
|
267
776
|
const txHashesAlreadyIncluded = new Set();
|
|
268
777
|
const initialBlockNumber = BlockNumber(this.syncedToBlockNumber + 1);
|
|
778
|
+
// Remaining blob fields available for blocks (checkpoint end marker already subtracted)
|
|
779
|
+
let remainingBlobFields = BLOBS_PER_CHECKPOINT * FIELDS_PER_BLOB - NUM_CHECKPOINT_END_MARKER_FIELDS;
|
|
269
780
|
// Last block in the checkpoint will usually be flagged as pending broadcast, so we send it along with the checkpoint proposal
|
|
270
|
-
let
|
|
781
|
+
let blockPendingBroadcast = undefined;
|
|
271
782
|
while(true){
|
|
272
783
|
const blocksBuilt = blocksInCheckpoint.length;
|
|
273
|
-
const indexWithinCheckpoint = blocksBuilt;
|
|
784
|
+
const indexWithinCheckpoint = IndexWithinCheckpoint(blocksBuilt);
|
|
274
785
|
const blockNumber = BlockNumber(initialBlockNumber + blocksBuilt);
|
|
275
786
|
const secondsIntoSlot = this.getSecondsIntoSlot();
|
|
276
787
|
const timingInfo = this.timetable.canStartNextBlock(secondsIntoSlot);
|
|
@@ -291,8 +802,10 @@ import { SequencerState } from './utils.js';
|
|
|
291
802
|
buildDeadline: timingInfo.deadline ? new Date((this.getSlotStartBuildTimestamp() + timingInfo.deadline) * 1000) : undefined,
|
|
292
803
|
blockNumber,
|
|
293
804
|
indexWithinCheckpoint,
|
|
294
|
-
txHashesAlreadyIncluded
|
|
805
|
+
txHashesAlreadyIncluded,
|
|
806
|
+
remainingBlobFields
|
|
295
807
|
});
|
|
808
|
+
// TODO(palla/mbps): Review these conditions. We may want to keep trying in some scenarios.
|
|
296
809
|
if (!buildResult && timingInfo.isLastBlock) {
|
|
297
810
|
break;
|
|
298
811
|
} else if (!buildResult && timingInfo.deadline !== undefined) {
|
|
@@ -312,12 +825,21 @@ import { SequencerState } from './utils.js';
|
|
|
312
825
|
}
|
|
313
826
|
break;
|
|
314
827
|
}
|
|
315
|
-
const { block, usedTxs } = buildResult;
|
|
828
|
+
const { block, usedTxs, remainingBlobFields: newRemainingBlobFields } = buildResult;
|
|
316
829
|
blocksInCheckpoint.push(block);
|
|
830
|
+
// Update remaining blob fields for the next block
|
|
831
|
+
remainingBlobFields = newRemainingBlobFields;
|
|
317
832
|
// Sync the proposed block to the archiver to make it available
|
|
318
833
|
// Note that the checkpoint builder uses its own fork so it should not need to wait for this syncing
|
|
319
834
|
// Eventually we should refactor the checkpoint builder to not need a separate long-lived fork
|
|
320
|
-
|
|
835
|
+
// Fire and forget - don't block the critical path, but log errors
|
|
836
|
+
this.syncProposedBlockToArchiver(block).catch((err)=>{
|
|
837
|
+
this.log.error(`Failed to sync proposed block ${block.number} to archiver`, {
|
|
838
|
+
blockNumber: block.number,
|
|
839
|
+
err
|
|
840
|
+
});
|
|
841
|
+
});
|
|
842
|
+
usedTxs.forEach((tx)=>txHashesAlreadyIncluded.add(tx.txHash.toString()));
|
|
321
843
|
// If this is the last block, exit the loop now so we start collecting attestations
|
|
322
844
|
if (timingInfo.isLastBlock) {
|
|
323
845
|
this.log.verbose(`Completed final block ${blockNumber} for slot ${this.slot}`, {
|
|
@@ -325,7 +847,7 @@ import { SequencerState } from './utils.js';
|
|
|
325
847
|
blockNumber,
|
|
326
848
|
blocksBuilt
|
|
327
849
|
});
|
|
328
|
-
|
|
850
|
+
blockPendingBroadcast = {
|
|
329
851
|
block,
|
|
330
852
|
txs: usedTxs
|
|
331
853
|
};
|
|
@@ -334,8 +856,7 @@ import { SequencerState } from './utils.js';
|
|
|
334
856
|
// For non-last blocks, broadcast the block proposal (unless we're in fisherman mode)
|
|
335
857
|
// If the block is the last one, we'll broadcast it along with the checkpoint at the end of the loop
|
|
336
858
|
if (!this.config.fishermanMode) {
|
|
337
|
-
|
|
338
|
-
const proposal = await this.validatorClient.createBlockProposal(block.header.globalVariables.blockNumber, (await checkpointBuilder.getCheckpoint()).header, block.archive.root, usedTxs, this.proposer, blockProposalOptions);
|
|
859
|
+
const proposal = await this.validatorClient.createBlockProposal(block.header, block.indexWithinCheckpoint, inHash, block.archive.root, usedTxs, this.proposer, blockProposalOptions);
|
|
339
860
|
await this.p2pClient.broadcastProposal(proposal);
|
|
340
861
|
}
|
|
341
862
|
// Wait until the next block's start time
|
|
@@ -347,7 +868,7 @@ import { SequencerState } from './utils.js';
|
|
|
347
868
|
});
|
|
348
869
|
return {
|
|
349
870
|
blocksInCheckpoint,
|
|
350
|
-
|
|
871
|
+
blockPendingBroadcast
|
|
351
872
|
};
|
|
352
873
|
}
|
|
353
874
|
/** Sleeps until it is time to produce the next block in the slot */ async waitUntilNextSubslot(nextSubslotStart) {
|
|
@@ -358,7 +879,7 @@ import { SequencerState } from './utils.js';
|
|
|
358
879
|
await this.waitUntilTimeInSlot(nextSubslotStart);
|
|
359
880
|
}
|
|
360
881
|
/** Builds a single block. Called from the main block building loop. */ async buildSingleBlock(checkpointBuilder, opts) {
|
|
361
|
-
const { blockTimestamp, forceCreate, blockNumber, indexWithinCheckpoint, buildDeadline, txHashesAlreadyIncluded } = opts;
|
|
882
|
+
const { blockTimestamp, forceCreate, blockNumber, indexWithinCheckpoint, buildDeadline, txHashesAlreadyIncluded, remainingBlobFields } = opts;
|
|
362
883
|
this.log.verbose(`Preparing block ${blockNumber} index ${indexWithinCheckpoint} at checkpoint ${this.checkpointNumber} for slot ${this.slot}`, {
|
|
363
884
|
...checkpointBuilder.getConstantData(),
|
|
364
885
|
...opts
|
|
@@ -383,50 +904,53 @@ import { SequencerState } from './utils.js';
|
|
|
383
904
|
}
|
|
384
905
|
// Create iterator to pending txs. We filter out txs already included in previous blocks in the checkpoint
|
|
385
906
|
// just in case p2p failed to sync the provisional block and didn't get to remove those txs from the mempool yet.
|
|
386
|
-
const pendingTxs = filter(this.p2pClient.
|
|
907
|
+
const pendingTxs = filter(this.p2pClient.iterateEligiblePendingTxs(), (tx)=>!txHashesAlreadyIncluded.has(tx.txHash.toString()));
|
|
387
908
|
this.log.debug(`Building block ${blockNumber} at index ${indexWithinCheckpoint} for slot ${this.slot} with ${availableTxs} available txs`, {
|
|
388
909
|
slot: this.slot,
|
|
389
910
|
blockNumber,
|
|
390
911
|
indexWithinCheckpoint
|
|
391
912
|
});
|
|
392
913
|
this.setStateFn(SequencerState.CREATING_BLOCK, this.slot);
|
|
914
|
+
// Calculate blob fields limit for txs (remaining capacity - this block's end overhead)
|
|
915
|
+
const blockEndOverhead = getNumBlockEndBlobFields(indexWithinCheckpoint === 0);
|
|
916
|
+
const maxBlobFieldsForTxs = remainingBlobFields - blockEndOverhead;
|
|
393
917
|
const blockBuilderOptions = {
|
|
394
918
|
maxTransactions: this.config.maxTxsPerBlock,
|
|
395
919
|
maxBlockSize: this.config.maxBlockSizeInBytes,
|
|
396
920
|
maxBlockGas: new Gas(this.config.maxDABlockGas, this.config.maxL2BlockGas),
|
|
397
|
-
maxBlobFields:
|
|
921
|
+
maxBlobFields: maxBlobFieldsForTxs,
|
|
398
922
|
deadline: buildDeadline
|
|
399
923
|
};
|
|
400
924
|
// Actually build the block by executing txs
|
|
401
|
-
const
|
|
402
|
-
const { publicGas, block, publicProcessorDuration, numTxs, blockBuildingTimer, usedTxs, failedTxs } = await checkpointBuilder.buildBlock(pendingTxs, blockNumber, blockTimestamp, blockBuilderOptions);
|
|
403
|
-
const blockBuildDuration = workTimer.ms();
|
|
925
|
+
const buildResult = await this.buildSingleBlockWithCheckpointBuilder(checkpointBuilder, pendingTxs, blockNumber, blockTimestamp, blockBuilderOptions);
|
|
404
926
|
// If any txs failed during execution, drop them from the mempool so we don't pick them up again
|
|
405
|
-
await this.dropFailedTxsFromP2P(failedTxs);
|
|
927
|
+
await this.dropFailedTxsFromP2P(buildResult.failedTxs);
|
|
406
928
|
// Check if we have created a block with enough txs. If there were invalid txs in the pool, or if execution took
|
|
407
929
|
// too long, then we may not get to minTxsPerBlock after executing public functions.
|
|
408
930
|
const minValidTxs = this.config.minValidTxsPerBlock ?? minTxs;
|
|
409
|
-
|
|
410
|
-
|
|
931
|
+
const numTxs = buildResult.status === 'no-valid-txs' ? 0 : buildResult.numTxs;
|
|
932
|
+
if (buildResult.status === 'no-valid-txs' || !forceCreate && numTxs < minValidTxs) {
|
|
933
|
+
this.log.warn(`Block ${blockNumber} at index ${indexWithinCheckpoint} on slot ${this.slot} has too few valid txs to be proposed`, {
|
|
411
934
|
slot: this.slot,
|
|
412
935
|
blockNumber,
|
|
413
936
|
numTxs,
|
|
414
|
-
indexWithinCheckpoint
|
|
937
|
+
indexWithinCheckpoint,
|
|
938
|
+
minValidTxs,
|
|
939
|
+
buildResult: buildResult.status
|
|
415
940
|
});
|
|
416
|
-
this.eventEmitter.emit('block-
|
|
417
|
-
|
|
418
|
-
availableTxs: numTxs,
|
|
941
|
+
this.eventEmitter.emit('block-build-failed', {
|
|
942
|
+
reason: `Insufficient valid txs`,
|
|
419
943
|
slot: this.slot
|
|
420
944
|
});
|
|
421
945
|
this.metrics.recordBlockProposalFailed('insufficient_valid_txs');
|
|
422
946
|
return undefined;
|
|
423
947
|
}
|
|
424
948
|
// Block creation succeeded, emit stats and metrics
|
|
949
|
+
const { publicGas, block, publicProcessorDuration, usedTxs, usedTxBlobFields, blockBuildDuration } = buildResult;
|
|
425
950
|
const blockStats = {
|
|
426
951
|
eventName: 'l2-block-built',
|
|
427
952
|
duration: blockBuildDuration,
|
|
428
953
|
publicProcessDuration: publicProcessorDuration,
|
|
429
|
-
rollupCircuitsDuration: blockBuildingTimer.ms(),
|
|
430
954
|
...block.getStats()
|
|
431
955
|
};
|
|
432
956
|
const blockHash = await block.hash();
|
|
@@ -445,7 +969,8 @@ import { SequencerState } from './utils.js';
|
|
|
445
969
|
this.metrics.recordBuiltBlock(blockBuildDuration, publicGas.l2Gas);
|
|
446
970
|
return {
|
|
447
971
|
block,
|
|
448
|
-
usedTxs
|
|
972
|
+
usedTxs,
|
|
973
|
+
remainingBlobFields: maxBlobFieldsForTxs - usedTxBlobFields
|
|
449
974
|
};
|
|
450
975
|
} catch (err) {
|
|
451
976
|
this.eventEmitter.emit('block-build-failed', {
|
|
@@ -463,9 +988,30 @@ import { SequencerState } from './utils.js';
|
|
|
463
988
|
};
|
|
464
989
|
}
|
|
465
990
|
}
|
|
991
|
+
/** Uses the checkpoint builder to build a block, catching specific txs */ async buildSingleBlockWithCheckpointBuilder(checkpointBuilder, pendingTxs, blockNumber, blockTimestamp, blockBuilderOptions) {
|
|
992
|
+
try {
|
|
993
|
+
const workTimer = new Timer();
|
|
994
|
+
const result = await checkpointBuilder.buildBlock(pendingTxs, blockNumber, blockTimestamp, blockBuilderOptions);
|
|
995
|
+
const blockBuildDuration = workTimer.ms();
|
|
996
|
+
return {
|
|
997
|
+
...result,
|
|
998
|
+
blockBuildDuration,
|
|
999
|
+
status: 'success'
|
|
1000
|
+
};
|
|
1001
|
+
} catch (err) {
|
|
1002
|
+
if (isErrorClass(err, NoValidTxsError)) {
|
|
1003
|
+
return {
|
|
1004
|
+
failedTxs: err.failedTxs,
|
|
1005
|
+
status: 'no-valid-txs'
|
|
1006
|
+
};
|
|
1007
|
+
}
|
|
1008
|
+
throw err;
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
466
1011
|
/** Waits until minTxs are available on the pool for building a block. */ async waitForMinTxs(opts) {
|
|
467
|
-
const minTxs = this.config.minTxsPerBlock;
|
|
468
1012
|
const { indexWithinCheckpoint, blockNumber, buildDeadline, forceCreate } = opts;
|
|
1013
|
+
// We only allow a block with 0 txs in the first block of the checkpoint
|
|
1014
|
+
const minTxs = indexWithinCheckpoint > 0 && this.config.minTxsPerBlock === 0 ? 1 : this.config.minTxsPerBlock;
|
|
469
1015
|
// Deadline is undefined if we are not enforcing the timetable, meaning we'll exit immediately when out of time
|
|
470
1016
|
const startBuildingDeadline = buildDeadline ? new Date(buildDeadline.getTime() - this.timetable.minExecutionTime * 1000) : undefined;
|
|
471
1017
|
let availableTxs = await this.p2pClient.getPendingTxCount();
|
|
@@ -485,7 +1031,7 @@ import { SequencerState } from './utils.js';
|
|
|
485
1031
|
slot: this.slot,
|
|
486
1032
|
indexWithinCheckpoint
|
|
487
1033
|
});
|
|
488
|
-
await
|
|
1034
|
+
await this.waitForTxsPollingInterval();
|
|
489
1035
|
availableTxs = await this.p2pClient.getPendingTxCount();
|
|
490
1036
|
}
|
|
491
1037
|
return {
|
|
@@ -520,19 +1066,24 @@ import { SequencerState } from './utils.js';
|
|
|
520
1066
|
return new CommitteeAttestationsAndSigners(orderAttestations(attestations ?? [], committee));
|
|
521
1067
|
}
|
|
522
1068
|
const attestationTimeAllowed = this.config.enforceTimeTable ? this.timetable.getMaxAllowedTime(SequencerState.PUBLISHING_CHECKPOINT) : this.l1Constants.slotDuration;
|
|
523
|
-
const attestationDeadline = new Date(this.
|
|
1069
|
+
const attestationDeadline = new Date((this.getSlotStartBuildTimestamp() + attestationTimeAllowed) * 1000);
|
|
524
1070
|
this.metrics.recordRequiredAttestations(numberOfRequiredAttestations, attestationTimeAllowed);
|
|
525
1071
|
const collectAttestationsTimer = new Timer();
|
|
526
1072
|
let collectedAttestationsCount = 0;
|
|
527
1073
|
try {
|
|
528
1074
|
const attestations = await this.validatorClient.collectAttestations(proposal, numberOfRequiredAttestations, attestationDeadline);
|
|
529
1075
|
collectedAttestationsCount = attestations.length;
|
|
1076
|
+
// Trim attestations to minimum required to save L1 calldata gas
|
|
1077
|
+
const localAddresses = this.validatorClient.getValidatorAddresses();
|
|
1078
|
+
const trimmed = trimAttestations(attestations, numberOfRequiredAttestations, this.attestorAddress, localAddresses);
|
|
1079
|
+
if (trimmed.length < attestations.length) {
|
|
1080
|
+
this.log.debug(`Trimmed attestations from ${attestations.length} to ${trimmed.length} for L1 submission`);
|
|
1081
|
+
}
|
|
530
1082
|
// Rollup contract requires that the signatures are provided in the order of the committee
|
|
531
|
-
const sorted = orderAttestations(
|
|
1083
|
+
const sorted = orderAttestations(trimmed, committee);
|
|
532
1084
|
// Manipulate the attestations if we've been configured to do so
|
|
533
1085
|
if (this.config.injectFakeAttestation || this.config.shuffleAttestationOrdering) {
|
|
534
|
-
|
|
535
|
-
return this.manipulateAttestations(checkpoint, epoch, seed, committee, sorted);
|
|
1086
|
+
return this.manipulateAttestations(proposal.slotNumber, epoch, seed, committee, sorted);
|
|
536
1087
|
}
|
|
537
1088
|
return new CommitteeAttestationsAndSigners(sorted);
|
|
538
1089
|
} catch (err) {
|
|
@@ -544,10 +1095,9 @@ import { SequencerState } from './utils.js';
|
|
|
544
1095
|
this.metrics.recordCollectedAttestations(collectedAttestationsCount, collectAttestationsTimer.ms());
|
|
545
1096
|
}
|
|
546
1097
|
}
|
|
547
|
-
/** Breaks the attestations before publishing based on attack configs */ manipulateAttestations(
|
|
1098
|
+
/** Breaks the attestations before publishing based on attack configs */ manipulateAttestations(slotNumber, epoch, seed, committee, attestations) {
|
|
548
1099
|
// Compute the proposer index in the committee, since we dont want to tweak it.
|
|
549
1100
|
// Otherwise, the L1 rollup contract will reject the block outright.
|
|
550
|
-
const { slotNumber } = checkpoint;
|
|
551
1101
|
const proposerIndex = Number(this.epochCache.computeProposerIndex(slotNumber, epoch, seed, BigInt(committee.length)));
|
|
552
1102
|
if (this.config.injectFakeAttestation) {
|
|
553
1103
|
// Find non-empty attestations that are not from the proposer
|
|
@@ -589,18 +1139,25 @@ import { SequencerState } from './utils.js';
|
|
|
589
1139
|
const failedTxData = failedTxs.map((fail)=>fail.tx);
|
|
590
1140
|
const failedTxHashes = failedTxData.map((tx)=>tx.getTxHash());
|
|
591
1141
|
this.log.verbose(`Dropping failed txs ${failedTxHashes.join(', ')}`);
|
|
592
|
-
await this.p2pClient.
|
|
1142
|
+
await this.p2pClient.handleFailedExecution(failedTxHashes);
|
|
593
1143
|
}
|
|
594
1144
|
/**
|
|
595
|
-
*
|
|
596
|
-
*
|
|
1145
|
+
* Adds the proposed block to the archiver so it's available via P2P.
|
|
1146
|
+
* Gossip doesn't echo messages back to the sender, so the proposer's archiver/world-state
|
|
1147
|
+
* would never receive its own block without this explicit sync.
|
|
597
1148
|
*/ async syncProposedBlockToArchiver(block) {
|
|
598
|
-
this.
|
|
1149
|
+
if (this.config.skipPushProposedBlocksToArchiver !== false) {
|
|
1150
|
+
this.log.warn(`Skipping push of proposed block ${block.number} to archiver`, {
|
|
1151
|
+
blockNumber: block.number,
|
|
1152
|
+
slot: block.header.globalVariables.slotNumber
|
|
1153
|
+
});
|
|
1154
|
+
return;
|
|
1155
|
+
}
|
|
1156
|
+
this.log.debug(`Syncing proposed block ${block.number} to archiver`, {
|
|
599
1157
|
blockNumber: block.number,
|
|
600
1158
|
slot: block.header.globalVariables.slotNumber
|
|
601
1159
|
});
|
|
602
|
-
|
|
603
|
-
await Promise.resolve();
|
|
1160
|
+
await this.blockSink.addBlock(block);
|
|
604
1161
|
}
|
|
605
1162
|
/** Runs fee analysis and logs checkpoint outcome as fisherman */ async handleCheckpointEndAsFisherman(checkpoint) {
|
|
606
1163
|
// Perform L1 fee analysis before clearing requests
|
|
@@ -612,21 +1169,43 @@ import { SequencerState } from './utils.js';
|
|
|
612
1169
|
...checkpoint.getStats(),
|
|
613
1170
|
feeAnalysisId: feeAnalysis?.id
|
|
614
1171
|
});
|
|
615
|
-
this.metrics.recordBlockProposalSuccess();
|
|
616
1172
|
} else {
|
|
617
1173
|
this.log.warn(`Validation block building FAILED for slot ${this.slot}`, {
|
|
618
1174
|
slot: this.slot,
|
|
619
1175
|
feeAnalysisId: feeAnalysis?.id
|
|
620
1176
|
});
|
|
621
|
-
this.metrics.
|
|
1177
|
+
this.metrics.recordCheckpointProposalFailed('block_build_failed');
|
|
622
1178
|
}
|
|
623
1179
|
this.publisher.clearPendingRequests();
|
|
624
1180
|
}
|
|
1181
|
+
/**
|
|
1182
|
+
* Helper to handle HA double-signing errors. Returns true if the error was handled (caller should yield).
|
|
1183
|
+
*/ handleHASigningError(err, errorContext) {
|
|
1184
|
+
if (err instanceof DutyAlreadySignedError) {
|
|
1185
|
+
this.log.info(`${errorContext} for slot ${this.slot} already signed by another HA node, yielding`, {
|
|
1186
|
+
slot: this.slot,
|
|
1187
|
+
signedByNode: err.signedByNode
|
|
1188
|
+
});
|
|
1189
|
+
return true;
|
|
1190
|
+
}
|
|
1191
|
+
if (err instanceof SlashingProtectionError) {
|
|
1192
|
+
this.log.info(`${errorContext} for slot ${this.slot} blocked by slashing protection, yielding`, {
|
|
1193
|
+
slot: this.slot,
|
|
1194
|
+
existingMessageHash: err.existingMessageHash,
|
|
1195
|
+
attemptedMessageHash: err.attemptedMessageHash
|
|
1196
|
+
});
|
|
1197
|
+
return true;
|
|
1198
|
+
}
|
|
1199
|
+
return false;
|
|
1200
|
+
}
|
|
625
1201
|
/** Waits until a specific time within the current slot */ async waitUntilTimeInSlot(targetSecondsIntoSlot) {
|
|
626
1202
|
const slotStartTimestamp = this.getSlotStartBuildTimestamp();
|
|
627
1203
|
const targetTimestamp = slotStartTimestamp + targetSecondsIntoSlot;
|
|
628
1204
|
await sleepUntil(new Date(targetTimestamp * 1000), this.dateProvider.nowAsDate());
|
|
629
1205
|
}
|
|
1206
|
+
/** Waits the polling interval for transactions. Extracted for test overriding. */ async waitForTxsPollingInterval() {
|
|
1207
|
+
await sleep(TXS_POLLING_MS);
|
|
1208
|
+
}
|
|
630
1209
|
getSlotStartBuildTimestamp() {
|
|
631
1210
|
return getSlotStartBuildTimestamp(this.slot, this.l1Constants);
|
|
632
1211
|
}
|