@typeberry/jam 0.0.5-f91bac5 → 0.1.0-08a9db1

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/importer/index.js CHANGED
@@ -3561,6 +3561,7 @@ var __webpack_exports__ = {};
3561
3561
 
3562
3562
  // EXPORTS
3563
3563
  __nccwpck_require__.d(__webpack_exports__, {
3564
+ F: () => (/* binding */ createImporter),
3564
3565
  i: () => (/* binding */ main)
3565
3566
  });
3566
3567
 
@@ -4223,10 +4224,17 @@ async function initAll() {
4223
4224
  await init.ed25519();
4224
4225
  await init.reedSolomon();
4225
4226
  }
4227
+ function initOnce(doInit) {
4228
+ let ready = null;
4229
+ return async () => {
4230
+ if (ready === null) ready = doInit();
4231
+ return await ready;
4232
+ };
4233
+ }
4226
4234
  const init = {
4227
- bandersnatch: async () => await bandersnatch_default({ module_or_path: await bandersnatch_bg_default() }),
4228
- ed25519: async () => await ed25519_wasm_default({ module_or_path: await ed25519_wasm_bg_default() }),
4229
- reedSolomon: async () => await reed_solomon_wasm_default({ module_or_path: await reed_solomon_wasm_bg_default() })
4235
+ bandersnatch: initOnce(async () => await bandersnatch_default({ module_or_path: await bandersnatch_bg_default() })),
4236
+ ed25519: initOnce(async () => await ed25519_wasm_default({ module_or_path: await ed25519_wasm_bg_default() })),
4237
+ reedSolomon: initOnce(async () => await reed_solomon_wasm_default({ module_or_path: await reed_solomon_wasm_bg_default() }))
4230
4238
  };
4231
4239
 
4232
4240
  //#endregion
@@ -5694,6 +5702,7 @@ async function verifyBatch(input) {
5694
5702
 
5695
5703
  ;// CONCATENATED MODULE: ./packages/core/hash/hash.ts
5696
5704
 
5705
+
5697
5706
  /**
5698
5707
  * Size of the output of the hash functions.
5699
5708
  *
@@ -5703,6 +5712,7 @@ async function verifyBatch(input) {
5703
5712
  const hash_HASH_SIZE = 32;
5704
5713
  /** A hash without last byte (useful for trie representation). */
5705
5714
  const TRUNCATED_HASH_SIZE = 31;
5715
+ const ZERO_HASH = bytes_Bytes.zero(hash_HASH_SIZE);
5706
5716
  /**
5707
5717
  * Container for some object with a hash that is related to this object.
5708
5718
  *
@@ -8535,7 +8545,7 @@ const common_tryAsServiceGas = (v) => opaque_asOpaqueType(numbers_tryAsU64(v));
8535
8545
  /** Attempt to convert a number into `CoreIndex`. */
8536
8546
  const common_tryAsCoreIndex = (v) => opaque_asOpaqueType(numbers_tryAsU16(v));
8537
8547
  /** Attempt to convert a number into `Epoch`. */
8538
- const tryAsEpoch = (v) => opaque_asOpaqueType(numbers_tryAsU32(v));
8548
+ const tryAsEpoch = (v) => asOpaqueType(tryAsU32(v));
8539
8549
  function tryAsPerValidator(array, spec) {
8540
8550
  debug_check(array.length === spec.validatorsCount, `Invalid per-validator array length. Expected ${spec.validatorsCount}, got: ${array.length}`);
8541
8551
  return sized_array_asKnownSize(array);
@@ -15962,14 +15972,12 @@ class WriteablePage extends MemoryPage {
15962
15972
 
15963
15973
 
15964
15974
 
15965
-
15966
-
15967
15975
  var AccessType;
15968
15976
  (function (AccessType) {
15969
15977
  AccessType[AccessType["READ"] = 0] = "READ";
15970
15978
  AccessType[AccessType["WRITE"] = 1] = "WRITE";
15971
15979
  })(AccessType || (AccessType = {}));
15972
- const memory_logger = Logger.new(import.meta.filename, "pvm:mem");
15980
+ // const logger = Logger.new(import.meta.filename, "pvm:mem");
15973
15981
  class Memory {
15974
15982
  sbrkIndex;
15975
15983
  virtualSbrkIndex;
@@ -16000,7 +16008,7 @@ class Memory {
16000
16008
  if (bytes.length === 0) {
16001
16009
  return result_Result.ok(result_OK);
16002
16010
  }
16003
- memory_logger.insane(`MEM[${address}] <- ${bytes_BytesBlob.blobFrom(bytes)}`);
16011
+ // logger.insane(`MEM[${address}] <- ${BytesBlob.blobFrom(bytes)}`);
16004
16012
  const pagesResult = this.getPages(address, bytes.length, AccessType.WRITE);
16005
16013
  if (pagesResult.isError) {
16006
16014
  return result_Result.error(pagesResult.error);
@@ -16067,7 +16075,7 @@ class Memory {
16067
16075
  currentPosition += bytesToRead;
16068
16076
  bytesLeft -= bytesToRead;
16069
16077
  }
16070
- memory_logger.insane(`MEM[${startAddress}] => ${bytes_BytesBlob.blobFrom(result)}`);
16078
+ // logger.insane(`MEM[${startAddress}] => ${BytesBlob.blobFrom(result)}`);
16071
16079
  return result_Result.ok(result_OK);
16072
16080
  }
16073
16081
  sbrk(length) {
@@ -18921,88 +18929,6 @@ class PvmExecutor {
18921
18929
 
18922
18930
 
18923
18931
 
18924
- ;// CONCATENATED MODULE: ./workers/importer/import-queue.ts
18925
-
18926
-
18927
-
18928
-
18929
-
18930
- class ImportQueue {
18931
- spec;
18932
- importer;
18933
- toImport = SortedArray.fromSortedArray((a, b) => {
18934
- const diff = a.timeSlot - b.timeSlot;
18935
- if (diff < 0) {
18936
- return Ordering.Greater;
18937
- }
18938
- if (diff > 0) {
18939
- return Ordering.Less;
18940
- }
18941
- return Ordering.Equal;
18942
- });
18943
- queuedBlocks = HashSet.new();
18944
- lastEpoch = tryAsEpoch(2 ** 32 - 1);
18945
- constructor(spec, importer) {
18946
- this.spec = spec;
18947
- this.importer = importer;
18948
- }
18949
- isCurrentEpoch(timeSlot) {
18950
- const epoch = Math.floor(timeSlot / this.spec.epochLength);
18951
- return this.lastEpoch === epoch;
18952
- }
18953
- startPreverification() {
18954
- for (const entry of this.toImport) {
18955
- if (this.isCurrentEpoch(entry.timeSlot)) {
18956
- entry.seal = this.importer.preverifySeal(entry.timeSlot, entry.block);
18957
- }
18958
- }
18959
- }
18960
- static getBlockDetails(block) {
18961
- let encodedHeader;
18962
- let timeSlot;
18963
- try {
18964
- encodedHeader = block.header.encoded();
18965
- timeSlot = block.header.view().timeSlotIndex.materialize();
18966
- }
18967
- catch {
18968
- return result_Result.error("invalid");
18969
- }
18970
- const headerHash = hashBytes(encodedHeader).asOpaque();
18971
- return result_Result.ok(new WithHash(headerHash, { block, timeSlot }));
18972
- }
18973
- push(details) {
18974
- const headerHash = details.hash;
18975
- if (this.queuedBlocks.has(headerHash)) {
18976
- return result_Result.error("already queued");
18977
- }
18978
- const { timeSlot, block } = details.data;
18979
- const entry = {
18980
- headerHash,
18981
- timeSlot,
18982
- block,
18983
- seal: this.isCurrentEpoch(timeSlot) ? this.importer.preverifySeal(timeSlot, block) : Promise.resolve(null),
18984
- };
18985
- this.toImport.insert(entry);
18986
- this.queuedBlocks.insert(headerHash);
18987
- return result_Result.ok(result_OK);
18988
- }
18989
- shift() {
18990
- const entry = this.toImport.pop();
18991
- if (entry !== undefined) {
18992
- this.queuedBlocks.delete(entry.headerHash);
18993
- const blockEpoch = Math.floor(entry.timeSlot / this.spec.epochLength);
18994
- const hasEpochChanged = this.lastEpoch !== blockEpoch;
18995
- this.lastEpoch = tryAsEpoch(blockEpoch);
18996
- // currently removed block is changing the epoch, so fire up
18997
- // preverifcation for the following blocks.
18998
- if (hasEpochChanged) {
18999
- this.startPreverification();
19000
- }
19001
- }
19002
- return entry;
19003
- }
19004
- }
19005
-
19006
18932
  ;// CONCATENATED MODULE: ./packages/jam/transition/block-verifier.ts
19007
18933
 
19008
18934
 
@@ -19016,7 +18942,7 @@ var BlockVerifierError;
19016
18942
  BlockVerifierError[BlockVerifierError["InvalidStateRoot"] = 4] = "InvalidStateRoot";
19017
18943
  BlockVerifierError[BlockVerifierError["AlreadyImported"] = 5] = "AlreadyImported";
19018
18944
  })(BlockVerifierError || (BlockVerifierError = {}));
19019
- const ZERO_HASH = bytes_Bytes.zero(hash_HASH_SIZE).asOpaque();
18945
+ const block_verifier_ZERO_HASH = bytes_Bytes.zero(hash_HASH_SIZE).asOpaque();
19020
18946
  class BlockVerifier {
19021
18947
  hasher;
19022
18948
  blocks;
@@ -19036,7 +18962,7 @@ class BlockVerifier {
19036
18962
  // https://graypaper.fluffylabs.dev/#/cc517d7/0c9d000c9d00?v=0.6.5
19037
18963
  const parentHash = headerView.parentHeaderHash.materialize();
19038
18964
  // importing genesis block
19039
- if (!parentHash.isEqualTo(ZERO_HASH)) {
18965
+ if (!parentHash.isEqualTo(block_verifier_ZERO_HASH)) {
19040
18966
  const parentBlock = this.blocks.getHeader(parentHash);
19041
18967
  if (parentBlock === null) {
19042
18968
  return result_Result.error(BlockVerifierError.ParentNotFound, `Parent ${parentHash.toString()} not found`);
@@ -19549,304 +19475,22 @@ async function verifyTickets(bandersnatch, numberOfValidators, epochRoot, ticket
19549
19475
  }));
19550
19476
  }
19551
19477
 
19552
- ;// CONCATENATED MODULE: external "node:os"
19553
- const external_node_os_namespaceObject = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:os");
19554
- var external_node_os_default = /*#__PURE__*/__nccwpck_require__.n(external_node_os_namespaceObject);
19555
- ;// CONCATENATED MODULE: ./packages/core/concurrent/parent.ts
19556
-
19557
-
19558
- // Amount of tasks in the queue that will trigger creation of new worker thread.
19559
- // NOTE this might need to be configurable in the future.
19560
- const QUEUE_SIZE_WORKER_THRESHOLD = 5;
19561
- /** Execution pool manager. */
19562
- class Executor {
19563
- workers;
19564
- maxWorkers;
19565
- workerPath;
19566
- /** Initialize a new concurrent executor given a path to the worker. */
19567
- static async initialize(workerPath, options) {
19568
- debug_check(options.maxWorkers > 0, "Max workers has to be positive.");
19569
- debug_check(options.minWorkers <= options.maxWorkers, "Min workers has to be lower or equal to max workers.");
19570
- const workers = [];
19571
- for (let i = 0; i < options.minWorkers; i++) {
19572
- workers.push(await initWorker(workerPath));
19573
- }
19574
- return new Executor(workers, options.maxWorkers, workerPath);
19575
- }
19576
- // keeps track of the indices of worker threads that are currently free and available to execute tasks
19577
- freeWorkerIndices = [];
19578
- taskQueue = [];
19579
- isDestroyed = false;
19580
- isWorkerInitializing = false;
19581
- constructor(workers, maxWorkers, workerPath) {
19582
- this.workers = workers;
19583
- this.maxWorkers = maxWorkers;
19584
- this.workerPath = workerPath;
19585
- // intial free workers.
19586
- for (let i = 0; i < workers.length; i++) {
19587
- this.freeWorkerIndices.push(i);
19588
- }
19589
- }
19590
- /** Attempt to initialize a new worker. */
19591
- async initNewWorker(onSuccess = () => { }) {
19592
- if (this.workers.length >= this.maxWorkers) {
19593
- // biome-ignore lint/suspicious/noConsole: warning
19594
- console.warn(`Task queue has ${this.taskQueue.length} pending items and we can't init any more workers.`);
19595
- return;
19596
- }
19597
- if (this.isWorkerInitializing) {
19598
- return;
19599
- }
19600
- this.isWorkerInitializing = true;
19601
- this.workers.push(await initWorker(this.workerPath));
19602
- this.freeWorkerIndices.push(this.workers.length - 1);
19603
- this.isWorkerInitializing = false;
19604
- onSuccess();
19605
- }
19606
- /** Terminate all workers and clear the executor. */
19607
- async destroy() {
19608
- for (const worker of this.workers) {
19609
- worker.port.close();
19610
- await worker.worker.terminate();
19611
- }
19612
- this.workers.length = 0;
19613
- this.isDestroyed = true;
19614
- }
19615
- /** Execute a task with given parameters. */
19616
- async run(params) {
19617
- return new Promise((resolve, reject) => {
19618
- if (this.isDestroyed) {
19619
- reject("pool destroyed");
19620
- return;
19621
- }
19622
- this.taskQueue.push({
19623
- params,
19624
- resolve,
19625
- reject,
19626
- });
19627
- this.processEntryFromTaskQueue();
19628
- });
19629
- }
19630
- /** Process single element from the task queue. */
19631
- processEntryFromTaskQueue() {
19632
- const freeWorker = this.freeWorkerIndices.pop();
19633
- // no free workers available currently,
19634
- // we will retry when one of the tasks completes.
19635
- if (freeWorker === undefined) {
19636
- if (this.taskQueue.length > QUEUE_SIZE_WORKER_THRESHOLD) {
19637
- this.initNewWorker(() => {
19638
- // process an entry in this newly initialized worker.
19639
- this.processEntryFromTaskQueue();
19640
- });
19641
- }
19642
- return;
19643
- }
19644
- const task = this.taskQueue.pop();
19645
- // no tasks in the queue
19646
- if (task === undefined) {
19647
- this.freeWorkerIndices.push(freeWorker);
19648
- return;
19649
- }
19650
- const worker = this.workers[freeWorker];
19651
- worker.runTask(task, () => {
19652
- // mark the worker as available again
19653
- this.freeWorkerIndices.push(freeWorker);
19654
- // and continue processing the queue
19655
- this.processEntryFromTaskQueue();
19656
- });
19657
- }
19658
- }
19659
- async function initWorker(workerPath) {
19660
- // create a worker and initialize communication channel
19661
- const { port1, port2 } = new MessageChannel();
19662
- const workerThread = new external_node_worker_threads_namespaceObject.Worker(workerPath, {});
19663
- workerThread.postMessage(port1, [port1]);
19664
- // // wait for the worker to start
19665
- await new Promise((resolve, reject) => {
19666
- workerThread.once("message", resolve);
19667
- workerThread.once("error", reject);
19668
- });
19669
- // make sure the threads don't prevent the program from stopping.
19670
- workerThread.unref();
19671
- return new WorkerChannel(workerThread, port2);
19672
- }
19673
- class WorkerChannel {
19674
- worker;
19675
- port;
19676
- constructor(worker, port) {
19677
- this.worker = worker;
19678
- this.port = port;
19679
- }
19680
- runTask(task, onFinish) {
19681
- const message = {
19682
- params: task.params,
19683
- };
19684
- // when we receive a response, make sure to process it
19685
- this.port.once("message", (e) => {
19686
- if (e.isOk) {
19687
- task.resolve(e.ok);
19688
- }
19689
- else {
19690
- task.reject(new Error(e.error));
19691
- }
19692
- onFinish();
19693
- });
19694
- // send the task to work on.
19695
- this.port.postMessage(message, message.params.getTransferList());
19696
- }
19697
- }
19698
-
19699
- ;// CONCATENATED MODULE: ./packages/core/concurrent/worker.ts
19478
+ ;// CONCATENATED MODULE: ./packages/jam/safrole/bandersnatch-wasm.ts
19700
19479
 
19701
-
19702
- /** A in-worker abstraction. */
19703
- class ConcurrentWorker {
19704
- runInternal;
19705
- state;
19706
- static new(run, state) {
19707
- return new ConcurrentWorker(run, state);
19708
- }
19709
- constructor(runInternal, state) {
19710
- this.runInternal = runInternal;
19711
- this.state = state;
19712
- }
19713
- listenToParentPort() {
19714
- if (external_node_worker_threads_namespaceObject.parentPort === null) {
19715
- throw new Error("This method is meant to be run inside a worker thread!");
19716
- }
19717
- external_node_worker_threads_namespaceObject.parentPort.once("close", () => {
19718
- process.exit(0);
19719
- });
19720
- external_node_worker_threads_namespaceObject.parentPort.once("message", (port) => {
19721
- this.listenTo(port);
19722
- // send back readiness signal.
19723
- external_node_worker_threads_namespaceObject.parentPort?.postMessage("ready");
19724
- });
19725
- }
19726
- listenTo(port) {
19727
- port.once("close", () => {
19728
- port.removeAllListeners();
19729
- process.exit(0);
19730
- });
19731
- port.on("message", (ev) => {
19732
- const { params } = ev;
19733
- this.run(params)
19734
- .then((result) => {
19735
- const response = result_Result.ok(result);
19736
- port.postMessage(response, result.getTransferList());
19737
- })
19738
- .catch((e) => {
19739
- const response = result_Result.error(`${e}`);
19740
- port.postMessage(response, []);
19741
- });
19742
- });
19743
- }
19744
- async run(params) {
19745
- return await this.runInternal(params, this.state);
19746
- }
19747
- async destroy() { }
19748
- }
19749
-
19750
- ;// CONCATENATED MODULE: ./packages/core/concurrent/index.ts
19751
-
19752
-
19753
-
19754
- ;// CONCATENATED MODULE: ./packages/jam/safrole/bandersnatch-wasm/params.ts
19755
- var Method;
19756
- (function (Method) {
19757
- Method[Method["RingCommitment"] = 0] = "RingCommitment";
19758
- Method[Method["BatchVerifyTickets"] = 1] = "BatchVerifyTickets";
19759
- Method[Method["VerifySeal"] = 2] = "VerifySeal";
19760
- })(Method || (Method = {}));
19761
- class params_Response {
19762
- data;
19763
- constructor(data) {
19764
- this.data = data;
19765
- }
19766
- getTransferList() {
19767
- return [this.data.buffer];
19768
- }
19769
- }
19770
- class Params {
19771
- params;
19772
- constructor(params) {
19773
- this.params = params;
19774
- }
19775
- getTransferList() {
19776
- return [];
19777
- }
19778
- }
19779
-
19780
- ;// CONCATENATED MODULE: ./packages/jam/safrole/bandersnatch-wasm/worker.ts
19781
-
19782
-
19783
-
19784
-
19785
- const worker = ConcurrentWorker.new(async (p) => {
19786
- await initAll();
19787
- const params = p.params;
19788
- const method = params.method;
19789
- if (method === Method.RingCommitment) {
19790
- return Promise.resolve(new params_Response(bandersnatch_exports.ring_commitment(params.keys)));
19791
- }
19792
- if (method === Method.BatchVerifyTickets) {
19793
- return Promise.resolve(new params_Response(bandersnatch_exports.batch_verify_tickets(params.ringSize, params.commitment, params.ticketsData, params.contextLength)));
19794
- }
19795
- if (method === Method.VerifySeal) {
19796
- return Promise.resolve(new params_Response(bandersnatch_exports.verify_seal(params.authorKey, params.signature, params.payload, params.auxData)));
19797
- }
19798
- debug_assertNever(method);
19799
- }, null);
19800
-
19801
- ;// CONCATENATED MODULE: ./packages/jam/safrole/bandersnatch-wasm/index.ts
19802
-
19803
-
19804
-
19805
-
19806
- const workerFile = __nccwpck_require__.ab + "bootstrap-bandersnatch.mjs";
19807
19480
  class BandernsatchWasm {
19808
- executor;
19809
- constructor(executor) {
19810
- this.executor = executor;
19811
- }
19812
- destroy() {
19813
- return this.executor.destroy();
19814
- }
19815
- static async new({ synchronous }) {
19816
- const workers = external_node_os_default().cpus().length;
19817
- return new BandernsatchWasm(!synchronous
19818
- ? await Executor.initialize(workerFile, {
19819
- minWorkers: Math.max(1, Math.floor(workers / 2)),
19820
- maxWorkers: workers,
19821
- })
19822
- : worker);
19481
+ constructor() { }
19482
+ static async new() {
19483
+ await initAll();
19484
+ return new BandernsatchWasm();
19823
19485
  }
19824
19486
  async verifySeal(authorKey, signature, payload, auxData) {
19825
- const x = await this.executor.run(new Params({
19826
- method: Method.VerifySeal,
19827
- authorKey,
19828
- signature,
19829
- payload,
19830
- auxData,
19831
- }));
19832
- return x.data;
19487
+ return bandersnatch_exports.verify_seal(authorKey, signature, payload, auxData);
19833
19488
  }
19834
19489
  async getRingCommitment(keys) {
19835
- const x = await this.executor.run(new Params({
19836
- method: Method.RingCommitment,
19837
- keys,
19838
- }));
19839
- return x.data;
19490
+ return bandersnatch_exports.ring_commitment(keys);
19840
19491
  }
19841
19492
  async batchVerifyTicket(ringSize, commitment, ticketsData, contextLength) {
19842
- const x = await this.executor.run(new Params({
19843
- method: Method.BatchVerifyTickets,
19844
- ringSize,
19845
- commitment,
19846
- ticketsData,
19847
- contextLength,
19848
- }));
19849
- return x.data;
19493
+ return bandersnatch_exports.batch_verify_tickets(ringSize, commitment, ticketsData, contextLength);
19850
19494
  }
19851
19495
  }
19852
19496
 
@@ -19889,7 +19533,7 @@ class Safrole {
19889
19533
  chainSpec;
19890
19534
  state;
19891
19535
  bandersnatch;
19892
- constructor(chainSpec, state, bandersnatch = BandernsatchWasm.new({ synchronous: true })) {
19536
+ constructor(chainSpec, state, bandersnatch = BandernsatchWasm.new()) {
19893
19537
  this.chainSpec = chainSpec;
19894
19538
  this.state = state;
19895
19539
  this.bandersnatch = bandersnatch;
@@ -20267,7 +19911,7 @@ var SafroleSealError;
20267
19911
  const BANDERSNATCH_ZERO_KEY = bytes_Bytes.zero(BANDERSNATCH_KEY_BYTES).asOpaque();
20268
19912
  class SafroleSeal {
20269
19913
  bandersnatch;
20270
- constructor(bandersnatch = BandernsatchWasm.new({ synchronous: true })) {
19914
+ constructor(bandersnatch = BandernsatchWasm.new()) {
20271
19915
  this.bandersnatch = bandersnatch;
20272
19916
  }
20273
19917
  /**
@@ -24437,7 +24081,8 @@ function verifyRefineContexts(minLookupSlot, contexts, recentBlocksPartialUpdate
24437
24081
  *
24438
24082
  * https://graypaper.fluffylabs.dev/#/5f542d7/155c01155f01
24439
24083
  */
24440
- const isInChain = recentBlocks.has(context.lookupAnchor) || headerChain.isAncestor(context.lookupAnchor);
24084
+ const isInChain = recentBlocks.has(context.lookupAnchor) ||
24085
+ headerChain.isAncestor(context.lookupAnchorSlot, context.lookupAnchor, context.anchor);
24441
24086
  if (!isInChain) {
24442
24087
  if (process.env.SKIP_LOOKUP_ANCHOR_CHECK !== undefined) {
24443
24088
  verify_contextual_logger.warn(`Lookup anchor check for ${context.lookupAnchor} would fail, but override is active.`);
@@ -25145,11 +24790,25 @@ class DbHeaderChain {
25145
24790
  constructor(blocks) {
25146
24791
  this.blocks = blocks;
25147
24792
  }
25148
- isAncestor(header) {
25149
- // TODO [ToDr] This works only for simple forks scenario. We rather
25150
- // should make sure that the `header` we are checking is a descendant
25151
- // of the current header (i.e. there is a direct path when going by parent).
25152
- return this.blocks.getHeader(header) !== null;
24793
+ isAncestor(pastHeaderSlot, pastHeader, currentHeader) {
24794
+ let currentHash = currentHeader;
24795
+ for (;;) {
24796
+ // success = we found the right header in the DB
24797
+ if (currentHash.isEqualTo(pastHeader)) {
24798
+ return true;
24799
+ }
24800
+ const current = this.blocks.getHeader(currentHash);
24801
+ // fail if we don't find a parent (unlikely?)
24802
+ if (current === null) {
24803
+ return false;
24804
+ }
24805
+ // fail if we went pass that time slot index
24806
+ if (current.timeSlotIndex.materialize() < pastHeaderSlot) {
24807
+ return false;
24808
+ }
24809
+ // move one block up
24810
+ currentHash = current.parentHeaderHash.materialize();
24811
+ }
25153
24812
  }
25154
24813
  }
25155
24814
  const OFFENDERS_ERROR = "offenders not matching header";
@@ -25195,11 +24854,11 @@ class OnChain {
25195
24854
  authorization;
25196
24855
  // chapter 13: https://graypaper.fluffylabs.dev/#/68eaa1f/18b60118b601?v=0.6.4
25197
24856
  statistics;
25198
- constructor(chainSpec, state, blocks, hasher, { enableParallelSealVerification }) {
24857
+ constructor(chainSpec, state, blocks, hasher) {
25199
24858
  this.chainSpec = chainSpec;
25200
24859
  this.state = state;
25201
24860
  this.hasher = hasher;
25202
- const bandersnatch = BandernsatchWasm.new({ synchronous: !enableParallelSealVerification });
24861
+ const bandersnatch = BandernsatchWasm.new();
25203
24862
  this.statistics = new Statistics(chainSpec, state);
25204
24863
  this.safrole = new Safrole(chainSpec, state, bandersnatch);
25205
24864
  this.safroleSeal = new SafroleSeal(bandersnatch);
@@ -25217,16 +24876,16 @@ class OnChain {
25217
24876
  const sealState = this.safrole.getSafroleSealState(timeSlot);
25218
24877
  return await this.safroleSeal.verifyHeaderSeal(block.header.view(), sealState);
25219
24878
  }
25220
- async transition(block, headerHash, preverifiedSeal = null, omitSealVerification = false) {
24879
+ async transition(block, headerHash, omitSealVerification = false) {
25221
24880
  const headerView = block.header.view();
25222
24881
  const header = block.header.materialize();
25223
24882
  const timeSlot = header.timeSlotIndex;
25224
24883
  // safrole seal
25225
- let newEntropyHash = preverifiedSeal;
24884
+ let newEntropyHash;
25226
24885
  if (omitSealVerification) {
25227
24886
  newEntropyHash = hashBytes(header.seal).asOpaque();
25228
24887
  }
25229
- if (newEntropyHash === null) {
24888
+ else {
25230
24889
  const sealResult = await this.verifySeal(timeSlot, block);
25231
24890
  if (sealResult.isError) {
25232
24891
  return stfError(StfErrorKind.SafroleSeal, sealResult);
@@ -25333,7 +24992,7 @@ class OnChain {
25333
24992
  assertEmpty(deferredTransfersRest);
25334
24993
  const accumulateRoot = await this.accumulateOutput.transition({ accumulationOutputLog });
25335
24994
  // recent history
25336
- const recentHistoryUpdate = await this.recentHistory.transition({
24995
+ const recentHistoryUpdate = this.recentHistory.transition({
25337
24996
  partial: recentHistoryPartialUpdate,
25338
24997
  headerHash,
25339
24998
  accumulateRoot,
@@ -25413,6 +25072,7 @@ function checkOffendersMatch(offendersMark, headerOffendersMark) {
25413
25072
 
25414
25073
 
25415
25074
 
25075
+
25416
25076
  var ImporterErrorKind;
25417
25077
  (function (ImporterErrorKind) {
25418
25078
  ImporterErrorKind[ImporterErrorKind["Verifier"] = 0] = "Verifier";
@@ -25440,29 +25100,28 @@ class Importer {
25440
25100
  throw new Error(`Unable to load best state from header hash: ${currentBestHeaderHash}.`);
25441
25101
  }
25442
25102
  this.verifier = new BlockVerifier(hasher, blocks);
25443
- this.stf = new OnChain(spec, state, blocks, hasher, { enableParallelSealVerification: true });
25103
+ this.stf = new OnChain(spec, state, blocks, hasher);
25444
25104
  this.state = state;
25445
25105
  this.currentHash = currentBestHeaderHash;
25446
25106
  logger.info(`😎 Best time slot: ${state.timeslot} (header hash: ${currentBestHeaderHash})`);
25447
25107
  }
25448
- /** Attempt to pre-verify the seal to speed up importing. */
25449
- async preverifySeal(timeSlot, block) {
25450
- try {
25451
- const res = await this.stf.verifySeal(timeSlot, block);
25452
- if (res.isOk) {
25453
- return res.ok;
25454
- }
25455
- this.logger.warn(`Unable to pre-verify the seal: ${resultToString(res)}`);
25456
- return null;
25457
- }
25458
- catch (e) {
25459
- this.logger.warn(`Error while trying to pre-verify the seal: ${e}`);
25460
- return null;
25461
- }
25462
- }
25463
- async importBlock(block, preverifiedSeal, omitSealVerification = false) {
25108
+ async importBlock(block, omitSealVerification) {
25109
+ const timer = measure("importBlock");
25110
+ const timeSlot = extractTimeSlot(block);
25111
+ const maybeBestHeader = await this.importBlockInternal(block, omitSealVerification);
25112
+ if (maybeBestHeader.isOk) {
25113
+ const bestHeader = maybeBestHeader.ok;
25114
+ this.logger.info(`🧊 Best block: #${timeSlot} (${bestHeader.hash})`);
25115
+ this.logger.log(timer());
25116
+ return maybeBestHeader;
25117
+ }
25118
+ this.logger.log(`❌ Rejected block #${timeSlot}: ${resultToString(maybeBestHeader)}`);
25119
+ this.logger.log(timer());
25120
+ return maybeBestHeader;
25121
+ }
25122
+ async importBlockInternal(block, omitSealVerification = false) {
25464
25123
  const logger = this.logger;
25465
- logger.log(`🧱 Attempting to import a new block ${preverifiedSeal !== null ? "(seal preverified)" : ""}`);
25124
+ logger.log("🧱 Attempting to import a new block");
25466
25125
  const timerVerify = measure("import:verify");
25467
25126
  const hash = await this.verifier.verifyBlock(block);
25468
25127
  logger.log(timerVerify());
@@ -25487,7 +25146,7 @@ class Importer {
25487
25146
  const headerHash = hash.ok;
25488
25147
  logger.log(`🧱 Verified block: Got hash ${headerHash} for block at slot ${timeSlot}.`);
25489
25148
  const timerStf = measure("import:stf");
25490
- const res = await this.stf.transition(block, headerHash, preverifiedSeal, omitSealVerification);
25149
+ const res = await this.stf.transition(block, headerHash, omitSealVerification);
25491
25150
  logger.log(timerStf());
25492
25151
  if (res.isError) {
25493
25152
  return importerError(ImporterErrorKind.Stf, res);
@@ -25537,6 +25196,19 @@ class Importer {
25537
25196
  return stateEntries ?? null;
25538
25197
  }
25539
25198
  }
25199
+ /**
25200
+ * Attempt to safely extract timeslot of a block.
25201
+ *
25202
+ * NOTE: it may fail if encoding is invalid.
25203
+ */
25204
+ function extractTimeSlot(block) {
25205
+ try {
25206
+ return block.header.view().timeSlotIndex.materialize();
25207
+ }
25208
+ catch {
25209
+ return tryAsTimeSlot(2 ** 32 - 1);
25210
+ }
25211
+ }
25540
25212
 
25541
25213
  ;// CONCATENATED MODULE: ./workers/generic/finished.ts
25542
25214
 
@@ -25636,7 +25308,7 @@ function importerStateMachine() {
25636
25308
  return new machine_StateMachine("importer", initialized, [initialized, ready, finished]);
25637
25309
  }
25638
25310
  const state_machine_logger = Logger.new(import.meta.filename, "importer");
25639
- const importBlockCodec = descriptors_codec.custom({
25311
+ const importBlockResultCodec = descriptors_codec.custom({
25640
25312
  name: "Result<StateRootHash, string>",
25641
25313
  sizeHint: { bytes: 1, isExact: false },
25642
25314
  }, (e, x) => {
@@ -25704,7 +25376,7 @@ class MainReady extends State {
25704
25376
  async importBlock(port, block) {
25705
25377
  const res = await port.sendRequest("importBlock", block, [block.buffer]);
25706
25378
  if (res instanceof Uint8Array) {
25707
- return decoder_Decoder.decodeObject(importBlockCodec, res);
25379
+ return decoder_Decoder.decodeObject(importBlockResultCodec, res);
25708
25380
  }
25709
25381
  return result_Result.error("Invalid worker response.");
25710
25382
  }
@@ -25752,6 +25424,9 @@ class ImporterReady extends State {
25752
25424
  this.importer = importer;
25753
25425
  this.onImporter.emit();
25754
25426
  }
25427
+ setConfig(config) {
25428
+ this.data = config;
25429
+ }
25755
25430
  getConfig() {
25756
25431
  if (this.data === null) {
25757
25432
  throw new Error("Did not receive chain spec config!");
@@ -25797,7 +25472,6 @@ class ImporterReady extends State {
25797
25472
  response: rootHash === null ? bytes_Bytes.zero(hash_HASH_SIZE).raw : rootHash.raw,
25798
25473
  };
25799
25474
  }
25800
- // NOTE [ToDr] This should rather be using the import queue, instead of going directly.
25801
25475
  async importBlock(block) {
25802
25476
  if (this.importer === null) {
25803
25477
  state_machine_logger.error(`${this.constructor.name} importer not initialized yet!`);
@@ -25809,17 +25483,13 @@ class ImporterReady extends State {
25809
25483
  if (block instanceof Uint8Array) {
25810
25484
  const config = this.getConfig();
25811
25485
  const blockView = decoder_Decoder.decodeObject(Block.Codec.View, block, config.chainSpec);
25812
- const headerView = blockView.header.view();
25813
- const timeSlot = headerView.timeSlotIndex.materialize();
25814
25486
  let response;
25815
25487
  try {
25816
- const res = await this.importer.importBlock(blockView, null, config.omitSealVerification);
25488
+ const res = await this.importer.importBlock(blockView, config.omitSealVerification);
25817
25489
  if (res.isOk) {
25818
- state_machine_logger.info(`🧊 Best block: #${timeSlot} (${res.ok.hash})`);
25819
- response = result_Result.ok(this.importer.getBestStateRootHash() ?? bytes_Bytes.zero(hash_HASH_SIZE).asOpaque());
25490
+ response = result_Result.ok(this.importer.getBestStateRootHash() ?? ZERO_HASH.asOpaque());
25820
25491
  }
25821
25492
  else {
25822
- state_machine_logger.log(`❌ Rejected block #${timeSlot}: ${resultToString(res)}`);
25823
25493
  response = result_Result.error(resultToString(res));
25824
25494
  }
25825
25495
  }
@@ -25828,7 +25498,7 @@ class ImporterReady extends State {
25828
25498
  state_machine_logger.error(`${e instanceof Error ? e.stack : ""}`);
25829
25499
  response = result_Result.error(`${e}`);
25830
25500
  }
25831
- const encoded = encoder_Encoder.encodeObject(importBlockCodec, response);
25501
+ const encoded = encoder_Encoder.encodeObject(importBlockResultCodec, response);
25832
25502
  return {
25833
25503
  response: encoded.raw,
25834
25504
  };
@@ -25867,8 +25537,6 @@ class ImporterReady extends State {
25867
25537
 
25868
25538
 
25869
25539
 
25870
-
25871
-
25872
25540
  const importer_logger = Logger.new(import.meta.filename, "importer");
25873
25541
  if (!external_node_worker_threads_namespaceObject.isMainThread) {
25874
25542
  Logger.configureAll(process.env.JAM_LOG ?? "", Level.LOG);
@@ -25877,6 +25545,17 @@ if (!external_node_worker_threads_namespaceObject.isMainThread) {
25877
25545
  channel.then((channel) => main(channel)).catch((e) => importer_logger.error(e));
25878
25546
  }
25879
25547
  const keccakHasher = KeccakHasher.create();
25548
+ async function createImporter(config) {
25549
+ const lmdb = new LmdbRoot(config.dbPath);
25550
+ const blocks = new LmdbBlocks(config.chainSpec, lmdb);
25551
+ const states = new LmdbStates(config.chainSpec, lmdb);
25552
+ const hasher = new TransitionHasher(config.chainSpec, await keccakHasher, new allocator_SimpleAllocator());
25553
+ const importer = new Importer(config.chainSpec, hasher, importer_logger, blocks, states);
25554
+ return {
25555
+ lmdb,
25556
+ importer,
25557
+ };
25558
+ }
25880
25559
  /**
25881
25560
  * The `BlockImporter` listens to `block` signals, where it expects
25882
25561
  * RAW undecoded block objects (typically coming from the network).
@@ -25888,74 +25567,33 @@ async function main(channel) {
25888
25567
  importer_logger.info(`📥 Importer starting ${channel.currentState()}`);
25889
25568
  // Await the configuration object
25890
25569
  const ready = await channel.waitForState("ready(importer)");
25570
+ let closeDb = async () => { };
25891
25571
  const finished = await ready.doUntil("finished", async (worker, port) => {
25892
25572
  const config = worker.getConfig();
25893
- const lmdb = new LmdbRoot(config.dbPath);
25894
- const blocks = new LmdbBlocks(config.chainSpec, lmdb);
25895
- const states = new LmdbStates(config.chainSpec, lmdb);
25896
- const hasher = new TransitionHasher(config.chainSpec, await keccakHasher, new allocator_SimpleAllocator());
25897
- const importer = new Importer(config.chainSpec, hasher, importer_logger, blocks, states);
25573
+ const { lmdb, importer } = await createImporter(config);
25574
+ closeDb = async () => {
25575
+ await lmdb.close();
25576
+ };
25898
25577
  // TODO [ToDr] this is shit, since we have circular dependency.
25899
25578
  worker.setImporter(importer);
25900
25579
  importer_logger.info("📥 Importer waiting for blocks.");
25901
- // TODO [ToDr] back pressure?
25902
- let isProcessing = false;
25903
- const importingQueue = new ImportQueue(config.chainSpec, importer);
25904
25580
  worker.onBlock.on(async (block) => {
25905
- const details = ImportQueue.getBlockDetails(block);
25906
- // ignore invalid blocks.
25907
- if (details.isError) {
25908
- importer_logger.trace("🧊 Ignoring invalid block.");
25909
- return;
25910
- }
25911
- // ignore already known blocks
25912
- if (blocks.getHeader(details.ok.hash) !== null) {
25913
- importer_logger.trace(`🧊 Already imported block: #${details.ok.data.timeSlot}.`);
25914
- return;
25915
- }
25916
- const importResult = importingQueue.push(details.ok);
25917
- // ignore blocks that are already queued
25918
- if (importResult.isError) {
25919
- importer_logger.trace(`🧊 Already queued block: #${details.ok.data.timeSlot}.`);
25920
- return;
25921
- }
25922
- importer_logger.log(`🧊 Queued block: #${details.ok.data.timeSlot} (skip seal: ${config.omitSealVerification})`);
25923
- if (isProcessing) {
25924
- return;
25925
- }
25926
- isProcessing = true;
25927
- try {
25928
- for (;;) {
25929
- const entry = importingQueue.shift();
25930
- if (entry === undefined) {
25931
- return;
25932
- }
25933
- const { block, seal, timeSlot } = entry;
25934
- const timer = measure("importBlock");
25935
- const maybeBestHeader = await importer.importBlock(block, await seal, config.omitSealVerification);
25936
- if (maybeBestHeader.isOk) {
25937
- const bestHeader = maybeBestHeader.ok;
25938
- worker.announce(port, bestHeader);
25939
- importer_logger.info(`🧊 Best block: #${bestHeader.data.timeSlotIndex.materialize()} (${bestHeader.hash})`);
25940
- }
25941
- else {
25942
- importer_logger.log(`❌ Rejected block #${timeSlot}: ${resultToString(maybeBestHeader)}`);
25943
- }
25944
- importer_logger.log(timer());
25945
- }
25946
- }
25947
- finally {
25948
- isProcessing = false;
25581
+ const res = await importer.importBlock(block, config.omitSealVerification);
25582
+ if (res.isOk) {
25583
+ worker.announce(port, res.ok);
25949
25584
  }
25950
25585
  });
25951
25586
  await wasmPromise;
25952
25587
  });
25953
25588
  importer_logger.info("📥 Importer finished. Closing channel.");
25589
+ // close the database
25590
+ await closeDb();
25954
25591
  // Close the comms to gracefuly close the app.
25955
25592
  finished.currentState().close(channel);
25956
25593
  }
25957
25594
 
25595
+ var __webpack_exports__createImporter = __webpack_exports__.F;
25958
25596
  var __webpack_exports__main = __webpack_exports__.i;
25959
- export { __webpack_exports__main as main };
25597
+ export { __webpack_exports__createImporter as createImporter, __webpack_exports__main as main };
25960
25598
 
25961
25599
  //# sourceMappingURL=index.js.map