@typeberry/jam 0.5.8-857ec24 → 0.5.8-b3da767

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/index.js CHANGED
@@ -152750,6 +152750,9 @@ class InMemorySerializedStates {
152750
152750
  });
152751
152751
  return serialized_state_SerializedState.new(this.spec, this.blake2b, leafDb);
152752
152752
  }
152753
+ markUnused(header) {
152754
+ this.db.delete(header);
152755
+ }
152753
152756
  async close() { }
152754
152757
  }
152755
152758
 
@@ -152806,6 +152809,9 @@ class InMemoryStates {
152806
152809
  }
152807
152810
  return InMemoryState.copyFrom(this.spec, state, state.intoServicesData());
152808
152811
  }
152812
+ markUnused(header) {
152813
+ this.db.delete(header);
152814
+ }
152809
152815
  async close() { }
152810
152816
  }
152811
152817
 
@@ -153050,6 +153056,9 @@ class LmdbStates {
153050
153056
  }
153051
153057
  return serialized_state_SerializedState.new(this.spec, this.blake2b, leafDbResult.ok);
153052
153058
  }
153059
+ markUnused(header) {
153060
+ this.states.remove(header.raw);
153061
+ }
153053
153062
  async close() {
153054
153063
  await Promise.all([this.states.close(), this.values.close()]);
153055
153064
  }
@@ -166533,6 +166542,113 @@ class TransitionHasher {
166533
166542
 
166534
166543
 
166535
166544
 
166545
+ ;// CONCATENATED MODULE: ./packages/workers/importer/finality.ts
166546
+
166547
+ const finality_logger = logger_Logger.new(import.meta.filename, "finality");
166548
+ /**
166549
+ * A simple finalizer that considers a block finalized when N blocks
166550
+ * have been built on top of it.
166551
+ *
166552
+ * Maintains an array of fork chains starting from the last finalized block.
166553
+ * When any chain reaches `depth`, the earliest blocks are finalized and
166554
+ * dead forks (branching from before the finalized point) are discarded.
166555
+ */
166556
+ class DummyFinalizer {
166557
+ blocks;
166558
+ depth;
166559
+ lastFinalizedHash;
166560
+ unfinalized = [];
166561
+ static create(blocks, depth) {
166562
+ return new DummyFinalizer(blocks, depth);
166563
+ }
166564
+ constructor(blocks, depth) {
166565
+ this.blocks = blocks;
166566
+ this.depth = depth;
166567
+ this.lastFinalizedHash = blocks.getBestHeaderHash();
166568
+ finality_logger.info `🦭 Dummy Finalizer running with depth=${depth}`;
166569
+ }
166570
+ onBlockImported(headerHash) {
166571
+ const header = this.blocks.getHeader(headerHash);
166572
+ if (header === null) {
166573
+ return null;
166574
+ }
166575
+ const parentHash = header.parentHeaderHash.materialize();
166576
+ // Try to attach the block to an existing chain at its tip.
166577
+ let extendedChain = null;
166578
+ for (const chain of this.unfinalized) {
166579
+ if (chain.length > 0 && chain[chain.length - 1].isEqualTo(parentHash)) {
166580
+ chain.push(headerHash);
166581
+ extendedChain = chain;
166582
+ break;
166583
+ }
166584
+ }
166585
+ if (extendedChain === null) {
166586
+ if (this.lastFinalizedHash.isEqualTo(parentHash)) {
166587
+ // Parent is the finalized block — start a new chain.
166588
+ const newChain = [headerHash];
166589
+ this.unfinalized.push(newChain);
166590
+ extendedChain = newChain;
166591
+ }
166592
+ else {
166593
+ // Fork from the middle of an existing chain — copy the prefix and branch.
166594
+ for (const chain of this.unfinalized) {
166595
+ const forkIdx = chain.findIndex((h) => h.isEqualTo(parentHash));
166596
+ if (forkIdx !== -1) {
166597
+ const newChain = [...chain.slice(0, forkIdx + 1), headerHash];
166598
+ this.unfinalized.push(newChain);
166599
+ extendedChain = newChain;
166600
+ break;
166601
+ }
166602
+ }
166603
+ }
166604
+ }
166605
+ if (extendedChain === null) {
166606
+ // Orphan block — cannot attach to any known chain.
166607
+ return null;
166608
+ }
166609
+ // Check if the extended chain is long enough to trigger finality.
166610
+ // A chain of length N has N-1 blocks built on top of chain[0].
166611
+ // We finalize chain[0] when there are >= depth blocks after it,
166612
+ // i.e. chain.length > depth.
166613
+ if (extendedChain.length <= this.depth) {
166614
+ return null;
166615
+ }
166616
+ // The newly finalized block sits at index (length - 1 - depth).
166617
+ const finalizedIdx = extendedChain.length - 1 - this.depth;
166618
+ const finalizedHash = extendedChain[finalizedIdx];
166619
+ // Collect prunable hashes and rebuild the unfinalized set.
166620
+ // The previously finalized block's state is no longer needed.
166621
+ const prunable = [this.lastFinalizedHash];
166622
+ const newUnfinalized = [];
166623
+ for (const chain of this.unfinalized) {
166624
+ // Find the finalized block in this chain.
166625
+ const finIdx = chain.findIndex((h) => h.isEqualTo(finalizedHash));
166626
+ if (finIdx !== -1) {
166627
+ // Chain contains the finalized block — it's still alive.
166628
+ // Prune states for blocks before the finalized block.
166629
+ for (let i = 0; i < finIdx; i++) {
166630
+ prunable.push(chain[i]);
166631
+ }
166632
+ // Keep blocks after the finalized block.
166633
+ const remaining = chain.slice(finIdx + 1);
166634
+ if (remaining.length > 0) {
166635
+ newUnfinalized.push(remaining);
166636
+ }
166637
+ }
166638
+ else {
166639
+ // Dead fork — branches from a block that is no longer finalized.
166640
+ // Prune all its states.
166641
+ for (const h of chain) {
166642
+ prunable.push(h);
166643
+ }
166644
+ }
166645
+ }
166646
+ this.lastFinalizedHash = finalizedHash;
166647
+ this.unfinalized = newUnfinalized;
166648
+ return { finalizedHash, prunableStateHashes: prunable };
166649
+ }
166650
+ }
166651
+
166536
166652
  ;// CONCATENATED MODULE: ./packages/workers/importer/metrics.ts
166537
166653
 
166538
166654
 
@@ -166757,6 +166873,14 @@ class Importer {
166757
166873
  logger.log `${timerDb()}`;
166758
166874
  // finally update the best block
166759
166875
  await this.blocks.setBestHeaderHash(headerHash);
166876
+ // check for finality and prune old states
166877
+ const finality = this.options.finalizer?.onBlockImported(headerHash) ?? null;
166878
+ if (finality !== null) {
166879
+ this.logger.info `🦭 Finalized block: ${finality.finalizedHash} (${finality.prunableStateHashes.length} to prune)`;
166880
+ for (const hash of finality.prunableStateHashes) {
166881
+ this.states.markUnused(hash);
166882
+ }
166883
+ }
166760
166884
  return result_Result.ok(new WithHash(headerHash, block.header.view()));
166761
166885
  }
166762
166886
  getBestStateRootHash() {
@@ -166798,6 +166922,7 @@ function extractTimeSlot(block) {
166798
166922
 
166799
166923
 
166800
166924
 
166925
+
166801
166926
  const main_logger = logger_Logger.new(import.meta.filename, "importer");
166802
166927
  const keccakHasher = KeccakHasher.create();
166803
166928
  const blake2b = blake2b_Blake2b.createHasher();
@@ -166807,8 +166932,13 @@ async function createImporter(config, options = {}) {
166807
166932
  const pvm = config.workerParams.pvm;
166808
166933
  const blocks = db.getBlocksDb();
166809
166934
  const states = db.getStatesDb();
166935
+ const dummyFinalityDepth = config.workerParams.dummyFinalityDepth ?? 0;
166936
+ const finalizer = dummyFinalityDepth > 0 ? DummyFinalizer.create(blocks, dummyFinalityDepth) : undefined;
166810
166937
  const hasher = new TransitionHasher(await keccakHasher, await blake2b);
166811
- const importer = new Importer(chainSpec, pvm, hasher, main_logger, blocks, states, options);
166938
+ const importer = new Importer(chainSpec, pvm, hasher, main_logger, blocks, states, {
166939
+ ...options,
166940
+ finalizer,
166941
+ });
166812
166942
  return {
166813
166943
  importer,
166814
166944
  db,
@@ -166926,6 +167056,7 @@ const protocol_protocol = createProtocol("importer", {
166926
167056
  });
166927
167057
  class ImporterConfig {
166928
167058
  pvm;
167059
+ dummyFinalityDepth;
166929
167060
  static Codec = codec_codec.Class(ImporterConfig, {
166930
167061
  pvm: codec_codec.u8.convert((i) => tryAsU8(i), (o) => {
166931
167062
  if (o === PvmBackend.BuiltIn) {
@@ -166936,12 +167067,16 @@ class ImporterConfig {
166936
167067
  }
166937
167068
  throw new Error(`Invalid PvmBackend: ${o}`);
166938
167069
  }),
167070
+ dummyFinalityDepth: codec_codec.u16,
166939
167071
  });
166940
- static create({ pvm }) {
166941
- return new ImporterConfig(pvm);
167072
+ static create({ pvm, dummyFinalityDepth }) {
167073
+ return new ImporterConfig(pvm, dummyFinalityDepth);
166942
167074
  }
166943
- constructor(pvm) {
167075
+ constructor(pvm,
167076
+ /** Dummy finality depth. 0 means disabled, any positive value enables dummy finality with that depth. */
167077
+ dummyFinalityDepth = numbers_tryAsU16(0)) {
166944
167078
  this.pvm = pvm;
167079
+ this.dummyFinalityDepth = dummyFinalityDepth;
166945
167080
  }
166946
167081
  }
166947
167082
 
@@ -170304,6 +170439,7 @@ async function node_main_main(config, withRelPath, telemetry) {
170304
170439
  ...baseConfig,
170305
170440
  workerParams: ImporterConfig.create({
170306
170441
  pvm: config.pvmBackend,
170442
+ dummyFinalityDepth: numbers_tryAsU16(config.devValidatorIndex !== null ? 100 : 0),
170307
170443
  }),
170308
170444
  };
170309
170445
  const importerConfig = isInMemory
@@ -170498,6 +170634,7 @@ const initNetwork = async (importer, params, baseConfig, genesisHeaderHash, netw
170498
170634
 
170499
170635
 
170500
170636
 
170637
+
170501
170638
  const zeroHash = bytes_Bytes.zero(hash_HASH_SIZE).asOpaque();
170502
170639
  async function mainImporter(config, withRelPath, options = {}) {
170503
170640
  await initAll();
@@ -170510,23 +170647,23 @@ async function mainImporter(config, withRelPath, options = {}) {
170510
170647
  const blake2b = await blake2b_Blake2b.createHasher();
170511
170648
  const nodeName = config.nodeName;
170512
170649
  const { dbPath, genesisHeaderHash } = getDatabasePath(blake2b, config.nodeName, config.node.chainSpec.genesisHeader, withRelPath(config.node.databaseBasePath ?? "<in-memory>"));
170650
+ const workerParams = ImporterConfig.create({
170651
+ pvm: config.pvmBackend,
170652
+ dummyFinalityDepth: numbers_tryAsU16(options.dummyFinalityDepth ?? 0),
170653
+ });
170513
170654
  const workerConfig = config.node.databaseBasePath === undefined
170514
170655
  ? InMemWorkerConfig.new({
170515
170656
  nodeName,
170516
170657
  chainSpec,
170517
170658
  blake2b,
170518
- workerParams: {
170519
- pvm: config.pvmBackend,
170520
- },
170659
+ workerParams,
170521
170660
  })
170522
170661
  : config_LmdbWorkerConfig.new({
170523
170662
  nodeName,
170524
170663
  chainSpec,
170525
170664
  blake2b,
170526
170665
  dbPath,
170527
- workerParams: {
170528
- pvm: config.pvmBackend,
170529
- },
170666
+ workerParams,
170530
170667
  });
170531
170668
  // Initialize the database with genesis state and block if there isn't one.
170532
170669
  common_logger.info `🛢️ Opening database at ${dbPath}`;
@@ -170635,7 +170772,10 @@ async function mainFuzz(fuzzConfig, withRelPath) {
170635
170772
  },
170636
170773
  ancestry,
170637
170774
  network: null,
170638
- }, withRelPath, { initGenesisFromAncestry: fuzzConfig.initGenesisFromAncestry });
170775
+ }, withRelPath, {
170776
+ initGenesisFromAncestry: fuzzConfig.initGenesisFromAncestry,
170777
+ dummyFinalityDepth: 10_000,
170778
+ });
170639
170779
  runningNode = newNode;
170640
170780
  return await newNode.getBestStateRootHash();
170641
170781
  },