@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.
@@ -28975,6 +28975,113 @@ class TransitionHasher {
28975
28975
 
28976
28976
 
28977
28977
 
28978
+ ;// CONCATENATED MODULE: ./packages/workers/importer/finality.ts
28979
+
28980
+ const finality_logger = Logger.new(import.meta.filename, "finality");
28981
+ /**
28982
+ * A simple finalizer that considers a block finalized when N blocks
28983
+ * have been built on top of it.
28984
+ *
28985
+ * Maintains an array of fork chains starting from the last finalized block.
28986
+ * When any chain reaches `depth`, the earliest blocks are finalized and
28987
+ * dead forks (branching from before the finalized point) are discarded.
28988
+ */
28989
+ class DummyFinalizer {
28990
+ blocks;
28991
+ depth;
28992
+ lastFinalizedHash;
28993
+ unfinalized = [];
28994
+ static create(blocks, depth) {
28995
+ return new DummyFinalizer(blocks, depth);
28996
+ }
28997
+ constructor(blocks, depth) {
28998
+ this.blocks = blocks;
28999
+ this.depth = depth;
29000
+ this.lastFinalizedHash = blocks.getBestHeaderHash();
29001
+ finality_logger.info `🦭 Dummy Finalizer running with depth=${depth}`;
29002
+ }
29003
+ onBlockImported(headerHash) {
29004
+ const header = this.blocks.getHeader(headerHash);
29005
+ if (header === null) {
29006
+ return null;
29007
+ }
29008
+ const parentHash = header.parentHeaderHash.materialize();
29009
+ // Try to attach the block to an existing chain at its tip.
29010
+ let extendedChain = null;
29011
+ for (const chain of this.unfinalized) {
29012
+ if (chain.length > 0 && chain[chain.length - 1].isEqualTo(parentHash)) {
29013
+ chain.push(headerHash);
29014
+ extendedChain = chain;
29015
+ break;
29016
+ }
29017
+ }
29018
+ if (extendedChain === null) {
29019
+ if (this.lastFinalizedHash.isEqualTo(parentHash)) {
29020
+ // Parent is the finalized block — start a new chain.
29021
+ const newChain = [headerHash];
29022
+ this.unfinalized.push(newChain);
29023
+ extendedChain = newChain;
29024
+ }
29025
+ else {
29026
+ // Fork from the middle of an existing chain — copy the prefix and branch.
29027
+ for (const chain of this.unfinalized) {
29028
+ const forkIdx = chain.findIndex((h) => h.isEqualTo(parentHash));
29029
+ if (forkIdx !== -1) {
29030
+ const newChain = [...chain.slice(0, forkIdx + 1), headerHash];
29031
+ this.unfinalized.push(newChain);
29032
+ extendedChain = newChain;
29033
+ break;
29034
+ }
29035
+ }
29036
+ }
29037
+ }
29038
+ if (extendedChain === null) {
29039
+ // Orphan block — cannot attach to any known chain.
29040
+ return null;
29041
+ }
29042
+ // Check if the extended chain is long enough to trigger finality.
29043
+ // A chain of length N has N-1 blocks built on top of chain[0].
29044
+ // We finalize chain[0] when there are >= depth blocks after it,
29045
+ // i.e. chain.length > depth.
29046
+ if (extendedChain.length <= this.depth) {
29047
+ return null;
29048
+ }
29049
+ // The newly finalized block sits at index (length - 1 - depth).
29050
+ const finalizedIdx = extendedChain.length - 1 - this.depth;
29051
+ const finalizedHash = extendedChain[finalizedIdx];
29052
+ // Collect prunable hashes and rebuild the unfinalized set.
29053
+ // The previously finalized block's state is no longer needed.
29054
+ const prunable = [this.lastFinalizedHash];
29055
+ const newUnfinalized = [];
29056
+ for (const chain of this.unfinalized) {
29057
+ // Find the finalized block in this chain.
29058
+ const finIdx = chain.findIndex((h) => h.isEqualTo(finalizedHash));
29059
+ if (finIdx !== -1) {
29060
+ // Chain contains the finalized block — it's still alive.
29061
+ // Prune states for blocks before the finalized block.
29062
+ for (let i = 0; i < finIdx; i++) {
29063
+ prunable.push(chain[i]);
29064
+ }
29065
+ // Keep blocks after the finalized block.
29066
+ const remaining = chain.slice(finIdx + 1);
29067
+ if (remaining.length > 0) {
29068
+ newUnfinalized.push(remaining);
29069
+ }
29070
+ }
29071
+ else {
29072
+ // Dead fork — branches from a block that is no longer finalized.
29073
+ // Prune all its states.
29074
+ for (const h of chain) {
29075
+ prunable.push(h);
29076
+ }
29077
+ }
29078
+ }
29079
+ this.lastFinalizedHash = finalizedHash;
29080
+ this.unfinalized = newUnfinalized;
29081
+ return { finalizedHash, prunableStateHashes: prunable };
29082
+ }
29083
+ }
29084
+
28978
29085
  // EXTERNAL MODULE: ./node_modules/@opentelemetry/api/build/src/index.js
28979
29086
  var src = __nccwpck_require__(5555);
28980
29087
  ;// CONCATENATED MODULE: ./packages/workers/importer/metrics.ts
@@ -29201,6 +29308,14 @@ class Importer {
29201
29308
  logger.log `${timerDb()}`;
29202
29309
  // finally update the best block
29203
29310
  await this.blocks.setBestHeaderHash(headerHash);
29311
+ // check for finality and prune old states
29312
+ const finality = this.options.finalizer?.onBlockImported(headerHash) ?? null;
29313
+ if (finality !== null) {
29314
+ this.logger.info `🦭 Finalized block: ${finality.finalizedHash} (${finality.prunableStateHashes.length} to prune)`;
29315
+ for (const hash of finality.prunableStateHashes) {
29316
+ this.states.markUnused(hash);
29317
+ }
29318
+ }
29204
29319
  return result_Result.ok(new WithHash(headerHash, block.header.view()));
29205
29320
  }
29206
29321
  getBestStateRootHash() {
@@ -29242,6 +29357,7 @@ function extractTimeSlot(block) {
29242
29357
 
29243
29358
 
29244
29359
 
29360
+
29245
29361
  const main_logger = Logger.new(import.meta.filename, "importer");
29246
29362
  const keccakHasher = KeccakHasher.create();
29247
29363
  const blake2b = Blake2b.createHasher();
@@ -29251,8 +29367,13 @@ async function createImporter(config, options = {}) {
29251
29367
  const pvm = config.workerParams.pvm;
29252
29368
  const blocks = db.getBlocksDb();
29253
29369
  const states = db.getStatesDb();
29370
+ const dummyFinalityDepth = config.workerParams.dummyFinalityDepth ?? 0;
29371
+ const finalizer = dummyFinalityDepth > 0 ? DummyFinalizer.create(blocks, dummyFinalityDepth) : undefined;
29254
29372
  const hasher = new TransitionHasher(await keccakHasher, await blake2b);
29255
- const importer = new Importer(chainSpec, pvm, hasher, main_logger, blocks, states, options);
29373
+ const importer = new Importer(chainSpec, pvm, hasher, main_logger, blocks, states, {
29374
+ ...options,
29375
+ finalizer,
29376
+ });
29256
29377
  return {
29257
29378
  importer,
29258
29379
  db,
@@ -29570,6 +29691,7 @@ const protocol = createProtocol("importer", {
29570
29691
  });
29571
29692
  class ImporterConfig {
29572
29693
  pvm;
29694
+ dummyFinalityDepth;
29573
29695
  static Codec = codec_codec.Class(ImporterConfig, {
29574
29696
  pvm: codec_codec.u8.convert((i) => tryAsU8(i), (o) => {
29575
29697
  if (o === PvmBackend.BuiltIn) {
@@ -29580,12 +29702,16 @@ class ImporterConfig {
29580
29702
  }
29581
29703
  throw new Error(`Invalid PvmBackend: ${o}`);
29582
29704
  }),
29705
+ dummyFinalityDepth: codec_codec.u16,
29583
29706
  });
29584
- static create({ pvm }) {
29585
- return new ImporterConfig(pvm);
29707
+ static create({ pvm, dummyFinalityDepth }) {
29708
+ return new ImporterConfig(pvm, dummyFinalityDepth);
29586
29709
  }
29587
- constructor(pvm) {
29710
+ constructor(pvm,
29711
+ /** Dummy finality depth. 0 means disabled, any positive value enables dummy finality with that depth. */
29712
+ dummyFinalityDepth = numbers_tryAsU16(0)) {
29588
29713
  this.pvm = pvm;
29714
+ this.dummyFinalityDepth = dummyFinalityDepth;
29589
29715
  }
29590
29716
  }
29591
29717