@typeberry/jam 0.1.0-3c30204 → 0.1.0-eb00e84
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/bandersnatch/6b655f8772c01b768329.js +1 -0
- package/bandersnatch/ccf8ada94096a8f232f5.js +1 -0
- package/bandersnatch/e2fdc1b646378dd96eda.js +1 -0
- package/bandersnatch/index.js +3037 -0
- package/bandersnatch/index.js.map +1 -0
- package/bandersnatch/package.json +3 -0
- package/bandersnatch/sourcemap-register.cjs +1 -0
- package/block-generator/index.js +0 -2
- package/block-generator/index.js.map +1 -1
- package/bootstrap-bandersnatch.mjs +162 -0
- package/bootstrap-bandersnatch.mjs.map +1 -0
- package/importer/bootstrap-bandersnatch.mjs.map +1 -0
- package/importer/index.js +460 -65
- package/importer/index.js.map +1 -1
- package/index.js +457 -65
- package/index.js.map +1 -1
- package/jam-network/index.js +0 -2
- package/jam-network/index.js.map +1 -1
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -31862,7 +31862,6 @@ async function verifyBatch(input) {
|
|
|
31862
31862
|
|
|
31863
31863
|
;// CONCATENATED MODULE: ./packages/core/hash/hash.ts
|
|
31864
31864
|
|
|
31865
|
-
|
|
31866
31865
|
/**
|
|
31867
31866
|
* Size of the output of the hash functions.
|
|
31868
31867
|
*
|
|
@@ -31872,7 +31871,6 @@ async function verifyBatch(input) {
|
|
|
31872
31871
|
const hash_HASH_SIZE = 32;
|
|
31873
31872
|
/** A hash without last byte (useful for trie representation). */
|
|
31874
31873
|
const TRUNCATED_HASH_SIZE = 31;
|
|
31875
|
-
const ZERO_HASH = bytes_Bytes.zero(hash_HASH_SIZE);
|
|
31876
31874
|
/**
|
|
31877
31875
|
* Container for some object with a hash that is related to this object.
|
|
31878
31876
|
*
|
|
@@ -33591,7 +33589,7 @@ const common_tryAsServiceGas = (v) => opaque_asOpaqueType(numbers_tryAsU64(v));
|
|
|
33591
33589
|
/** Attempt to convert a number into `CoreIndex`. */
|
|
33592
33590
|
const common_tryAsCoreIndex = (v) => opaque_asOpaqueType(numbers_tryAsU16(v));
|
|
33593
33591
|
/** Attempt to convert a number into `Epoch`. */
|
|
33594
|
-
const tryAsEpoch = (v) =>
|
|
33592
|
+
const tryAsEpoch = (v) => opaque_asOpaqueType(numbers_tryAsU32(v));
|
|
33595
33593
|
function tryAsPerValidator(array, spec) {
|
|
33596
33594
|
debug_check(array.length === spec.validatorsCount, `Invalid per-validator array length. Expected ${spec.validatorsCount}, got: ${array.length}`);
|
|
33597
33595
|
return sized_array_asKnownSize(array);
|
|
@@ -40179,6 +40177,7 @@ class ImporterReady extends State {
|
|
|
40179
40177
|
response: rootHash === null ? bytes_Bytes.zero(hash_HASH_SIZE).raw : rootHash.raw,
|
|
40180
40178
|
};
|
|
40181
40179
|
}
|
|
40180
|
+
// NOTE [ToDr] This should rather be using the import queue, instead of going directly.
|
|
40182
40181
|
async importBlock(block) {
|
|
40183
40182
|
if (this.importer === null) {
|
|
40184
40183
|
state_machine_logger.error(`${this.constructor.name} importer not initialized yet!`);
|
|
@@ -40190,13 +40189,17 @@ class ImporterReady extends State {
|
|
|
40190
40189
|
if (block instanceof Uint8Array) {
|
|
40191
40190
|
const config = this.getConfig();
|
|
40192
40191
|
const blockView = decoder_Decoder.decodeObject(Block.Codec.View, block, config.chainSpec);
|
|
40192
|
+
const headerView = blockView.header.view();
|
|
40193
|
+
const timeSlot = headerView.timeSlotIndex.materialize();
|
|
40193
40194
|
let response;
|
|
40194
40195
|
try {
|
|
40195
|
-
const res = await this.importer.importBlock(blockView, config.omitSealVerification);
|
|
40196
|
+
const res = await this.importer.importBlock(blockView, null, config.omitSealVerification);
|
|
40196
40197
|
if (res.isOk) {
|
|
40197
|
-
|
|
40198
|
+
state_machine_logger.info(`🧊 Best block: #${timeSlot} (${res.ok.hash})`);
|
|
40199
|
+
response = result_Result.ok(this.importer.getBestStateRootHash() ?? bytes_Bytes.zero(hash_HASH_SIZE).asOpaque());
|
|
40198
40200
|
}
|
|
40199
40201
|
else {
|
|
40202
|
+
state_machine_logger.log(`❌ Rejected block #${timeSlot}: ${resultToString(res)}`);
|
|
40200
40203
|
response = result_Result.error(resultToString(res));
|
|
40201
40204
|
}
|
|
40202
40205
|
}
|
|
@@ -57574,6 +57577,88 @@ const initNetwork = async (importerReady, workerConfig, genesisHeaderHash, netwo
|
|
|
57574
57577
|
;// CONCATENATED MODULE: external "node:fs/promises"
|
|
57575
57578
|
const external_node_fs_promises_namespaceObject = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:fs/promises");
|
|
57576
57579
|
var external_node_fs_promises_default = /*#__PURE__*/__nccwpck_require__.n(external_node_fs_promises_namespaceObject);
|
|
57580
|
+
;// CONCATENATED MODULE: ./workers/importer/import-queue.ts
|
|
57581
|
+
|
|
57582
|
+
|
|
57583
|
+
|
|
57584
|
+
|
|
57585
|
+
|
|
57586
|
+
class ImportQueue {
|
|
57587
|
+
spec;
|
|
57588
|
+
importer;
|
|
57589
|
+
toImport = SortedArray.fromSortedArray((a, b) => {
|
|
57590
|
+
const diff = a.timeSlot - b.timeSlot;
|
|
57591
|
+
if (diff < 0) {
|
|
57592
|
+
return Ordering.Greater;
|
|
57593
|
+
}
|
|
57594
|
+
if (diff > 0) {
|
|
57595
|
+
return Ordering.Less;
|
|
57596
|
+
}
|
|
57597
|
+
return Ordering.Equal;
|
|
57598
|
+
});
|
|
57599
|
+
queuedBlocks = HashSet.new();
|
|
57600
|
+
lastEpoch = tryAsEpoch(2 ** 32 - 1);
|
|
57601
|
+
constructor(spec, importer) {
|
|
57602
|
+
this.spec = spec;
|
|
57603
|
+
this.importer = importer;
|
|
57604
|
+
}
|
|
57605
|
+
isCurrentEpoch(timeSlot) {
|
|
57606
|
+
const epoch = Math.floor(timeSlot / this.spec.epochLength);
|
|
57607
|
+
return this.lastEpoch === epoch;
|
|
57608
|
+
}
|
|
57609
|
+
startPreverification() {
|
|
57610
|
+
for (const entry of this.toImport) {
|
|
57611
|
+
if (this.isCurrentEpoch(entry.timeSlot)) {
|
|
57612
|
+
entry.seal = this.importer.preverifySeal(entry.timeSlot, entry.block);
|
|
57613
|
+
}
|
|
57614
|
+
}
|
|
57615
|
+
}
|
|
57616
|
+
static getBlockDetails(block) {
|
|
57617
|
+
let encodedHeader;
|
|
57618
|
+
let timeSlot;
|
|
57619
|
+
try {
|
|
57620
|
+
encodedHeader = block.header.encoded();
|
|
57621
|
+
timeSlot = block.header.view().timeSlotIndex.materialize();
|
|
57622
|
+
}
|
|
57623
|
+
catch {
|
|
57624
|
+
return result_Result.error("invalid");
|
|
57625
|
+
}
|
|
57626
|
+
const headerHash = hashBytes(encodedHeader).asOpaque();
|
|
57627
|
+
return result_Result.ok(new WithHash(headerHash, { block, timeSlot }));
|
|
57628
|
+
}
|
|
57629
|
+
push(details) {
|
|
57630
|
+
const headerHash = details.hash;
|
|
57631
|
+
if (this.queuedBlocks.has(headerHash)) {
|
|
57632
|
+
return result_Result.error("already queued");
|
|
57633
|
+
}
|
|
57634
|
+
const { timeSlot, block } = details.data;
|
|
57635
|
+
const entry = {
|
|
57636
|
+
headerHash,
|
|
57637
|
+
timeSlot,
|
|
57638
|
+
block,
|
|
57639
|
+
seal: this.isCurrentEpoch(timeSlot) ? this.importer.preverifySeal(timeSlot, block) : Promise.resolve(null),
|
|
57640
|
+
};
|
|
57641
|
+
this.toImport.insert(entry);
|
|
57642
|
+
this.queuedBlocks.insert(headerHash);
|
|
57643
|
+
return result_Result.ok(result_OK);
|
|
57644
|
+
}
|
|
57645
|
+
shift() {
|
|
57646
|
+
const entry = this.toImport.pop();
|
|
57647
|
+
if (entry !== undefined) {
|
|
57648
|
+
this.queuedBlocks.delete(entry.headerHash);
|
|
57649
|
+
const blockEpoch = Math.floor(entry.timeSlot / this.spec.epochLength);
|
|
57650
|
+
const hasEpochChanged = this.lastEpoch !== blockEpoch;
|
|
57651
|
+
this.lastEpoch = tryAsEpoch(blockEpoch);
|
|
57652
|
+
// currently removed block is changing the epoch, so fire up
|
|
57653
|
+
// preverifcation for the following blocks.
|
|
57654
|
+
if (hasEpochChanged) {
|
|
57655
|
+
this.startPreverification();
|
|
57656
|
+
}
|
|
57657
|
+
}
|
|
57658
|
+
return entry;
|
|
57659
|
+
}
|
|
57660
|
+
}
|
|
57661
|
+
|
|
57577
57662
|
;// CONCATENATED MODULE: ./packages/jam/transition/block-verifier.ts
|
|
57578
57663
|
|
|
57579
57664
|
|
|
@@ -57587,7 +57672,7 @@ var BlockVerifierError;
|
|
|
57587
57672
|
BlockVerifierError[BlockVerifierError["InvalidStateRoot"] = 4] = "InvalidStateRoot";
|
|
57588
57673
|
BlockVerifierError[BlockVerifierError["AlreadyImported"] = 5] = "AlreadyImported";
|
|
57589
57674
|
})(BlockVerifierError || (BlockVerifierError = {}));
|
|
57590
|
-
const
|
|
57675
|
+
const ZERO_HASH = bytes_Bytes.zero(hash_HASH_SIZE).asOpaque();
|
|
57591
57676
|
class BlockVerifier {
|
|
57592
57677
|
hasher;
|
|
57593
57678
|
blocks;
|
|
@@ -57607,7 +57692,7 @@ class BlockVerifier {
|
|
|
57607
57692
|
// https://graypaper.fluffylabs.dev/#/cc517d7/0c9d000c9d00?v=0.6.5
|
|
57608
57693
|
const parentHash = headerView.parentHeaderHash.materialize();
|
|
57609
57694
|
// importing genesis block
|
|
57610
|
-
if (!parentHash.isEqualTo(
|
|
57695
|
+
if (!parentHash.isEqualTo(ZERO_HASH)) {
|
|
57611
57696
|
const parentBlock = this.blocks.getHeader(parentHash);
|
|
57612
57697
|
if (parentBlock === null) {
|
|
57613
57698
|
return result_Result.error(BlockVerifierError.ParentNotFound, `Parent ${parentHash.toString()} not found`);
|
|
@@ -58120,22 +58205,301 @@ async function verifyTickets(bandersnatch, numberOfValidators, epochRoot, ticket
|
|
|
58120
58205
|
}));
|
|
58121
58206
|
}
|
|
58122
58207
|
|
|
58123
|
-
;// CONCATENATED MODULE: ./packages/
|
|
58208
|
+
;// CONCATENATED MODULE: ./packages/core/concurrent/parent.ts
|
|
58209
|
+
|
|
58210
|
+
|
|
58211
|
+
// Amount of tasks in the queue that will trigger creation of new worker thread.
|
|
58212
|
+
// NOTE this might need to be configurable in the future.
|
|
58213
|
+
const QUEUE_SIZE_WORKER_THRESHOLD = 5;
|
|
58214
|
+
/** Execution pool manager. */
|
|
58215
|
+
class Executor {
|
|
58216
|
+
workers;
|
|
58217
|
+
maxWorkers;
|
|
58218
|
+
workerPath;
|
|
58219
|
+
/** Initialize a new concurrent executor given a path to the worker. */
|
|
58220
|
+
static async initialize(workerPath, options) {
|
|
58221
|
+
debug_check(options.maxWorkers > 0, "Max workers has to be positive.");
|
|
58222
|
+
debug_check(options.minWorkers <= options.maxWorkers, "Min workers has to be lower or equal to max workers.");
|
|
58223
|
+
const workers = [];
|
|
58224
|
+
for (let i = 0; i < options.minWorkers; i++) {
|
|
58225
|
+
workers.push(await initWorker(workerPath));
|
|
58226
|
+
}
|
|
58227
|
+
return new Executor(workers, options.maxWorkers, workerPath);
|
|
58228
|
+
}
|
|
58229
|
+
// keeps track of the indices of worker threads that are currently free and available to execute tasks
|
|
58230
|
+
freeWorkerIndices = [];
|
|
58231
|
+
taskQueue = [];
|
|
58232
|
+
isDestroyed = false;
|
|
58233
|
+
isWorkerInitializing = false;
|
|
58234
|
+
constructor(workers, maxWorkers, workerPath) {
|
|
58235
|
+
this.workers = workers;
|
|
58236
|
+
this.maxWorkers = maxWorkers;
|
|
58237
|
+
this.workerPath = workerPath;
|
|
58238
|
+
// intial free workers.
|
|
58239
|
+
for (let i = 0; i < workers.length; i++) {
|
|
58240
|
+
this.freeWorkerIndices.push(i);
|
|
58241
|
+
}
|
|
58242
|
+
}
|
|
58243
|
+
/** Attempt to initialize a new worker. */
|
|
58244
|
+
async initNewWorker(onSuccess = () => { }) {
|
|
58245
|
+
if (this.workers.length >= this.maxWorkers) {
|
|
58246
|
+
// biome-ignore lint/suspicious/noConsole: warning
|
|
58247
|
+
console.warn(`Task queue has ${this.taskQueue.length} pending items and we can't init any more workers.`);
|
|
58248
|
+
return;
|
|
58249
|
+
}
|
|
58250
|
+
if (this.isWorkerInitializing) {
|
|
58251
|
+
return;
|
|
58252
|
+
}
|
|
58253
|
+
this.isWorkerInitializing = true;
|
|
58254
|
+
this.workers.push(await initWorker(this.workerPath));
|
|
58255
|
+
this.freeWorkerIndices.push(this.workers.length - 1);
|
|
58256
|
+
this.isWorkerInitializing = false;
|
|
58257
|
+
onSuccess();
|
|
58258
|
+
}
|
|
58259
|
+
/** Terminate all workers and clear the executor. */
|
|
58260
|
+
async destroy() {
|
|
58261
|
+
for (const worker of this.workers) {
|
|
58262
|
+
worker.port.close();
|
|
58263
|
+
await worker.worker.terminate();
|
|
58264
|
+
}
|
|
58265
|
+
this.workers.length = 0;
|
|
58266
|
+
this.isDestroyed = true;
|
|
58267
|
+
}
|
|
58268
|
+
/** Execute a task with given parameters. */
|
|
58269
|
+
async run(params) {
|
|
58270
|
+
return new Promise((resolve, reject) => {
|
|
58271
|
+
if (this.isDestroyed) {
|
|
58272
|
+
reject("pool destroyed");
|
|
58273
|
+
return;
|
|
58274
|
+
}
|
|
58275
|
+
this.taskQueue.push({
|
|
58276
|
+
params,
|
|
58277
|
+
resolve,
|
|
58278
|
+
reject,
|
|
58279
|
+
});
|
|
58280
|
+
this.processEntryFromTaskQueue();
|
|
58281
|
+
});
|
|
58282
|
+
}
|
|
58283
|
+
/** Process single element from the task queue. */
|
|
58284
|
+
processEntryFromTaskQueue() {
|
|
58285
|
+
const freeWorker = this.freeWorkerIndices.pop();
|
|
58286
|
+
// no free workers available currently,
|
|
58287
|
+
// we will retry when one of the tasks completes.
|
|
58288
|
+
if (freeWorker === undefined) {
|
|
58289
|
+
if (this.taskQueue.length > QUEUE_SIZE_WORKER_THRESHOLD) {
|
|
58290
|
+
this.initNewWorker(() => {
|
|
58291
|
+
// process an entry in this newly initialized worker.
|
|
58292
|
+
this.processEntryFromTaskQueue();
|
|
58293
|
+
});
|
|
58294
|
+
}
|
|
58295
|
+
return;
|
|
58296
|
+
}
|
|
58297
|
+
const task = this.taskQueue.pop();
|
|
58298
|
+
// no tasks in the queue
|
|
58299
|
+
if (task === undefined) {
|
|
58300
|
+
this.freeWorkerIndices.push(freeWorker);
|
|
58301
|
+
return;
|
|
58302
|
+
}
|
|
58303
|
+
const worker = this.workers[freeWorker];
|
|
58304
|
+
worker.runTask(task, () => {
|
|
58305
|
+
// mark the worker as available again
|
|
58306
|
+
this.freeWorkerIndices.push(freeWorker);
|
|
58307
|
+
// and continue processing the queue
|
|
58308
|
+
this.processEntryFromTaskQueue();
|
|
58309
|
+
});
|
|
58310
|
+
}
|
|
58311
|
+
}
|
|
58312
|
+
async function initWorker(workerPath) {
|
|
58313
|
+
// create a worker and initialize communication channel
|
|
58314
|
+
const { port1, port2 } = new MessageChannel();
|
|
58315
|
+
const workerThread = new external_node_worker_threads_namespaceObject.Worker(workerPath, {});
|
|
58316
|
+
workerThread.postMessage(port1, [port1]);
|
|
58317
|
+
// // wait for the worker to start
|
|
58318
|
+
await new Promise((resolve, reject) => {
|
|
58319
|
+
workerThread.once("message", resolve);
|
|
58320
|
+
workerThread.once("error", reject);
|
|
58321
|
+
});
|
|
58322
|
+
// make sure the threads don't prevent the program from stopping.
|
|
58323
|
+
workerThread.unref();
|
|
58324
|
+
return new WorkerChannel(workerThread, port2);
|
|
58325
|
+
}
|
|
58326
|
+
class WorkerChannel {
|
|
58327
|
+
worker;
|
|
58328
|
+
port;
|
|
58329
|
+
constructor(worker, port) {
|
|
58330
|
+
this.worker = worker;
|
|
58331
|
+
this.port = port;
|
|
58332
|
+
}
|
|
58333
|
+
runTask(task, onFinish) {
|
|
58334
|
+
const message = {
|
|
58335
|
+
params: task.params,
|
|
58336
|
+
};
|
|
58337
|
+
// when we receive a response, make sure to process it
|
|
58338
|
+
this.port.once("message", (e) => {
|
|
58339
|
+
if (e.isOk) {
|
|
58340
|
+
task.resolve(e.ok);
|
|
58341
|
+
}
|
|
58342
|
+
else {
|
|
58343
|
+
task.reject(new Error(e.error));
|
|
58344
|
+
}
|
|
58345
|
+
onFinish();
|
|
58346
|
+
});
|
|
58347
|
+
// send the task to work on.
|
|
58348
|
+
this.port.postMessage(message, message.params.getTransferList());
|
|
58349
|
+
}
|
|
58350
|
+
}
|
|
58351
|
+
|
|
58352
|
+
;// CONCATENATED MODULE: ./packages/core/concurrent/worker.ts
|
|
58353
|
+
|
|
58354
|
+
|
|
58355
|
+
/** A in-worker abstraction. */
|
|
58356
|
+
class ConcurrentWorker {
|
|
58357
|
+
runInternal;
|
|
58358
|
+
state;
|
|
58359
|
+
static new(run, state) {
|
|
58360
|
+
return new ConcurrentWorker(run, state);
|
|
58361
|
+
}
|
|
58362
|
+
constructor(runInternal, state) {
|
|
58363
|
+
this.runInternal = runInternal;
|
|
58364
|
+
this.state = state;
|
|
58365
|
+
}
|
|
58366
|
+
listenToParentPort() {
|
|
58367
|
+
if (external_node_worker_threads_namespaceObject.parentPort === null) {
|
|
58368
|
+
throw new Error("This method is meant to be run inside a worker thread!");
|
|
58369
|
+
}
|
|
58370
|
+
external_node_worker_threads_namespaceObject.parentPort.once("close", () => {
|
|
58371
|
+
process.exit(0);
|
|
58372
|
+
});
|
|
58373
|
+
external_node_worker_threads_namespaceObject.parentPort.once("message", (port) => {
|
|
58374
|
+
this.listenTo(port);
|
|
58375
|
+
// send back readiness signal.
|
|
58376
|
+
external_node_worker_threads_namespaceObject.parentPort?.postMessage("ready");
|
|
58377
|
+
});
|
|
58378
|
+
}
|
|
58379
|
+
listenTo(port) {
|
|
58380
|
+
port.once("close", () => {
|
|
58381
|
+
port.removeAllListeners();
|
|
58382
|
+
process.exit(0);
|
|
58383
|
+
});
|
|
58384
|
+
port.on("message", (ev) => {
|
|
58385
|
+
const { params } = ev;
|
|
58386
|
+
this.run(params)
|
|
58387
|
+
.then((result) => {
|
|
58388
|
+
const response = result_Result.ok(result);
|
|
58389
|
+
port.postMessage(response, result.getTransferList());
|
|
58390
|
+
})
|
|
58391
|
+
.catch((e) => {
|
|
58392
|
+
const response = result_Result.error(`${e}`);
|
|
58393
|
+
port.postMessage(response, []);
|
|
58394
|
+
});
|
|
58395
|
+
});
|
|
58396
|
+
}
|
|
58397
|
+
async run(params) {
|
|
58398
|
+
return await this.runInternal(params, this.state);
|
|
58399
|
+
}
|
|
58400
|
+
async destroy() { }
|
|
58401
|
+
}
|
|
58402
|
+
|
|
58403
|
+
;// CONCATENATED MODULE: ./packages/core/concurrent/index.ts
|
|
58404
|
+
|
|
58405
|
+
|
|
58406
|
+
|
|
58407
|
+
;// CONCATENATED MODULE: ./packages/jam/safrole/bandersnatch-wasm/params.ts
|
|
58408
|
+
var Method;
|
|
58409
|
+
(function (Method) {
|
|
58410
|
+
Method[Method["RingCommitment"] = 0] = "RingCommitment";
|
|
58411
|
+
Method[Method["BatchVerifyTickets"] = 1] = "BatchVerifyTickets";
|
|
58412
|
+
Method[Method["VerifySeal"] = 2] = "VerifySeal";
|
|
58413
|
+
})(Method || (Method = {}));
|
|
58414
|
+
class params_Response {
|
|
58415
|
+
data;
|
|
58416
|
+
constructor(data) {
|
|
58417
|
+
this.data = data;
|
|
58418
|
+
}
|
|
58419
|
+
getTransferList() {
|
|
58420
|
+
return [this.data.buffer];
|
|
58421
|
+
}
|
|
58422
|
+
}
|
|
58423
|
+
class Params {
|
|
58424
|
+
params;
|
|
58425
|
+
constructor(params) {
|
|
58426
|
+
this.params = params;
|
|
58427
|
+
}
|
|
58428
|
+
getTransferList() {
|
|
58429
|
+
return [];
|
|
58430
|
+
}
|
|
58431
|
+
}
|
|
58432
|
+
|
|
58433
|
+
;// CONCATENATED MODULE: ./packages/jam/safrole/bandersnatch-wasm/worker.ts
|
|
58434
|
+
|
|
58435
|
+
|
|
58436
|
+
|
|
58437
|
+
|
|
58438
|
+
const worker = ConcurrentWorker.new(async (p) => {
|
|
58439
|
+
await initAll();
|
|
58440
|
+
const params = p.params;
|
|
58441
|
+
const method = params.method;
|
|
58442
|
+
if (method === Method.RingCommitment) {
|
|
58443
|
+
return Promise.resolve(new params_Response(bandersnatch_exports.ring_commitment(params.keys)));
|
|
58444
|
+
}
|
|
58445
|
+
if (method === Method.BatchVerifyTickets) {
|
|
58446
|
+
return Promise.resolve(new params_Response(bandersnatch_exports.batch_verify_tickets(params.ringSize, params.commitment, params.ticketsData, params.contextLength)));
|
|
58447
|
+
}
|
|
58448
|
+
if (method === Method.VerifySeal) {
|
|
58449
|
+
return Promise.resolve(new params_Response(bandersnatch_exports.verify_seal(params.authorKey, params.signature, params.payload, params.auxData)));
|
|
58450
|
+
}
|
|
58451
|
+
debug_assertNever(method);
|
|
58452
|
+
}, null);
|
|
58453
|
+
|
|
58454
|
+
;// CONCATENATED MODULE: ./packages/jam/safrole/bandersnatch-wasm/index.ts
|
|
58455
|
+
|
|
58124
58456
|
|
|
58457
|
+
|
|
58458
|
+
|
|
58459
|
+
const bandersnatch_wasm_workerFile = __nccwpck_require__.ab + "bootstrap-bandersnatch.mjs";
|
|
58125
58460
|
class BandernsatchWasm {
|
|
58126
|
-
|
|
58127
|
-
|
|
58128
|
-
|
|
58129
|
-
|
|
58461
|
+
executor;
|
|
58462
|
+
constructor(executor) {
|
|
58463
|
+
this.executor = executor;
|
|
58464
|
+
}
|
|
58465
|
+
destroy() {
|
|
58466
|
+
return this.executor.destroy();
|
|
58467
|
+
}
|
|
58468
|
+
static async new({ synchronous }) {
|
|
58469
|
+
const workers = external_node_os_default().cpus().length;
|
|
58470
|
+
return new BandernsatchWasm(!synchronous
|
|
58471
|
+
? await Executor.initialize(bandersnatch_wasm_workerFile, {
|
|
58472
|
+
minWorkers: Math.max(1, Math.floor(workers / 2)),
|
|
58473
|
+
maxWorkers: workers,
|
|
58474
|
+
})
|
|
58475
|
+
: worker);
|
|
58130
58476
|
}
|
|
58131
58477
|
async verifySeal(authorKey, signature, payload, auxData) {
|
|
58132
|
-
|
|
58478
|
+
const x = await this.executor.run(new Params({
|
|
58479
|
+
method: Method.VerifySeal,
|
|
58480
|
+
authorKey,
|
|
58481
|
+
signature,
|
|
58482
|
+
payload,
|
|
58483
|
+
auxData,
|
|
58484
|
+
}));
|
|
58485
|
+
return x.data;
|
|
58133
58486
|
}
|
|
58134
58487
|
async getRingCommitment(keys) {
|
|
58135
|
-
|
|
58488
|
+
const x = await this.executor.run(new Params({
|
|
58489
|
+
method: Method.RingCommitment,
|
|
58490
|
+
keys,
|
|
58491
|
+
}));
|
|
58492
|
+
return x.data;
|
|
58136
58493
|
}
|
|
58137
58494
|
async batchVerifyTicket(ringSize, commitment, ticketsData, contextLength) {
|
|
58138
|
-
|
|
58495
|
+
const x = await this.executor.run(new Params({
|
|
58496
|
+
method: Method.BatchVerifyTickets,
|
|
58497
|
+
ringSize,
|
|
58498
|
+
commitment,
|
|
58499
|
+
ticketsData,
|
|
58500
|
+
contextLength,
|
|
58501
|
+
}));
|
|
58502
|
+
return x.data;
|
|
58139
58503
|
}
|
|
58140
58504
|
}
|
|
58141
58505
|
|
|
@@ -58178,7 +58542,7 @@ class Safrole {
|
|
|
58178
58542
|
chainSpec;
|
|
58179
58543
|
state;
|
|
58180
58544
|
bandersnatch;
|
|
58181
|
-
constructor(chainSpec, state, bandersnatch = BandernsatchWasm.new()) {
|
|
58545
|
+
constructor(chainSpec, state, bandersnatch = BandernsatchWasm.new({ synchronous: true })) {
|
|
58182
58546
|
this.chainSpec = chainSpec;
|
|
58183
58547
|
this.state = state;
|
|
58184
58548
|
this.bandersnatch = bandersnatch;
|
|
@@ -58556,7 +58920,7 @@ var SafroleSealError;
|
|
|
58556
58920
|
const BANDERSNATCH_ZERO_KEY = bytes_Bytes.zero(BANDERSNATCH_KEY_BYTES).asOpaque();
|
|
58557
58921
|
class SafroleSeal {
|
|
58558
58922
|
bandersnatch;
|
|
58559
|
-
constructor(bandersnatch = BandernsatchWasm.new()) {
|
|
58923
|
+
constructor(bandersnatch = BandernsatchWasm.new({ synchronous: true })) {
|
|
58560
58924
|
this.bandersnatch = bandersnatch;
|
|
58561
58925
|
}
|
|
58562
58926
|
/**
|
|
@@ -63499,11 +63863,11 @@ class OnChain {
|
|
|
63499
63863
|
authorization;
|
|
63500
63864
|
// chapter 13: https://graypaper.fluffylabs.dev/#/68eaa1f/18b60118b601?v=0.6.4
|
|
63501
63865
|
statistics;
|
|
63502
|
-
constructor(chainSpec, state, blocks, hasher) {
|
|
63866
|
+
constructor(chainSpec, state, blocks, hasher, { enableParallelSealVerification }) {
|
|
63503
63867
|
this.chainSpec = chainSpec;
|
|
63504
63868
|
this.state = state;
|
|
63505
63869
|
this.hasher = hasher;
|
|
63506
|
-
const bandersnatch = BandernsatchWasm.new();
|
|
63870
|
+
const bandersnatch = BandernsatchWasm.new({ synchronous: !enableParallelSealVerification });
|
|
63507
63871
|
this.statistics = new Statistics(chainSpec, state);
|
|
63508
63872
|
this.safrole = new Safrole(chainSpec, state, bandersnatch);
|
|
63509
63873
|
this.safroleSeal = new SafroleSeal(bandersnatch);
|
|
@@ -63521,16 +63885,16 @@ class OnChain {
|
|
|
63521
63885
|
const sealState = this.safrole.getSafroleSealState(timeSlot);
|
|
63522
63886
|
return await this.safroleSeal.verifyHeaderSeal(block.header.view(), sealState);
|
|
63523
63887
|
}
|
|
63524
|
-
async transition(block, headerHash, omitSealVerification = false) {
|
|
63888
|
+
async transition(block, headerHash, preverifiedSeal = null, omitSealVerification = false) {
|
|
63525
63889
|
const headerView = block.header.view();
|
|
63526
63890
|
const header = block.header.materialize();
|
|
63527
63891
|
const timeSlot = header.timeSlotIndex;
|
|
63528
63892
|
// safrole seal
|
|
63529
|
-
let newEntropyHash;
|
|
63893
|
+
let newEntropyHash = preverifiedSeal;
|
|
63530
63894
|
if (omitSealVerification) {
|
|
63531
63895
|
newEntropyHash = hashBytes(header.seal).asOpaque();
|
|
63532
63896
|
}
|
|
63533
|
-
|
|
63897
|
+
if (newEntropyHash === null) {
|
|
63534
63898
|
const sealResult = await this.verifySeal(timeSlot, block);
|
|
63535
63899
|
if (sealResult.isError) {
|
|
63536
63900
|
return stfError(StfErrorKind.SafroleSeal, sealResult);
|
|
@@ -63637,7 +64001,7 @@ class OnChain {
|
|
|
63637
64001
|
assertEmpty(deferredTransfersRest);
|
|
63638
64002
|
const accumulateRoot = await this.accumulateOutput.transition({ accumulationOutputLog });
|
|
63639
64003
|
// recent history
|
|
63640
|
-
const recentHistoryUpdate = this.recentHistory.transition({
|
|
64004
|
+
const recentHistoryUpdate = await this.recentHistory.transition({
|
|
63641
64005
|
partial: recentHistoryPartialUpdate,
|
|
63642
64006
|
headerHash,
|
|
63643
64007
|
accumulateRoot,
|
|
@@ -63717,7 +64081,6 @@ function checkOffendersMatch(offendersMark, headerOffendersMark) {
|
|
|
63717
64081
|
|
|
63718
64082
|
|
|
63719
64083
|
|
|
63720
|
-
|
|
63721
64084
|
var ImporterErrorKind;
|
|
63722
64085
|
(function (ImporterErrorKind) {
|
|
63723
64086
|
ImporterErrorKind[ImporterErrorKind["Verifier"] = 0] = "Verifier";
|
|
@@ -63745,28 +64108,29 @@ class Importer {
|
|
|
63745
64108
|
throw new Error(`Unable to load best state from header hash: ${currentBestHeaderHash}.`);
|
|
63746
64109
|
}
|
|
63747
64110
|
this.verifier = new BlockVerifier(hasher, blocks);
|
|
63748
|
-
this.stf = new OnChain(spec, state, blocks, hasher);
|
|
64111
|
+
this.stf = new OnChain(spec, state, blocks, hasher, { enableParallelSealVerification: true });
|
|
63749
64112
|
this.state = state;
|
|
63750
64113
|
this.currentHash = currentBestHeaderHash;
|
|
63751
64114
|
logger.info(`😎 Best time slot: ${state.timeslot} (header hash: ${currentBestHeaderHash})`);
|
|
63752
64115
|
}
|
|
63753
|
-
|
|
63754
|
-
|
|
63755
|
-
|
|
63756
|
-
|
|
63757
|
-
|
|
63758
|
-
|
|
63759
|
-
|
|
63760
|
-
this.logger.
|
|
63761
|
-
return
|
|
63762
|
-
}
|
|
63763
|
-
|
|
63764
|
-
|
|
63765
|
-
|
|
63766
|
-
|
|
63767
|
-
|
|
64116
|
+
/** Attempt to pre-verify the seal to speed up importing. */
|
|
64117
|
+
async preverifySeal(timeSlot, block) {
|
|
64118
|
+
try {
|
|
64119
|
+
const res = await this.stf.verifySeal(timeSlot, block);
|
|
64120
|
+
if (res.isOk) {
|
|
64121
|
+
return res.ok;
|
|
64122
|
+
}
|
|
64123
|
+
this.logger.warn(`Unable to pre-verify the seal: ${resultToString(res)}`);
|
|
64124
|
+
return null;
|
|
64125
|
+
}
|
|
64126
|
+
catch (e) {
|
|
64127
|
+
this.logger.warn(`Error while trying to pre-verify the seal: ${e}`);
|
|
64128
|
+
return null;
|
|
64129
|
+
}
|
|
64130
|
+
}
|
|
64131
|
+
async importBlock(block, preverifiedSeal, omitSealVerification = false) {
|
|
63768
64132
|
const logger = this.logger;
|
|
63769
|
-
logger.log(
|
|
64133
|
+
logger.log(`🧱 Attempting to import a new block ${preverifiedSeal !== null ? "(seal preverified)" : ""}`);
|
|
63770
64134
|
const timerVerify = measure("import:verify");
|
|
63771
64135
|
const hash = await this.verifier.verifyBlock(block);
|
|
63772
64136
|
logger.log(timerVerify());
|
|
@@ -63791,7 +64155,7 @@ class Importer {
|
|
|
63791
64155
|
const headerHash = hash.ok;
|
|
63792
64156
|
logger.log(`🧱 Verified block: Got hash ${headerHash} for block at slot ${timeSlot}.`);
|
|
63793
64157
|
const timerStf = measure("import:stf");
|
|
63794
|
-
const res = await this.stf.transition(block, headerHash, omitSealVerification);
|
|
64158
|
+
const res = await this.stf.transition(block, headerHash, preverifiedSeal, omitSealVerification);
|
|
63795
64159
|
logger.log(timerStf());
|
|
63796
64160
|
if (res.isError) {
|
|
63797
64161
|
return importerError(ImporterErrorKind.Stf, res);
|
|
@@ -63841,19 +64205,6 @@ class Importer {
|
|
|
63841
64205
|
return stateEntries ?? null;
|
|
63842
64206
|
}
|
|
63843
64207
|
}
|
|
63844
|
-
/**
|
|
63845
|
-
* Attempt to safely extract timeslot of a block.
|
|
63846
|
-
*
|
|
63847
|
-
* NOTE: it may fail if encoding is invalid.
|
|
63848
|
-
*/
|
|
63849
|
-
function extractTimeSlot(block) {
|
|
63850
|
-
try {
|
|
63851
|
-
return block.header.view().timeSlotIndex.materialize();
|
|
63852
|
-
}
|
|
63853
|
-
catch {
|
|
63854
|
-
return tryAsTimeSlot(2 ** 32 - 1);
|
|
63855
|
-
}
|
|
63856
|
-
}
|
|
63857
64208
|
|
|
63858
64209
|
;// CONCATENATED MODULE: ./workers/importer/index.ts
|
|
63859
64210
|
|
|
@@ -63865,6 +64216,8 @@ function extractTimeSlot(block) {
|
|
|
63865
64216
|
|
|
63866
64217
|
|
|
63867
64218
|
|
|
64219
|
+
|
|
64220
|
+
|
|
63868
64221
|
const importer_logger = Logger.new(import.meta.filename, "importer");
|
|
63869
64222
|
if (!external_node_worker_threads_namespaceObject.isMainThread) {
|
|
63870
64223
|
Logger.configureAll(process.env.JAM_LOG ?? "", Level.LOG);
|
|
@@ -63881,6 +64234,7 @@ async function createImporter(config) {
|
|
|
63881
64234
|
const importer = new Importer(config.chainSpec, hasher, importer_logger, blocks, states);
|
|
63882
64235
|
return {
|
|
63883
64236
|
lmdb,
|
|
64237
|
+
blocks,
|
|
63884
64238
|
importer,
|
|
63885
64239
|
};
|
|
63886
64240
|
}
|
|
@@ -63895,27 +64249,65 @@ async function importer_main(channel) {
|
|
|
63895
64249
|
importer_logger.info(`📥 Importer starting ${channel.currentState()}`);
|
|
63896
64250
|
// Await the configuration object
|
|
63897
64251
|
const ready = await channel.waitForState("ready(importer)");
|
|
63898
|
-
let closeDb = async () => { };
|
|
63899
64252
|
const finished = await ready.doUntil("finished", async (worker, port) => {
|
|
63900
64253
|
const config = worker.getConfig();
|
|
63901
|
-
const {
|
|
63902
|
-
closeDb = async () => {
|
|
63903
|
-
await lmdb.close();
|
|
63904
|
-
};
|
|
64254
|
+
const { blocks, importer } = await createImporter(config);
|
|
63905
64255
|
// TODO [ToDr] this is shit, since we have circular dependency.
|
|
63906
64256
|
worker.setImporter(importer);
|
|
63907
64257
|
importer_logger.info("📥 Importer waiting for blocks.");
|
|
64258
|
+
// TODO [ToDr] back pressure?
|
|
64259
|
+
let isProcessing = false;
|
|
64260
|
+
const importingQueue = new ImportQueue(config.chainSpec, importer);
|
|
63908
64261
|
worker.onBlock.on(async (block) => {
|
|
63909
|
-
const
|
|
63910
|
-
|
|
63911
|
-
|
|
64262
|
+
const details = ImportQueue.getBlockDetails(block);
|
|
64263
|
+
// ignore invalid blocks.
|
|
64264
|
+
if (details.isError) {
|
|
64265
|
+
importer_logger.trace("🧊 Ignoring invalid block.");
|
|
64266
|
+
return;
|
|
64267
|
+
}
|
|
64268
|
+
// ignore already known blocks
|
|
64269
|
+
if (blocks.getHeader(details.ok.hash) !== null) {
|
|
64270
|
+
importer_logger.trace(`🧊 Already imported block: #${details.ok.data.timeSlot}.`);
|
|
64271
|
+
return;
|
|
64272
|
+
}
|
|
64273
|
+
const importResult = importingQueue.push(details.ok);
|
|
64274
|
+
// ignore blocks that are already queued
|
|
64275
|
+
if (importResult.isError) {
|
|
64276
|
+
importer_logger.trace(`🧊 Already queued block: #${details.ok.data.timeSlot}.`);
|
|
64277
|
+
return;
|
|
64278
|
+
}
|
|
64279
|
+
importer_logger.log(`🧊 Queued block: #${details.ok.data.timeSlot} (skip seal: ${config.omitSealVerification})`);
|
|
64280
|
+
if (isProcessing) {
|
|
64281
|
+
return;
|
|
64282
|
+
}
|
|
64283
|
+
isProcessing = true;
|
|
64284
|
+
try {
|
|
64285
|
+
for (;;) {
|
|
64286
|
+
const entry = importingQueue.shift();
|
|
64287
|
+
if (entry === undefined) {
|
|
64288
|
+
return;
|
|
64289
|
+
}
|
|
64290
|
+
const { block, seal, timeSlot } = entry;
|
|
64291
|
+
const timer = measure("importBlock");
|
|
64292
|
+
const maybeBestHeader = await importer.importBlock(block, await seal, config.omitSealVerification);
|
|
64293
|
+
if (maybeBestHeader.isOk) {
|
|
64294
|
+
const bestHeader = maybeBestHeader.ok;
|
|
64295
|
+
worker.announce(port, bestHeader);
|
|
64296
|
+
importer_logger.info(`🧊 Best block: #${bestHeader.data.timeSlotIndex.materialize()} (${bestHeader.hash})`);
|
|
64297
|
+
}
|
|
64298
|
+
else {
|
|
64299
|
+
importer_logger.log(`❌ Rejected block #${timeSlot}: ${resultToString(maybeBestHeader)}`);
|
|
64300
|
+
}
|
|
64301
|
+
importer_logger.log(timer());
|
|
64302
|
+
}
|
|
64303
|
+
}
|
|
64304
|
+
finally {
|
|
64305
|
+
isProcessing = false;
|
|
63912
64306
|
}
|
|
63913
64307
|
});
|
|
63914
64308
|
await wasmPromise;
|
|
63915
64309
|
});
|
|
63916
64310
|
importer_logger.info("📥 Importer finished. Closing channel.");
|
|
63917
|
-
// close the database
|
|
63918
|
-
await closeDb();
|
|
63919
64311
|
// Close the comms to gracefuly close the app.
|
|
63920
64312
|
finished.currentState().close(channel);
|
|
63921
64313
|
}
|