@typeberry/lib 0.5.8-2073cb2 → 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.
Files changed (32) hide show
  1. package/package.json +1 -1
  2. package/packages/core/pvm-host-calls/host-calls-executor.js +3 -3
  3. package/packages/jam/database/serialized-states-db.d.ts +1 -0
  4. package/packages/jam/database/serialized-states-db.d.ts.map +1 -1
  5. package/packages/jam/database/serialized-states-db.js +3 -0
  6. package/packages/jam/database/states.d.ts +3 -0
  7. package/packages/jam/database/states.d.ts.map +1 -1
  8. package/packages/jam/database/states.js +3 -0
  9. package/packages/jam/database-lmdb/states.d.ts +1 -0
  10. package/packages/jam/database-lmdb/states.d.ts.map +1 -1
  11. package/packages/jam/database-lmdb/states.js +3 -0
  12. package/packages/jam/node/main-fuzz.d.ts.map +1 -1
  13. package/packages/jam/node/main-fuzz.js +4 -1
  14. package/packages/jam/node/main-importer.d.ts +1 -0
  15. package/packages/jam/node/main-importer.d.ts.map +1 -1
  16. package/packages/jam/node/main-importer.js +8 -7
  17. package/packages/jam/node/main.d.ts.map +1 -1
  18. package/packages/jam/node/main.js +1 -0
  19. package/packages/workers/importer/finality.d.ts +32 -0
  20. package/packages/workers/importer/finality.d.ts.map +1 -0
  21. package/packages/workers/importer/finality.js +105 -0
  22. package/packages/workers/importer/finality.test.d.ts +2 -0
  23. package/packages/workers/importer/finality.test.d.ts.map +1 -0
  24. package/packages/workers/importer/finality.test.js +369 -0
  25. package/packages/workers/importer/importer.d.ts +2 -0
  26. package/packages/workers/importer/importer.d.ts.map +1 -1
  27. package/packages/workers/importer/importer.js +8 -0
  28. package/packages/workers/importer/main.d.ts.map +1 -1
  29. package/packages/workers/importer/main.js +7 -1
  30. package/packages/workers/importer/protocol.d.ts +7 -3
  31. package/packages/workers/importer/protocol.d.ts.map +1 -1
  32. package/packages/workers/importer/protocol.js +9 -4
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@typeberry/lib",
3
- "version": "0.5.8-2073cb2",
3
+ "version": "0.5.8-b3da767",
4
4
  "description": "Typeberry Library",
5
5
  "main": "./bin/lib/index.js",
6
6
  "types": "./bin/lib/index.d.ts",
@@ -52,7 +52,7 @@ export class HostCallsExecutor {
52
52
  this.ioTracer?.logPanic(pvmInstance.getExitParam() ?? 0, pc, gas, registers);
53
53
  return { consumedGas, status: ReturnStatus.PANIC };
54
54
  }
55
- async execute(pvmInstance) {
55
+ async execute(pvmInstance, initialPc) {
56
56
  const ioTracker = this.ioTracer?.tracker() ?? null;
57
57
  const registers = new HostCallRegisters(pvmInstance.registers.getAllEncoded());
58
58
  registers.ioTracker = ioTracker;
@@ -60,7 +60,7 @@ export class HostCallsExecutor {
60
60
  memory.ioTracker = ioTracker;
61
61
  const gas = pvmInstance.gas;
62
62
  // log start of execution (note the PVM initialisation should be logged already)
63
- this.ioTracer?.logStart(pvmInstance.getPC(), pvmInstance.gas.get(), registers);
63
+ this.ioTracer?.logStart(initialPc, pvmInstance.gas.get(), registers);
64
64
  for (;;) {
65
65
  // execute program as much as we can
66
66
  pvmInstance.runProgram();
@@ -120,7 +120,7 @@ export class HostCallsExecutor {
120
120
  pvmInstance.resetJam(program, args, initialPc, initialGas);
121
121
  try {
122
122
  this.ioTracer?.logProgram(program, args);
123
- return await this.execute(pvmInstance);
123
+ return await this.execute(pvmInstance, initialPc);
124
124
  }
125
125
  finally {
126
126
  this.pvmInstanceManager.releaseInstance(pvmInstance);
@@ -27,6 +27,7 @@ export declare class InMemorySerializedStates implements StatesDb<SerializedStat
27
27
  getStateRoot(state: SerializedState<LeafDb>): Promise<StateRootHash>;
28
28
  updateAndSetState(header: HeaderHash, state: SerializedState<LeafDb>, update: Partial<State & ServicesUpdate>): Promise<Result<OK, StateUpdateError>>;
29
29
  getState(header: HeaderHash): SerializedState<LeafDb> | null;
30
+ markUnused(header: HeaderHash): void;
30
31
  close(): Promise<void>;
31
32
  }
32
33
  //# sourceMappingURL=serialized-states-db.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"serialized-states-db.d.ts","sourceRoot":"","sources":["../../../../../packages/jam/database/serialized-states-db.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAGlE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAC1C,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,2BAA2B,CAAC;AACvD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kCAAkC,CAAC;AACvE,OAAO,EACL,eAAe,EACf,KAAK,YAAY,EAGlB,MAAM,gCAAgC,CAAC;AAExC,OAAO,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAEtC,OAAO,KAAK,EAAE,YAAY,EAAE,QAAQ,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAE5E,qCAAqC;AACrC,MAAM,MAAM,kBAAkB,GAAG,QAAQ,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC,GAAG,YAAY,CAAC,YAAY,CAAC,CAAC;AAEhG,sCAAsC;AACtC,qBAAa,wBAAyB,YAAW,QAAQ,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC,EAAE,YAAY,CAAC,YAAY,CAAC;IAc1G,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAd1B,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAyE;IAC5F,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA8D;WAE1E,GAAG,CAAC,EAAE,SAAS,EAAE,EAAE;QAAE,SAAS,EAAE,SAAS,CAAA;KAAE;IAKxD,MAAM,CAAC,UAAU,CAAC,EAAE,SAAS,EAAE,OAAO,EAAE,EAAE;QAAE,SAAS,EAAE,SAAS,CAAC;QAAC,OAAO,EAAE,OAAO,CAAA;KAAE;IAIpF,OAAO;IAKD,kBAAkB,CAAC,UAAU,EAAE,UAAU,EAAE,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,gBAAgB,CAAC,CAAC;IAiBxG,YAAY,CAAC,KAAK,EAAE,eAAe,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,aAAa,CAAC;IAIpE,iBAAiB,CACrB,MAAM,EAAE,UAAU,EAClB,KAAK,EAAE,eAAe,CAAC,MAAM,CAAC,EAC9B,MAAM,EAAE,OAAO,CAAC,KAAK,GAAG,cAAc,CAAC,GACtC,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,gBAAgB,CAAC,CAAC;IAsBxC,QAAQ,CAAC,MAAM,EAAE,UAAU,GAAG,eAAe,CAAC,MAAM,CAAC,GAAG,IAAI;IAkBtD,KAAK;CACZ"}
1
+ {"version":3,"file":"serialized-states-db.d.ts","sourceRoot":"","sources":["../../../../../packages/jam/database/serialized-states-db.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAGlE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAC1C,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,2BAA2B,CAAC;AACvD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kCAAkC,CAAC;AACvE,OAAO,EACL,eAAe,EACf,KAAK,YAAY,EAGlB,MAAM,gCAAgC,CAAC;AAExC,OAAO,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAEtC,OAAO,KAAK,EAAE,YAAY,EAAE,QAAQ,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAE5E,qCAAqC;AACrC,MAAM,MAAM,kBAAkB,GAAG,QAAQ,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC,GAAG,YAAY,CAAC,YAAY,CAAC,CAAC;AAEhG,sCAAsC;AACtC,qBAAa,wBAAyB,YAAW,QAAQ,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC,EAAE,YAAY,CAAC,YAAY,CAAC;IAc1G,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAd1B,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAyE;IAC5F,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA8D;WAE1E,GAAG,CAAC,EAAE,SAAS,EAAE,EAAE;QAAE,SAAS,EAAE,SAAS,CAAA;KAAE;IAKxD,MAAM,CAAC,UAAU,CAAC,EAAE,SAAS,EAAE,OAAO,EAAE,EAAE;QAAE,SAAS,EAAE,SAAS,CAAC;QAAC,OAAO,EAAE,OAAO,CAAA;KAAE;IAIpF,OAAO;IAKD,kBAAkB,CAAC,UAAU,EAAE,UAAU,EAAE,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,gBAAgB,CAAC,CAAC;IAiBxG,YAAY,CAAC,KAAK,EAAE,eAAe,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,aAAa,CAAC;IAIpE,iBAAiB,CACrB,MAAM,EAAE,UAAU,EAClB,KAAK,EAAE,eAAe,CAAC,MAAM,CAAC,EAC9B,MAAM,EAAE,OAAO,CAAC,KAAK,GAAG,cAAc,CAAC,GACtC,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,gBAAgB,CAAC,CAAC;IAsBxC,QAAQ,CAAC,MAAM,EAAE,UAAU,GAAG,eAAe,CAAC,MAAM,CAAC,GAAG,IAAI;IAkB5D,UAAU,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI;IAI9B,KAAK;CACZ"}
@@ -70,5 +70,8 @@ export class InMemorySerializedStates {
70
70
  });
71
71
  return SerializedState.new(this.spec, this.blake2b, leafDb);
72
72
  }
73
+ markUnused(header) {
74
+ this.db.delete(header);
75
+ }
73
76
  async close() { }
74
77
  }
@@ -33,6 +33,8 @@ export interface StatesDb<T extends State = State> {
33
33
  updateAndSetState(header: HeaderHash, state: T, update: Partial<State & ServicesUpdate>): Promise<Result<OK, StateUpdateError>>;
34
34
  /** Retrieve posterior state of given header. */
35
35
  getState(header: HeaderHash): T | null;
36
+ /** Mark state as no longer needed. Backend may remove it asynchronously. */
37
+ markUnused(header: HeaderHash): void;
36
38
  /** Close the database and free resources. */
37
39
  close(): Promise<void>;
38
40
  }
@@ -46,6 +48,7 @@ export declare class InMemoryStates implements StatesDb<InMemoryState> {
46
48
  /** Insert a full state into the database. */
47
49
  insertInitialState(headerHash: HeaderHash, state: InMemoryState): Promise<Result<OK, StateUpdateError>>;
48
50
  getState(headerHash: HeaderHash): InMemoryState | null;
51
+ markUnused(header: HeaderHash): void;
49
52
  close(): Promise<void>;
50
53
  }
51
54
  //# sourceMappingURL=states.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"states.d.ts","sourceRoot":"","sources":["../../../../../packages/jam/database/states.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAElE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAEnD,OAAO,EAAE,aAAa,EAAE,KAAK,cAAc,EAAE,KAAK,KAAK,EAAe,MAAM,kBAAkB,CAAC;AAE/F,OAAO,EAAe,EAAE,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAE3D,0DAA0D;AAC1D,oBAAY,gBAAgB;IAC1B,oDAAoD;IACpD,QAAQ,IAAI;IACZ,iDAAiD;IACjD,MAAM,IAAI;CACX;AAED,wFAAwF;AACxF,MAAM,WAAW,YAAY,CAAC,CAAC,GAAG,KAAK;IACrC,qEAAqE;IACrE,kBAAkB,CAAC,UAAU,EAAE,UAAU,EAAE,YAAY,EAAE,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,gBAAgB,CAAC,CAAC,CAAC;CACpG;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,QAAQ,CAAC,CAAC,SAAS,KAAK,GAAG,KAAK;IAC/C,4CAA4C;IAC5C,YAAY,CAAC,KAAK,EAAE,CAAC,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;IAE/C;;;;OAIG;IACH,iBAAiB,CACf,MAAM,EAAE,UAAU,EAClB,KAAK,EAAE,CAAC,EACR,MAAM,EAAE,OAAO,CAAC,KAAK,GAAG,cAAc,CAAC,GACtC,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,gBAAgB,CAAC,CAAC,CAAC;IAEzC,gDAAgD;IAChD,QAAQ,CAAC,MAAM,EAAE,UAAU,GAAG,CAAC,GAAG,IAAI,CAAC;IAEvC,6CAA6C;IAC7C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED,qBAAa,cAAe,YAAW,QAAQ,CAAC,aAAa,CAAC;IAIhD,OAAO,CAAC,QAAQ,CAAC,IAAI;IAHjC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAmE;IACtF,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAmB;gBAEd,IAAI,EAAE,SAAS;IAItC,iBAAiB,CACrB,UAAU,EAAE,UAAU,EACtB,KAAK,EAAE,aAAa,EACpB,MAAM,EAAE,OAAO,CAAC,KAAK,GAAG,cAAc,CAAC,GACtC,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,gBAAgB,CAAC,CAAC;IAgBlC,YAAY,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC;IAKhE,6CAA6C;IACvC,kBAAkB,CAAC,UAAU,EAAE,UAAU,EAAE,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,gBAAgB,CAAC,CAAC;IAM7G,QAAQ,CAAC,UAAU,EAAE,UAAU,GAAG,aAAa,GAAG,IAAI;IAShD,KAAK;CACZ"}
1
+ {"version":3,"file":"states.d.ts","sourceRoot":"","sources":["../../../../../packages/jam/database/states.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAElE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAEnD,OAAO,EAAE,aAAa,EAAE,KAAK,cAAc,EAAE,KAAK,KAAK,EAAe,MAAM,kBAAkB,CAAC;AAE/F,OAAO,EAAe,EAAE,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAE3D,0DAA0D;AAC1D,oBAAY,gBAAgB;IAC1B,oDAAoD;IACpD,QAAQ,IAAI;IACZ,iDAAiD;IACjD,MAAM,IAAI;CACX;AAED,wFAAwF;AACxF,MAAM,WAAW,YAAY,CAAC,CAAC,GAAG,KAAK;IACrC,qEAAqE;IACrE,kBAAkB,CAAC,UAAU,EAAE,UAAU,EAAE,YAAY,EAAE,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,gBAAgB,CAAC,CAAC,CAAC;CACpG;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,QAAQ,CAAC,CAAC,SAAS,KAAK,GAAG,KAAK;IAC/C,4CAA4C;IAC5C,YAAY,CAAC,KAAK,EAAE,CAAC,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;IAE/C;;;;OAIG;IACH,iBAAiB,CACf,MAAM,EAAE,UAAU,EAClB,KAAK,EAAE,CAAC,EACR,MAAM,EAAE,OAAO,CAAC,KAAK,GAAG,cAAc,CAAC,GACtC,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,gBAAgB,CAAC,CAAC,CAAC;IAEzC,gDAAgD;IAChD,QAAQ,CAAC,MAAM,EAAE,UAAU,GAAG,CAAC,GAAG,IAAI,CAAC;IAEvC,4EAA4E;IAC5E,UAAU,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI,CAAC;IAErC,6CAA6C;IAC7C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED,qBAAa,cAAe,YAAW,QAAQ,CAAC,aAAa,CAAC;IAIhD,OAAO,CAAC,QAAQ,CAAC,IAAI;IAHjC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAmE;IACtF,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAmB;gBAEd,IAAI,EAAE,SAAS;IAItC,iBAAiB,CACrB,UAAU,EAAE,UAAU,EACtB,KAAK,EAAE,aAAa,EACpB,MAAM,EAAE,OAAO,CAAC,KAAK,GAAG,cAAc,CAAC,GACtC,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,gBAAgB,CAAC,CAAC;IAgBlC,YAAY,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC;IAKhE,6CAA6C;IACvC,kBAAkB,CAAC,UAAU,EAAE,UAAU,EAAE,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,gBAAgB,CAAC,CAAC;IAM7G,QAAQ,CAAC,UAAU,EAAE,UAAU,GAAG,aAAa,GAAG,IAAI;IAStD,UAAU,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI;IAI9B,KAAK;CACZ"}
@@ -50,5 +50,8 @@ export class InMemoryStates {
50
50
  }
51
51
  return InMemoryState.copyFrom(this.spec, state, state.intoServicesData());
52
52
  }
53
+ markUnused(header) {
54
+ this.db.delete(header);
55
+ }
53
56
  async close() { }
54
57
  }
@@ -67,6 +67,7 @@ export declare class LmdbStates implements StatesDb<SerializedState<LeafDb>>, In
67
67
  updateAndSetState(headerHash: HeaderHash, state: SerializedState<LeafDb>, update: Partial<State & ServicesUpdate>): Promise<Result<OK, StateUpdateError>>;
68
68
  getStateRoot(state: SerializedState<LeafDb>): Promise<StateRootHash>;
69
69
  getState(root: HeaderHash): SerializedState<LeafDb> | null;
70
+ markUnused(header: HeaderHash): void;
70
71
  close(): Promise<void>;
71
72
  }
72
73
  //# sourceMappingURL=states.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"states.d.ts","sourceRoot":"","sources":["../../../../../packages/jam/database-lmdb/states.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAGlE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,EAAE,KAAK,QAAQ,EAAE,gBAAgB,EAAe,MAAM,qBAAqB,CAAC;AAC9G,OAAO,KAAK,EAAE,OAAO,EAAiB,MAAM,iBAAiB,CAAC;AAE9D,OAAO,KAAK,EAAE,cAAc,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAC9D,OAAO,KAAK,EAAE,YAAY,EAAY,MAAM,gCAAgC,CAAC;AAC7E,OAAO,EAAE,eAAe,EAAgD,MAAM,gCAAgC,CAAC;AAG/G,OAAO,EAAE,EAAE,EAAE,MAAM,EAAkB,MAAM,kBAAkB,CAAC;AAC9D,OAAO,KAAK,EAAE,QAAQ,EAAS,MAAM,WAAW,CAAC;AAGjD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AAEH,qBAAa,UAAW,YAAW,QAAQ,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC,EAAE,YAAY,CAAC,YAAY,CAAC;IAK5F,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,IAAI;IANvB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAQ;IAC/B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAQ;gBAGZ,IAAI,EAAE,SAAS,EACf,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,QAAQ;IAM3B,kBAAkB,CACtB,UAAU,EAAE,UAAU,EACtB,eAAe,EAAE,YAAY,GAC5B,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,gBAAgB,CAAC,CAAC;YAQ1B,eAAe;IA0BvB,iBAAiB,CACrB,UAAU,EAAE,UAAU,EACtB,KAAK,EAAE,eAAe,CAAC,MAAM,CAAC,EAC9B,MAAM,EAAE,OAAO,CAAC,KAAK,GAAG,cAAc,CAAC,GACtC,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,gBAAgB,CAAC,CAAC;IAWlC,YAAY,CAAC,KAAK,EAAE,eAAe,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,aAAa,CAAC;IAI1E,QAAQ,CAAC,IAAI,EAAE,UAAU,GAAG,eAAe,CAAC,MAAM,CAAC,GAAG,IAAI;IAsBpD,KAAK;CAGZ"}
1
+ {"version":3,"file":"states.d.ts","sourceRoot":"","sources":["../../../../../packages/jam/database-lmdb/states.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAGlE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,EAAE,KAAK,QAAQ,EAAE,gBAAgB,EAAe,MAAM,qBAAqB,CAAC;AAC9G,OAAO,KAAK,EAAE,OAAO,EAAiB,MAAM,iBAAiB,CAAC;AAE9D,OAAO,KAAK,EAAE,cAAc,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAC9D,OAAO,KAAK,EAAE,YAAY,EAAY,MAAM,gCAAgC,CAAC;AAC7E,OAAO,EAAE,eAAe,EAAgD,MAAM,gCAAgC,CAAC;AAG/G,OAAO,EAAE,EAAE,EAAE,MAAM,EAAkB,MAAM,kBAAkB,CAAC;AAC9D,OAAO,KAAK,EAAE,QAAQ,EAAS,MAAM,WAAW,CAAC;AAGjD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AAEH,qBAAa,UAAW,YAAW,QAAQ,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC,EAAE,YAAY,CAAC,YAAY,CAAC;IAK5F,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,IAAI;IANvB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAQ;IAC/B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAQ;gBAGZ,IAAI,EAAE,SAAS,EACf,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,QAAQ;IAM3B,kBAAkB,CACtB,UAAU,EAAE,UAAU,EACtB,eAAe,EAAE,YAAY,GAC5B,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,gBAAgB,CAAC,CAAC;YAQ1B,eAAe;IA0BvB,iBAAiB,CACrB,UAAU,EAAE,UAAU,EACtB,KAAK,EAAE,eAAe,CAAC,MAAM,CAAC,EAC9B,MAAM,EAAE,OAAO,CAAC,KAAK,GAAG,cAAc,CAAC,GACtC,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,gBAAgB,CAAC,CAAC;IAWlC,YAAY,CAAC,KAAK,EAAE,eAAe,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,aAAa,CAAC;IAI1E,QAAQ,CAAC,IAAI,EAAE,UAAU,GAAG,eAAe,CAAC,MAAM,CAAC,GAAG,IAAI;IAsB1D,UAAU,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI;IAI9B,KAAK;CAGZ"}
@@ -125,6 +125,9 @@ export class LmdbStates {
125
125
  }
126
126
  return SerializedState.new(this.spec, this.blake2b, leafDbResult.ok);
127
127
  }
128
+ markUnused(header) {
129
+ this.states.remove(header.raw);
130
+ }
128
131
  async close() {
129
132
  await Promise.all([this.states.close(), this.values.close()]);
130
133
  }
@@ -1 +1 @@
1
- {"version":3,"file":"main-fuzz.d.ts","sourceRoot":"","sources":["../../../../../packages/jam/node/main-fuzz.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,WAAW,EAAmB,MAAM,oBAAoB,CAAC;AACvE,OAAO,EAAE,EAAE,IAAI,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAMrD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAIjD,MAAM,MAAM,UAAU,GAAG;IACvB,OAAO,EAAE,WAAW,CAAC;IACrB,aAAa,EAAE,SAAS,CAAC;IACzB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,uBAAuB,EAAE,OAAO,CAAC;CAClC,CAAC;AAIF,wBAAgB,cAAc;;;;EAM7B;AAED,wBAAsB,QAAQ,CAAC,UAAU,EAAE,UAAU,EAAE,WAAW,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,uBAoExF"}
1
+ {"version":3,"file":"main-fuzz.d.ts","sourceRoot":"","sources":["../../../../../packages/jam/node/main-fuzz.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,WAAW,EAAmB,MAAM,oBAAoB,CAAC;AACvE,OAAO,EAAE,EAAE,IAAI,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAMrD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAIjD,MAAM,MAAM,UAAU,GAAG;IACvB,OAAO,EAAE,WAAW,CAAC;IACrB,aAAa,EAAE,SAAS,CAAC;IACzB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,uBAAuB,EAAE,OAAO,CAAC;CAClC,CAAC;AAIF,wBAAgB,cAAc;;;;EAM7B;AAED,wBAAsB,QAAQ,CAAC,UAAU,EAAE,UAAU,EAAE,WAAW,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,uBAuExF"}
@@ -66,7 +66,10 @@ export async function mainFuzz(fuzzConfig, withRelPath) {
66
66
  },
67
67
  ancestry,
68
68
  network: null,
69
- }, withRelPath, { initGenesisFromAncestry: fuzzConfig.initGenesisFromAncestry });
69
+ }, withRelPath, {
70
+ initGenesisFromAncestry: fuzzConfig.initGenesisFromAncestry,
71
+ dummyFinalityDepth: 10_000,
72
+ });
70
73
  runningNode = newNode;
71
74
  return await newNode.getBestStateRootHash();
72
75
  },
@@ -2,6 +2,7 @@ import type { JamConfig } from "./jam-config.js";
2
2
  import type { NodeApi } from "./main.js";
3
3
  export type ImporterOptions = {
4
4
  initGenesisFromAncestry?: boolean;
5
+ dummyFinalityDepth?: number;
5
6
  };
6
7
  export declare function mainImporter(config: JamConfig, withRelPath: (v: string) => string, options?: ImporterOptions): Promise<NodeApi>;
7
8
  //# sourceMappingURL=main-importer.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"main-importer.d.ts","sourceRoot":"","sources":["../../../../../packages/jam/node/main-importer.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAIzC,MAAM,MAAM,eAAe,GAAG;IAC5B,uBAAuB,CAAC,EAAE,OAAO,CAAC;CACnC,CAAC;AAEF,wBAAsB,YAAY,CAChC,MAAM,EAAE,SAAS,EACjB,WAAW,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,EAClC,OAAO,GAAE,eAAoB,GAC5B,OAAO,CAAC,OAAO,CAAC,CA+ElB"}
1
+ {"version":3,"file":"main-importer.d.ts","sourceRoot":"","sources":["../../../../../packages/jam/node/main-importer.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAIzC,MAAM,MAAM,eAAe,GAAG;IAC5B,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAClC,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B,CAAC;AAEF,wBAAsB,YAAY,CAChC,MAAM,EAAE,SAAS,EACjB,WAAW,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,EAClC,OAAO,GAAE,eAAoB,GAC5B,OAAO,CAAC,OAAO,CAAC,CA+ElB"}
@@ -2,7 +2,8 @@ import { Bytes } from "#@typeberry/bytes";
2
2
  import { PvmBackend } from "#@typeberry/config";
3
3
  import { bandersnatch, initWasm } from "#@typeberry/crypto";
4
4
  import { Blake2b, HASH_SIZE } from "#@typeberry/hash";
5
- import { createImporter } from "#@typeberry/importer";
5
+ import { createImporter, ImporterConfig } from "#@typeberry/importer";
6
+ import { tryAsU16 } from "#@typeberry/numbers";
6
7
  import { CURRENT_SUITE, CURRENT_VERSION, Result, resultToString, version } from "#@typeberry/utils";
7
8
  import { InMemWorkerConfig, LmdbWorkerConfig } from "#@typeberry/workers-api-node";
8
9
  import { getChainSpec, getDatabasePath, initializeDatabase, logger } from "./common.js";
@@ -18,23 +19,23 @@ export async function mainImporter(config, withRelPath, options = {}) {
18
19
  const blake2b = await Blake2b.createHasher();
19
20
  const nodeName = config.nodeName;
20
21
  const { dbPath, genesisHeaderHash } = getDatabasePath(blake2b, config.nodeName, config.node.chainSpec.genesisHeader, withRelPath(config.node.databaseBasePath ?? "<in-memory>"));
22
+ const workerParams = ImporterConfig.create({
23
+ pvm: config.pvmBackend,
24
+ dummyFinalityDepth: tryAsU16(options.dummyFinalityDepth ?? 0),
25
+ });
21
26
  const workerConfig = config.node.databaseBasePath === undefined
22
27
  ? InMemWorkerConfig.new({
23
28
  nodeName,
24
29
  chainSpec,
25
30
  blake2b,
26
- workerParams: {
27
- pvm: config.pvmBackend,
28
- },
31
+ workerParams,
29
32
  })
30
33
  : LmdbWorkerConfig.new({
31
34
  nodeName,
32
35
  chainSpec,
33
36
  blake2b,
34
37
  dbPath,
35
- workerParams: {
36
- pvm: config.pvmBackend,
37
- },
38
+ workerParams,
38
39
  });
39
40
  // Initialize the database with genesis state and block if there isn't one.
40
41
  logger.info `🛢️ Opening database at ${dbPath}`;
@@ -1 +1 @@
1
- {"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../../../../packages/jam/node/main.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAc,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEzF,OAAO,EAAE,KAAK,SAAS,EAAc,MAAM,mBAAmB,CAAC;AAe/D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AACnE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAkC,MAAM,EAAW,MAAM,kBAAkB,CAAC;AAKnF,OAAO,KAAK,EAAE,SAAS,EAAiB,MAAM,iBAAiB,CAAC;AAWhE,MAAM,MAAM,OAAO,GAAG;IACpB,SAAS,EAAE,SAAS,CAAC;IACrB,eAAe,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAAC;IAChE,WAAW,CAAC,KAAK,EAAE,SAAS,GAAG,OAAO,CAAC,MAAM,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC,CAAC;IACtE,oBAAoB,IAAI,OAAO,CAAC,aAAa,CAAC,CAAC;IAC/C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB,CAAC;AAEF,wBAAsB,IAAI,CACxB,MAAM,EAAE,SAAS,EACjB,WAAW,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,EAClC,SAAS,EAAE,SAAS,GAAG,IAAI,GAC1B,OAAO,CAAC,OAAO,CAAC,CAuKlB"}
1
+ {"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../../../../packages/jam/node/main.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAc,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEzF,OAAO,EAAE,KAAK,SAAS,EAAc,MAAM,mBAAmB,CAAC;AAe/D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AACnE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAkC,MAAM,EAAW,MAAM,kBAAkB,CAAC;AAKnF,OAAO,KAAK,EAAE,SAAS,EAAiB,MAAM,iBAAiB,CAAC;AAWhE,MAAM,MAAM,OAAO,GAAG;IACpB,SAAS,EAAE,SAAS,CAAC;IACrB,eAAe,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAAC;IAChE,WAAW,CAAC,KAAK,EAAE,SAAS,GAAG,OAAO,CAAC,MAAM,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC,CAAC;IACtE,oBAAoB,IAAI,OAAO,CAAC,aAAa,CAAC,CAAC;IAC/C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB,CAAC;AAEF,wBAAsB,IAAI,CACxB,MAAM,EAAE,SAAS,EACjB,WAAW,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,EAClC,SAAS,EAAE,SAAS,GAAG,IAAI,GAC1B,OAAO,CAAC,OAAO,CAAC,CAwKlB"}
@@ -34,6 +34,7 @@ export async function main(config, withRelPath, telemetry) {
34
34
  ...baseConfig,
35
35
  workerParams: ImporterConfig.create({
36
36
  pvm: config.pvmBackend,
37
+ dummyFinalityDepth: tryAsU16(config.devValidatorIndex !== null ? 100 : 0),
37
38
  }),
38
39
  };
39
40
  const importerConfig = isInMemory
@@ -0,0 +1,32 @@
1
+ import type { HeaderHash } from "#@typeberry/block";
2
+ import type { BlocksDb } from "#@typeberry/database";
3
+ /** Result returned when a new block is finalized. */
4
+ export interface FinalityResult {
5
+ /** The newly finalized block hash. */
6
+ finalizedHash: HeaderHash;
7
+ /** Block hashes whose states are no longer needed and can be pruned. */
8
+ prunableStateHashes: HeaderHash[];
9
+ }
10
+ /** An abstraction for deciding which blocks are finalized. */
11
+ export interface Finalizer {
12
+ /** Called after block import. Returns finality info if a new block was finalized, or null. */
13
+ onBlockImported(headerHash: HeaderHash): FinalityResult | null;
14
+ }
15
+ /**
16
+ * A simple finalizer that considers a block finalized when N blocks
17
+ * have been built on top of it.
18
+ *
19
+ * Maintains an array of fork chains starting from the last finalized block.
20
+ * When any chain reaches `depth`, the earliest blocks are finalized and
21
+ * dead forks (branching from before the finalized point) are discarded.
22
+ */
23
+ export declare class DummyFinalizer implements Finalizer {
24
+ private readonly blocks;
25
+ private readonly depth;
26
+ private lastFinalizedHash;
27
+ private unfinalized;
28
+ static create(blocks: BlocksDb, depth: number): DummyFinalizer;
29
+ private constructor();
30
+ onBlockImported(headerHash: HeaderHash): FinalityResult | null;
31
+ }
32
+ //# sourceMappingURL=finality.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"finality.d.ts","sourceRoot":"","sources":["../../../../../packages/workers/importer/finality.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAKpD,qDAAqD;AACrD,MAAM,WAAW,cAAc;IAC7B,sCAAsC;IACtC,aAAa,EAAE,UAAU,CAAC;IAC1B,wEAAwE;IACxE,mBAAmB,EAAE,UAAU,EAAE,CAAC;CACnC;AAED,8DAA8D;AAC9D,MAAM,WAAW,SAAS;IACxB,8FAA8F;IAC9F,eAAe,CAAC,UAAU,EAAE,UAAU,GAAG,cAAc,GAAG,IAAI,CAAC;CAChE;AAKD;;;;;;;GAOG;AACH,qBAAa,cAAe,YAAW,SAAS;IAS5C,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,KAAK;IATxB,OAAO,CAAC,iBAAiB,CAAa;IACtC,OAAO,CAAC,WAAW,CAAe;IAElC,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,GAAG,cAAc;IAI9D,OAAO;IAQP,eAAe,CAAC,UAAU,EAAE,UAAU,GAAG,cAAc,GAAG,IAAI;CAyF/D"}
@@ -0,0 +1,105 @@
1
+ import { Logger } from "#@typeberry/logger";
2
+ const logger = Logger.new(import.meta.filename, "finality");
3
+ /**
4
+ * A simple finalizer that considers a block finalized when N blocks
5
+ * have been built on top of it.
6
+ *
7
+ * Maintains an array of fork chains starting from the last finalized block.
8
+ * When any chain reaches `depth`, the earliest blocks are finalized and
9
+ * dead forks (branching from before the finalized point) are discarded.
10
+ */
11
+ export class DummyFinalizer {
12
+ blocks;
13
+ depth;
14
+ lastFinalizedHash;
15
+ unfinalized = [];
16
+ static create(blocks, depth) {
17
+ return new DummyFinalizer(blocks, depth);
18
+ }
19
+ constructor(blocks, depth) {
20
+ this.blocks = blocks;
21
+ this.depth = depth;
22
+ this.lastFinalizedHash = blocks.getBestHeaderHash();
23
+ logger.info `🦭 Dummy Finalizer running with depth=${depth}`;
24
+ }
25
+ onBlockImported(headerHash) {
26
+ const header = this.blocks.getHeader(headerHash);
27
+ if (header === null) {
28
+ return null;
29
+ }
30
+ const parentHash = header.parentHeaderHash.materialize();
31
+ // Try to attach the block to an existing chain at its tip.
32
+ let extendedChain = null;
33
+ for (const chain of this.unfinalized) {
34
+ if (chain.length > 0 && chain[chain.length - 1].isEqualTo(parentHash)) {
35
+ chain.push(headerHash);
36
+ extendedChain = chain;
37
+ break;
38
+ }
39
+ }
40
+ if (extendedChain === null) {
41
+ if (this.lastFinalizedHash.isEqualTo(parentHash)) {
42
+ // Parent is the finalized block — start a new chain.
43
+ const newChain = [headerHash];
44
+ this.unfinalized.push(newChain);
45
+ extendedChain = newChain;
46
+ }
47
+ else {
48
+ // Fork from the middle of an existing chain — copy the prefix and branch.
49
+ for (const chain of this.unfinalized) {
50
+ const forkIdx = chain.findIndex((h) => h.isEqualTo(parentHash));
51
+ if (forkIdx !== -1) {
52
+ const newChain = [...chain.slice(0, forkIdx + 1), headerHash];
53
+ this.unfinalized.push(newChain);
54
+ extendedChain = newChain;
55
+ break;
56
+ }
57
+ }
58
+ }
59
+ }
60
+ if (extendedChain === null) {
61
+ // Orphan block — cannot attach to any known chain.
62
+ return null;
63
+ }
64
+ // Check if the extended chain is long enough to trigger finality.
65
+ // A chain of length N has N-1 blocks built on top of chain[0].
66
+ // We finalize chain[0] when there are >= depth blocks after it,
67
+ // i.e. chain.length > depth.
68
+ if (extendedChain.length <= this.depth) {
69
+ return null;
70
+ }
71
+ // The newly finalized block sits at index (length - 1 - depth).
72
+ const finalizedIdx = extendedChain.length - 1 - this.depth;
73
+ const finalizedHash = extendedChain[finalizedIdx];
74
+ // Collect prunable hashes and rebuild the unfinalized set.
75
+ // The previously finalized block's state is no longer needed.
76
+ const prunable = [this.lastFinalizedHash];
77
+ const newUnfinalized = [];
78
+ for (const chain of this.unfinalized) {
79
+ // Find the finalized block in this chain.
80
+ const finIdx = chain.findIndex((h) => h.isEqualTo(finalizedHash));
81
+ if (finIdx !== -1) {
82
+ // Chain contains the finalized block — it's still alive.
83
+ // Prune states for blocks before the finalized block.
84
+ for (let i = 0; i < finIdx; i++) {
85
+ prunable.push(chain[i]);
86
+ }
87
+ // Keep blocks after the finalized block.
88
+ const remaining = chain.slice(finIdx + 1);
89
+ if (remaining.length > 0) {
90
+ newUnfinalized.push(remaining);
91
+ }
92
+ }
93
+ else {
94
+ // Dead fork — branches from a block that is no longer finalized.
95
+ // Prune all its states.
96
+ for (const h of chain) {
97
+ prunable.push(h);
98
+ }
99
+ }
100
+ }
101
+ this.lastFinalizedHash = finalizedHash;
102
+ this.unfinalized = newUnfinalized;
103
+ return { finalizedHash, prunableStateHashes: prunable };
104
+ }
105
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=finality.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"finality.test.d.ts","sourceRoot":"","sources":["../../../../../packages/workers/importer/finality.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,369 @@
1
+ import assert from "node:assert";
2
+ import { before, describe, it } from "node:test";
3
+ import { Block, DisputesExtrinsic, Extrinsic, Header, reencodeAsView, tryAsTimeSlot, } from "#@typeberry/block";
4
+ import { asKnownSize } from "#@typeberry/collections";
5
+ import { tinyChainSpec } from "#@typeberry/config";
6
+ import { InMemoryBlocks } from "#@typeberry/database";
7
+ import { Blake2b, WithHash } from "#@typeberry/hash";
8
+ import { DummyFinalizer } from "./finality.js";
9
+ let blake2b;
10
+ before(async () => {
11
+ blake2b = await Blake2b.createHasher();
12
+ });
13
+ function assertExists(value) {
14
+ assert.notStrictEqual(value, null);
15
+ assert.notStrictEqual(value, undefined);
16
+ }
17
+ /**
18
+ * Create a block with the given parent hash and slot, insert it into the db,
19
+ * and return its hash.
20
+ */
21
+ async function createBlock(db, parent, slot = 0) {
22
+ const header = Header.create({
23
+ ...Header.empty(),
24
+ parentHeaderHash: parent,
25
+ timeSlotIndex: tryAsTimeSlot(slot),
26
+ });
27
+ const block = Block.create({
28
+ header,
29
+ extrinsic: Extrinsic.create({
30
+ tickets: asKnownSize([]),
31
+ preimages: [],
32
+ assurances: asKnownSize([]),
33
+ guarantees: asKnownSize([]),
34
+ disputes: DisputesExtrinsic.create({ verdicts: [], culprits: [], faults: [] }),
35
+ }),
36
+ });
37
+ const blockView = reencodeAsView(Block.Codec, block, tinyChainSpec);
38
+ const headerHash = blake2b.hashBytes(blockView.header.view().encoded()).asOpaque();
39
+ await db.insertBlock(new WithHash(headerHash, blockView));
40
+ return headerHash;
41
+ }
42
+ /** Build a linear chain of `length` blocks starting from `parentHash`. */
43
+ async function buildLinearChain(db, parentHash, length) {
44
+ const hashes = [];
45
+ let parent = parentHash;
46
+ for (let i = 0; i < length; i++) {
47
+ const h = await createBlock(db, parent, i);
48
+ hashes.push(h);
49
+ parent = h;
50
+ }
51
+ return hashes;
52
+ }
53
+ describe("DummyFinalizer", () => {
54
+ describe("linear chain", () => {
55
+ it("should return null when chain is shorter than depth", async () => {
56
+ const db = InMemoryBlocks.new();
57
+ const genesis = db.getBestHeaderHash();
58
+ const finalizer = DummyFinalizer.create(db, 3);
59
+ // Build a chain of 3 blocks: genesis -> 1 -> 2 -> 3
60
+ const chain = await buildLinearChain(db, genesis, 3);
61
+ // Import all 3 — chain length = depth, not > depth, so no finality.
62
+ for (const h of chain) {
63
+ const result = finalizer.onBlockImported(h);
64
+ assert.strictEqual(result, null);
65
+ }
66
+ });
67
+ it("should finalize when chain exceeds depth", async () => {
68
+ const db = InMemoryBlocks.new();
69
+ const genesis = db.getBestHeaderHash();
70
+ const finalizer = DummyFinalizer.create(db, 3);
71
+ // Build: genesis -> 1 -> 2 -> 3 -> 4
72
+ const chain = await buildLinearChain(db, genesis, 4);
73
+ // First 3 imports: no finality.
74
+ for (let i = 0; i < 3; i++) {
75
+ assert.strictEqual(finalizer.onBlockImported(chain[i]), null);
76
+ }
77
+ // 4th import: chain length = 4 > depth(3), finalize block at index 0.
78
+ const result = finalizer.onBlockImported(chain[3]);
79
+ assertExists(result);
80
+ assert.strictEqual(result.finalizedHash.isEqualTo(chain[0]), true);
81
+ });
82
+ it("should prune the previously finalized block on first finality", async () => {
83
+ const db = InMemoryBlocks.new();
84
+ const genesis = db.getBestHeaderHash();
85
+ const finalizer = DummyFinalizer.create(db, 3);
86
+ const chain = await buildLinearChain(db, genesis, 4);
87
+ for (let i = 0; i < 3; i++) {
88
+ finalizer.onBlockImported(chain[i]);
89
+ }
90
+ const result = finalizer.onBlockImported(chain[3]);
91
+ assertExists(result);
92
+ // Block 1 is finalized. The previously finalized block (genesis) is pruned.
93
+ assert.strictEqual(result.prunableStateHashes.length, 1);
94
+ assert.ok(result.prunableStateHashes[0].isEqualTo(genesis));
95
+ });
96
+ it("should advance finality one block at a time, pruning previous finalized each time", async () => {
97
+ const db = InMemoryBlocks.new();
98
+ const genesis = db.getBestHeaderHash();
99
+ const finalizer = DummyFinalizer.create(db, 2);
100
+ // Build: genesis -> 1 -> 2 -> 3 -> 4 -> 5
101
+ const chain = await buildLinearChain(db, genesis, 5);
102
+ // Import 1, 2: no finality (length <= depth)
103
+ assert.strictEqual(finalizer.onBlockImported(chain[0]), null);
104
+ assert.strictEqual(finalizer.onBlockImported(chain[1]), null);
105
+ // Import 3: length=3 > depth=2, finalize block 1. Prune genesis.
106
+ const r1 = finalizer.onBlockImported(chain[2]);
107
+ assertExists(r1);
108
+ assert.strictEqual(r1.finalizedHash.isEqualTo(chain[0]), true);
109
+ assert.strictEqual(r1.prunableStateHashes.length, 1);
110
+ assert.ok(r1.prunableStateHashes[0].isEqualTo(genesis));
111
+ // Import 4: finalize block 2. Prune block 1 (previous finalized).
112
+ const r2 = finalizer.onBlockImported(chain[3]);
113
+ assertExists(r2);
114
+ assert.strictEqual(r2.finalizedHash.isEqualTo(chain[1]), true);
115
+ assert.strictEqual(r2.prunableStateHashes.length, 1);
116
+ assert.ok(r2.prunableStateHashes[0].isEqualTo(chain[0]));
117
+ // Import 5: finalize block 3. Prune block 2 (previous finalized).
118
+ const r3 = finalizer.onBlockImported(chain[4]);
119
+ assertExists(r3);
120
+ assert.strictEqual(r3.finalizedHash.isEqualTo(chain[2]), true);
121
+ assert.strictEqual(r3.prunableStateHashes.length, 1);
122
+ assert.ok(r3.prunableStateHashes[0].isEqualTo(chain[1]));
123
+ });
124
+ it("should advance finality on every import even when blocks arrive in a burst", async () => {
125
+ const db = InMemoryBlocks.new();
126
+ const genesis = db.getBestHeaderHash();
127
+ const finalizer = DummyFinalizer.create(db, 2);
128
+ // Build: genesis -> 1 -> 2 -> 3 -> 4 -> 5
129
+ const chain = await buildLinearChain(db, genesis, 5);
130
+ // Import blocks 1..4 — finality fires on block 3 and block 4.
131
+ assert.strictEqual(finalizer.onBlockImported(chain[0]), null);
132
+ assert.strictEqual(finalizer.onBlockImported(chain[1]), null);
133
+ // Block 3: chain [1,2,3] length=3 > depth=2 → finalize block 1.
134
+ const r1 = finalizer.onBlockImported(chain[2]);
135
+ assertExists(r1);
136
+ assert.strictEqual(r1.finalizedHash.isEqualTo(chain[0]), true);
137
+ // Block 4: chain [2,3,4] length=3 > depth=2 → finalize block 2.
138
+ const r2 = finalizer.onBlockImported(chain[3]);
139
+ assertExists(r2);
140
+ assert.strictEqual(r2.finalizedHash.isEqualTo(chain[1]), true);
141
+ // Block 5: chain [3,4,5] length=3 > depth=2 → finalize block 3.
142
+ const r3 = finalizer.onBlockImported(chain[4]);
143
+ assertExists(r3);
144
+ assert.strictEqual(r3.finalizedHash.isEqualTo(chain[2]), true);
145
+ });
146
+ });
147
+ describe("with depth=1", () => {
148
+ it("should finalize immediately after 2 blocks", async () => {
149
+ const db = InMemoryBlocks.new();
150
+ const genesis = db.getBestHeaderHash();
151
+ const finalizer = DummyFinalizer.create(db, 1);
152
+ const chain = await buildLinearChain(db, genesis, 3);
153
+ // Import 1: length=1, not > 1. No finality.
154
+ assert.strictEqual(finalizer.onBlockImported(chain[0]), null);
155
+ // Import 2: length=2 > 1. Finalize block 1.
156
+ const r = finalizer.onBlockImported(chain[1]);
157
+ assertExists(r);
158
+ assert.strictEqual(r.finalizedHash.isEqualTo(chain[0]), true);
159
+ // Import 3: finalize block 2.
160
+ const r2 = finalizer.onBlockImported(chain[2]);
161
+ assertExists(r2);
162
+ assert.strictEqual(r2.finalizedHash.isEqualTo(chain[1]), true);
163
+ });
164
+ });
165
+ describe("forks", () => {
166
+ it("should track two forks from the finalized block", async () => {
167
+ const db = InMemoryBlocks.new();
168
+ const genesis = db.getBestHeaderHash();
169
+ const finalizer = DummyFinalizer.create(db, 2);
170
+ // Fork A: genesis -> A1 -> A2 -> A3
171
+ const a1 = await createBlock(db, genesis, 1);
172
+ const a2 = await createBlock(db, a1, 2);
173
+ const a3 = await createBlock(db, a2, 3);
174
+ // Fork B: genesis -> B1 -> B2
175
+ const b1 = await createBlock(db, genesis, 10);
176
+ const b2 = await createBlock(db, b1, 11);
177
+ // Import A1, A2, B1, B2 — no finality yet.
178
+ assert.strictEqual(finalizer.onBlockImported(a1), null);
179
+ assert.strictEqual(finalizer.onBlockImported(a2), null);
180
+ assert.strictEqual(finalizer.onBlockImported(b1), null);
181
+ assert.strictEqual(finalizer.onBlockImported(b2), null);
182
+ // Import A3: fork A has length 3 > depth 2. Finalize A1.
183
+ const result = finalizer.onBlockImported(a3);
184
+ assertExists(result);
185
+ assert.strictEqual(result.finalizedHash.isEqualTo(a1), true);
186
+ // Fork B is dead (doesn't contain A1). B1 and B2 should be pruned.
187
+ // Also, the previous finalized (genesis) is pruned.
188
+ const prunedStrings = result.prunableStateHashes.map((h) => h.toString());
189
+ assert.ok(prunedStrings.includes(genesis.toString()), "Genesis (prev finalized) should be pruned");
190
+ assert.ok(prunedStrings.includes(b1.toString()), "B1 should be pruned");
191
+ assert.ok(prunedStrings.includes(b2.toString()), "B2 should be pruned");
192
+ // A1 is the finalized block — should NOT be pruned.
193
+ assert.ok(!prunedStrings.includes(a1.toString()), "A1 (finalized) should not be pruned");
194
+ });
195
+ it("should keep alive forks that diverge after the finalized block", async () => {
196
+ const db = InMemoryBlocks.new();
197
+ const genesis = db.getBestHeaderHash();
198
+ const finalizer = DummyFinalizer.create(db, 2);
199
+ // Main chain: genesis -> 1 -> 2 -> 3
200
+ const b1 = await createBlock(db, genesis, 1);
201
+ const b2 = await createBlock(db, b1, 2);
202
+ const b3 = await createBlock(db, b2, 3);
203
+ // Fork from block 2: 2 -> F1
204
+ const f1 = await createBlock(db, b2, 20);
205
+ // Import 1, 2 — no finality yet (chain length 2 = depth 2).
206
+ finalizer.onBlockImported(b1);
207
+ finalizer.onBlockImported(b2);
208
+ // Import F1: fork chain [b1, b2, f1] has length 3 > depth 2.
209
+ // This triggers finality for b1.
210
+ const r1 = finalizer.onBlockImported(f1);
211
+ assertExists(r1);
212
+ assert.strictEqual(r1.finalizedHash.isEqualTo(b1), true);
213
+ // Both chains contain b1: main [b1, b2] → alive, trimmed to [b2].
214
+ // Fork [b1, b2, f1] → alive, trimmed to [b2, f1].
215
+ // Only the previous finalized (genesis) is pruned.
216
+ assert.strictEqual(r1.prunableStateHashes.length, 1);
217
+ assert.ok(r1.prunableStateHashes[0].isEqualTo(genesis));
218
+ // Now import b3: it extends the main chain [b2] → [b2, b3].
219
+ // Length 2, not > depth 2. No finality.
220
+ const r2 = finalizer.onBlockImported(b3);
221
+ assert.strictEqual(r2, null);
222
+ // Extend the fork: F1 -> F2 -> F3
223
+ const f2 = await createBlock(db, f1, 21);
224
+ const f3 = await createBlock(db, f2, 22);
225
+ // Import F2: fork chain becomes [b2, f1, f2], length=3 > depth=2.
226
+ // Finalize b2 (index 0). Both chains contain b2, so both are alive.
227
+ // unfinalized becomes [[b3], [f1, f2]]. Only prev finalized (b1) pruned.
228
+ const r3 = finalizer.onBlockImported(f2);
229
+ assertExists(r3);
230
+ assert.strictEqual(r3.finalizedHash.isEqualTo(b2), true);
231
+ assert.strictEqual(r3.prunableStateHashes.length, 1);
232
+ assert.ok(r3.prunableStateHashes[0].isEqualTo(b1));
233
+ // Import F3: fork chain becomes [f1, f2, f3], length=3 > depth=2.
234
+ // Finalize f1 (index 0). Main chain [b3] doesn't contain f1 → dead.
235
+ const r4 = finalizer.onBlockImported(f3);
236
+ assertExists(r4);
237
+ assert.strictEqual(r4.finalizedHash.isEqualTo(f1), true);
238
+ const pruned = r4.prunableStateHashes.map((h) => h.toString());
239
+ assert.ok(pruned.includes(b2.toString()), "B2 (prev finalized) should be pruned");
240
+ assert.ok(pruned.includes(b3.toString()), "B3 should be pruned (dead fork)");
241
+ assert.ok(!pruned.includes(f1.toString()), "F1 should not be pruned (finalized)");
242
+ assert.ok(!pruned.includes(f2.toString()), "F2 should not be pruned (after finalized)");
243
+ assert.ok(!pruned.includes(f3.toString()), "F3 should not be pruned (after finalized)");
244
+ });
245
+ it("should prune a dead fork that diverged before the finalized block", async () => {
246
+ const db = InMemoryBlocks.new();
247
+ const genesis = db.getBestHeaderHash();
248
+ const finalizer = DummyFinalizer.create(db, 2);
249
+ // Main: genesis -> 1 -> 2 -> 3 -> 4
250
+ const chain = await buildLinearChain(db, genesis, 4);
251
+ // Fork from genesis: genesis -> F1 -> F2
252
+ const f1 = await createBlock(db, genesis, 100);
253
+ const f2 = await createBlock(db, f1, 101);
254
+ // Import: 1, 2, F1, F2, 3 (finalize block 1), 4 (finalize block 2)
255
+ finalizer.onBlockImported(chain[0]);
256
+ finalizer.onBlockImported(chain[1]);
257
+ finalizer.onBlockImported(f1);
258
+ finalizer.onBlockImported(f2);
259
+ // Import 3: main chain length=3 > depth=2, finalize block 1.
260
+ const r1 = finalizer.onBlockImported(chain[2]);
261
+ assertExists(r1);
262
+ assert.strictEqual(r1.finalizedHash.isEqualTo(chain[0]), true);
263
+ // Fork [F1, F2] doesn't contain block 1, so it's dead.
264
+ // Also, the previous finalized (genesis) is pruned.
265
+ const pruned1 = r1.prunableStateHashes.map((h) => h.toString());
266
+ assert.ok(pruned1.includes(genesis.toString()), "Genesis (prev finalized) should be pruned");
267
+ assert.ok(pruned1.includes(f1.toString()), "F1 should be pruned");
268
+ assert.ok(pruned1.includes(f2.toString()), "F2 should be pruned");
269
+ });
270
+ it("should handle fork from the middle of a chain", async () => {
271
+ const db = InMemoryBlocks.new();
272
+ const genesis = db.getBestHeaderHash();
273
+ const finalizer = DummyFinalizer.create(db, 2);
274
+ // Main: genesis -> 1 -> 2 -> 3
275
+ const b1 = await createBlock(db, genesis, 1);
276
+ const b2 = await createBlock(db, b1, 2);
277
+ const b3 = await createBlock(db, b2, 3);
278
+ // Fork from block 1: 1 -> F1 -> F2 -> F3
279
+ const f1 = await createBlock(db, b1, 10);
280
+ const f2 = await createBlock(db, f1, 11);
281
+ const f3 = await createBlock(db, f2, 12);
282
+ // Import main chain first.
283
+ finalizer.onBlockImported(b1);
284
+ finalizer.onBlockImported(b2);
285
+ finalizer.onBlockImported(b3);
286
+ // Import fork — F1's parent is b1 which is mid-chain, not a tip.
287
+ finalizer.onBlockImported(f1);
288
+ finalizer.onBlockImported(f2);
289
+ // Import F3: fork chain = [b1, f1, f2, f3], length=4 > depth=2.
290
+ // Finalize block at index 4-1-2 = 1 → f1.
291
+ // But wait — the main chain [b1, b2, b3] also has length 3 > 2.
292
+ // Block 3 import already triggered finality for b1.
293
+ // After that, main chain trimmed to [b2, b3].
294
+ // Fork was created from mid-chain of [b1, b2, b3] at b1.
295
+ // But b1 was already part of the chain before finality.
296
+ // After finality of b1: unfinalized = [[b2, b3]].
297
+ // Now F1's parent is b1 = lastFinalized, so it starts a new chain: [f1].
298
+ // F2 extends it: [f1, f2]. F3 extends: [f1, f2, f3]. Length=3 > 2.
299
+ // Finalize f1. Main chain [b2, b3] doesn't contain f1 → dead fork.
300
+ const r = finalizer.onBlockImported(f3);
301
+ assertExists(r);
302
+ assert.strictEqual(r.finalizedHash.isEqualTo(f1), true);
303
+ const pruned = r.prunableStateHashes.map((h) => h.toString());
304
+ // Previous finalized (b1) is pruned, plus main chain [b2, b3] is dead.
305
+ assert.ok(pruned.includes(b1.toString()), "B1 (prev finalized) should be pruned");
306
+ assert.ok(pruned.includes(b2.toString()), "B2 should be pruned (dead fork)");
307
+ assert.ok(pruned.includes(b3.toString()), "B3 should be pruned (dead fork)");
308
+ });
309
+ });
310
+ describe("edge cases", () => {
311
+ it("should return null for unknown block hash", async () => {
312
+ const db = InMemoryBlocks.new();
313
+ const finalizer = DummyFinalizer.create(db, 3);
314
+ // Create a block hash that was never inserted into the db.
315
+ const unknownHash = await createBlock(InMemoryBlocks.new(), db.getBestHeaderHash());
316
+ const result = finalizer.onBlockImported(unknownHash);
317
+ assert.strictEqual(result, null);
318
+ });
319
+ it("should return null for orphan block (parent not in any chain)", async () => {
320
+ const db = InMemoryBlocks.new();
321
+ const genesis = db.getBestHeaderHash();
322
+ const finalizer = DummyFinalizer.create(db, 2);
323
+ // Block whose parent is some unknown hash not in any chain.
324
+ // We create a separate db to get a "foreign" hash, then insert the orphan
325
+ // into our db with that foreign hash as parent.
326
+ const foreignDb = InMemoryBlocks.new();
327
+ const foreignParent = await createBlock(foreignDb, genesis, 99);
328
+ const orphan = await createBlock(db, foreignParent, 50);
329
+ const result = finalizer.onBlockImported(orphan);
330
+ assert.strictEqual(result, null);
331
+ });
332
+ it("should always advance finality forward, never re-finalizing earlier blocks", async () => {
333
+ const db = InMemoryBlocks.new();
334
+ const genesis = db.getBestHeaderHash();
335
+ const finalizer = DummyFinalizer.create(db, 2);
336
+ // genesis -> 1 -> 2 -> 3 -> 4
337
+ const chain = await buildLinearChain(db, genesis, 4);
338
+ finalizer.onBlockImported(chain[0]);
339
+ finalizer.onBlockImported(chain[1]);
340
+ // Block 3 finalizes block 1.
341
+ const r1 = finalizer.onBlockImported(chain[2]);
342
+ assertExists(r1);
343
+ assert.strictEqual(r1.finalizedHash.isEqualTo(chain[0]), true);
344
+ // Block 4 finalizes block 2. Block 1 (prev finalized) is pruned.
345
+ const r2 = finalizer.onBlockImported(chain[3]);
346
+ assertExists(r2);
347
+ assert.strictEqual(r2.finalizedHash.isEqualTo(chain[1]), true);
348
+ // Block 1 appears exactly once (as previous finalized, not re-finalized).
349
+ const pruned = r2.prunableStateHashes.map((h) => h.toString());
350
+ assert.strictEqual(pruned.filter((h) => h === chain[0].toString()).length, 1);
351
+ // The newly finalized block (chain[1]) should NOT be pruned.
352
+ assert.ok(!pruned.includes(chain[1].toString()), "Newly finalized block should not be pruned");
353
+ });
354
+ it("should work with depth=0", async () => {
355
+ const db = InMemoryBlocks.new();
356
+ const genesis = db.getBestHeaderHash();
357
+ // depth=0 means finalize as soon as any block exists.
358
+ const finalizer = DummyFinalizer.create(db, 0);
359
+ const b1 = await createBlock(db, genesis, 1);
360
+ // Chain length = 1 > 0 → finalize block at index 1-1-0 = 0 → b1.
361
+ // Genesis (prev finalized) is pruned.
362
+ const result = finalizer.onBlockImported(b1);
363
+ assertExists(result);
364
+ assert.strictEqual(result.finalizedHash.isEqualTo(b1), true);
365
+ assert.strictEqual(result.prunableStateHashes.length, 1);
366
+ assert.ok(result.prunableStateHashes[0].isEqualTo(genesis));
367
+ });
368
+ });
369
+ });
@@ -8,6 +8,7 @@ import type { TransitionHasher } from "#@typeberry/transition";
8
8
  import { BlockVerifierError } from "#@typeberry/transition/block-verifier.js";
9
9
  import { type StfError } from "#@typeberry/transition/chain-stf.js";
10
10
  import { Result, type TaggedError } from "#@typeberry/utils";
11
+ import type { Finalizer } from "./finality.js";
11
12
  export declare enum ImporterErrorKind {
12
13
  Verifier = 0,
13
14
  Stf = 1,
@@ -16,6 +17,7 @@ export declare enum ImporterErrorKind {
16
17
  export type ImporterError = TaggedError<ImporterErrorKind.Verifier, BlockVerifierError> | TaggedError<ImporterErrorKind.Stf, StfError> | TaggedError<ImporterErrorKind.Update, StateUpdateError>;
17
18
  export type ImporterOptions = {
18
19
  initGenesisFromAncestry?: boolean;
20
+ finalizer?: Finalizer;
19
21
  };
20
22
  export declare class Importer {
21
23
  private readonly hasher;
@@ -1 +1 @@
1
- {"version":3,"file":"importer.d.ts","sourceRoot":"","sources":["../../../../../packages/workers/importer/importer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAAE,KAAK,UAAU,EAAE,KAAK,UAAU,EAAE,KAAK,aAAa,EAAiB,MAAM,kBAAkB,CAAC;AACvH,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AACxF,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAC3C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AACtE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAC9D,OAAO,EAAiB,kBAAkB,EAAE,MAAM,yCAAyC,CAAC;AAC5F,OAAO,EAA0B,KAAK,QAAQ,EAAE,MAAM,oCAAoC,CAAC;AAC3F,OAAO,EAAkC,MAAM,EAAkB,KAAK,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAG5G,oBAAY,iBAAiB;IAC3B,QAAQ,IAAI;IACZ,GAAG,IAAI;IACP,MAAM,IAAI;CACX;AAED,MAAM,MAAM,aAAa,GACrB,WAAW,CAAC,iBAAiB,CAAC,QAAQ,EAAE,kBAAkB,CAAC,GAC3D,WAAW,CAAC,iBAAiB,CAAC,GAAG,EAAE,QAAQ,CAAC,GAC5C,WAAW,CAAC,iBAAiB,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;AAO5D,MAAM,MAAM,eAAe,GAAG;IAC5B,uBAAuB,CAAC,EAAE,OAAO,CAAC;CACnC,CAAC;AAEF,qBAAa,QAAQ;IAajB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAhB1B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAgB;IACzC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAU;IAG9B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAA0B;IAEhD,OAAO,CAAC,WAAW,CAAa;IAChC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA2C;gBAGjE,IAAI,EAAE,SAAS,EACf,GAAG,EAAE,UAAU,EACE,MAAM,EAAE,gBAAgB,EACxB,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,QAAQ,EAChB,MAAM,EAAE,QAAQ,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC,EACzC,OAAO,GAAE,eAAoB;IAkBhD,6DAA6D;IAChD,mBAAmB;IAQnB,wBAAwB,CAAC,KAAK,EAAE,SAAS,GAAG,OAAO,CAAC,MAAM,CAAC,aAAa,EAAE,aAAa,CAAC,CAAC;IAQzF,WAAW,CAAC,KAAK,EAAE,SAAS,GAAG,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAC,EAAE,aAAa,CAAC,CAAC;YAwB9F,mBAAmB;IAkFjC,oBAAoB;IAMpB,gBAAgB;IAIhB,eAAe,CAAC,UAAU,EAAE,UAAU;IAKhC,KAAK;CAIZ"}
1
+ {"version":3,"file":"importer.d.ts","sourceRoot":"","sources":["../../../../../packages/workers/importer/importer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAAE,KAAK,UAAU,EAAE,KAAK,UAAU,EAAE,KAAK,aAAa,EAAiB,MAAM,kBAAkB,CAAC;AACvH,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AACxF,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAC3C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AACtE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAC9D,OAAO,EAAiB,kBAAkB,EAAE,MAAM,yCAAyC,CAAC;AAC5F,OAAO,EAA0B,KAAK,QAAQ,EAAE,MAAM,oCAAoC,CAAC;AAC3F,OAAO,EAAkC,MAAM,EAAkB,KAAK,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC5G,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAG/C,oBAAY,iBAAiB;IAC3B,QAAQ,IAAI;IACZ,GAAG,IAAI;IACP,MAAM,IAAI;CACX;AAED,MAAM,MAAM,aAAa,GACrB,WAAW,CAAC,iBAAiB,CAAC,QAAQ,EAAE,kBAAkB,CAAC,GAC3D,WAAW,CAAC,iBAAiB,CAAC,GAAG,EAAE,QAAQ,CAAC,GAC5C,WAAW,CAAC,iBAAiB,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;AAO5D,MAAM,MAAM,eAAe,GAAG;IAC5B,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAClC,SAAS,CAAC,EAAE,SAAS,CAAC;CACvB,CAAC;AAEF,qBAAa,QAAQ;IAajB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAhB1B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAgB;IACzC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAU;IAG9B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAA0B;IAEhD,OAAO,CAAC,WAAW,CAAa;IAChC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA2C;gBAGjE,IAAI,EAAE,SAAS,EACf,GAAG,EAAE,UAAU,EACE,MAAM,EAAE,gBAAgB,EACxB,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,QAAQ,EAChB,MAAM,EAAE,QAAQ,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC,EACzC,OAAO,GAAE,eAAoB;IAkBhD,6DAA6D;IAChD,mBAAmB;IAQnB,wBAAwB,CAAC,KAAK,EAAE,SAAS,GAAG,OAAO,CAAC,MAAM,CAAC,aAAa,EAAE,aAAa,CAAC,CAAC;IAQzF,WAAW,CAAC,KAAK,EAAE,SAAS,GAAG,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAC,EAAE,aAAa,CAAC,CAAC;YAwB9F,mBAAmB;IA2FjC,oBAAoB;IAMpB,gBAAgB;IAIhB,eAAe,CAAC,UAAU,EAAE,UAAU;IAKhC,KAAK;CAIZ"}
@@ -144,6 +144,14 @@ export class Importer {
144
144
  logger.log `${timerDb()}`;
145
145
  // finally update the best block
146
146
  await this.blocks.setBestHeaderHash(headerHash);
147
+ // check for finality and prune old states
148
+ const finality = this.options.finalizer?.onBlockImported(headerHash) ?? null;
149
+ if (finality !== null) {
150
+ this.logger.info `🦭 Finalized block: ${finality.finalizedHash} (${finality.prunableStateHashes.length} to prune)`;
151
+ for (const hash of finality.prunableStateHashes) {
152
+ this.states.markUnused(hash);
153
+ }
154
+ }
147
155
  return Result.ok(new WithHash(headerHash, block.header.view()));
148
156
  }
149
157
  getBestStateRootHash() {
@@ -1 +1 @@
1
- {"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../../../../packages/workers/importer/main.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAGtE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AAGtE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,KAAK,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAMtE,MAAM,MAAM,MAAM,GAAG,YAAY,CAAC,cAAc,EAAE,QAAQ,EAAE,QAAQ,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;AAE/F,MAAM,MAAM,qBAAqB,GAAG;IAClC,uBAAuB,CAAC,EAAE,OAAO,CAAC;CACnC,CAAC;AAEF,wBAAsB,cAAc,CAClC,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,qBAA0B,GAClC,OAAO,CAAC;IACT,QAAQ,EAAE,QAAQ,CAAC;IACnB,EAAE,EAAE,UAAU,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC,CAAC;CACxC,CAAC,CAcD;AAED;;;;;GAKG;AACH,wBAAsB,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,gBAAgB,iBAwCjE"}
1
+ {"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../../../../packages/workers/importer/main.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAGtE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AAGtE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAE3D,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,KAAK,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAMtE,MAAM,MAAM,MAAM,GAAG,YAAY,CAAC,cAAc,EAAE,QAAQ,EAAE,QAAQ,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;AAE/F,MAAM,MAAM,qBAAqB,GAAG;IAClC,uBAAuB,CAAC,EAAE,OAAO,CAAC;CACnC,CAAC;AAEF,wBAAsB,cAAc,CAClC,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,qBAA0B,GAClC,OAAO,CAAC;IACT,QAAQ,EAAE,QAAQ,CAAC;IACnB,EAAE,EAAE,UAAU,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC,CAAC;CACxC,CAAC,CAoBD;AAED;;;;;GAKG;AACH,wBAAsB,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,gBAAgB,iBAwCjE"}
@@ -3,6 +3,7 @@ import { Blake2b, keccak, ZERO_HASH } from "#@typeberry/hash";
3
3
  import { Logger } from "#@typeberry/logger";
4
4
  import { TransitionHasher } from "#@typeberry/transition";
5
5
  import { Result, resultToString } from "#@typeberry/utils";
6
+ import { DummyFinalizer } from "./finality.js";
6
7
  import { Importer } from "./importer.js";
7
8
  const logger = Logger.new(import.meta.filename, "importer");
8
9
  const keccakHasher = keccak.KeccakHasher.create();
@@ -13,8 +14,13 @@ export async function createImporter(config, options = {}) {
13
14
  const pvm = config.workerParams.pvm;
14
15
  const blocks = db.getBlocksDb();
15
16
  const states = db.getStatesDb();
17
+ const dummyFinalityDepth = config.workerParams.dummyFinalityDepth ?? 0;
18
+ const finalizer = dummyFinalityDepth > 0 ? DummyFinalizer.create(blocks, dummyFinalityDepth) : undefined;
16
19
  const hasher = new TransitionHasher(await keccakHasher, await blake2b);
17
- const importer = new Importer(chainSpec, pvm, hasher, logger, blocks, states, options);
20
+ const importer = new Importer(chainSpec, pvm, hasher, logger, blocks, states, {
21
+ ...options,
22
+ finalizer,
23
+ });
18
24
  return {
19
25
  importer,
20
26
  db,
@@ -3,6 +3,7 @@ import { BytesBlob } from "#@typeberry/bytes";
3
3
  import { type CodecRecord } from "#@typeberry/codec";
4
4
  import { PvmBackend } from "#@typeberry/config";
5
5
  import { type OpaqueHash } from "#@typeberry/hash";
6
+ import { type U16 } from "#@typeberry/numbers";
6
7
  import { StateEntries } from "#@typeberry/state-merkleization";
7
8
  import { Result } from "#@typeberry/utils";
8
9
  import { type Api, type Internal } from "#@typeberry/workers-api";
@@ -57,7 +58,7 @@ export declare const protocol: import("@typeberry/workers-api").LousyProtocol<{
57
58
  length: import("@typeberry/codec").Descriptor<import("@typeberry/numbers").U32, import("@typeberry/bytes").Bytes<4>>;
58
59
  erasureRoot: import("@typeberry/codec").Descriptor<import("@typeberry/bytes").Bytes<32>, import("@typeberry/bytes").Bytes<32>>;
59
60
  exportsRoot: import("@typeberry/codec").Descriptor<import("@typeberry/bytes").Bytes<32> & import("@typeberry/utils").WithOpaque<"ExportsRootHash">, import("@typeberry/bytes").Bytes<32>>;
60
- exportsCount: import("@typeberry/codec").Descriptor<import("@typeberry/numbers").U16, import("@typeberry/bytes").Bytes<2>>;
61
+ exportsCount: import("@typeberry/codec").Descriptor<U16, import("@typeberry/bytes").Bytes<2>>;
61
62
  }>>;
62
63
  context: import("@typeberry/codec").Descriptor<import("@typeberry/block").RefineContext, import("@typeberry/codec").ViewOf<import("@typeberry/block").RefineContext, {
63
64
  anchor: import("@typeberry/codec").Descriptor<import("@typeberry/bytes").Bytes<32> & import("@typeberry/utils").WithOpaque<"HeaderHash">, import("@typeberry/bytes").Bytes<32>>;
@@ -181,7 +182,7 @@ export declare const protocol: import("@typeberry/workers-api").LousyProtocol<{
181
182
  length: import("@typeberry/codec").Descriptor<import("@typeberry/numbers").U32, import("@typeberry/bytes").Bytes<4>>;
182
183
  erasureRoot: import("@typeberry/codec").Descriptor<import("@typeberry/bytes").Bytes<32>, import("@typeberry/bytes").Bytes<32>>;
183
184
  exportsRoot: import("@typeberry/codec").Descriptor<import("@typeberry/bytes").Bytes<32> & import("@typeberry/utils").WithOpaque<"ExportsRootHash">, import("@typeberry/bytes").Bytes<32>>;
184
- exportsCount: import("@typeberry/codec").Descriptor<import("@typeberry/numbers").U16, import("@typeberry/bytes").Bytes<2>>;
185
+ exportsCount: import("@typeberry/codec").Descriptor<U16, import("@typeberry/bytes").Bytes<2>>;
185
186
  }>>;
186
187
  context: import("@typeberry/codec").Descriptor<import("@typeberry/block").RefineContext, import("@typeberry/codec").ViewOf<import("@typeberry/block").RefineContext, {
187
188
  anchor: import("@typeberry/codec").Descriptor<import("@typeberry/bytes").Bytes<32> & import("@typeberry/utils").WithOpaque<"HeaderHash">, import("@typeberry/bytes").Bytes<32>>;
@@ -376,10 +377,13 @@ export type ImporterInternal = Internal<typeof protocol>;
376
377
  export type ImporterApi = Api<typeof protocol>;
377
378
  export declare class ImporterConfig {
378
379
  readonly pvm: PvmBackend;
380
+ /** Dummy finality depth. 0 means disabled, any positive value enables dummy finality with that depth. */
381
+ readonly dummyFinalityDepth: U16;
379
382
  static Codec: import("@typeberry/codec").Descriptor<ImporterConfig, import("@typeberry/codec").ViewOf<ImporterConfig, {
380
383
  pvm: import("@typeberry/codec").Descriptor<PvmBackend, import("@typeberry/numbers").U8>;
384
+ dummyFinalityDepth: import("@typeberry/codec").Descriptor<U16, import("@typeberry/bytes").Bytes<2>>;
381
385
  }>>;
382
- static create({ pvm }: CodecRecord<ImporterConfig>): ImporterConfig;
386
+ static create({ pvm, dummyFinalityDepth }: CodecRecord<ImporterConfig>): ImporterConfig;
383
387
  private constructor();
384
388
  }
385
389
  //# sourceMappingURL=protocol.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../../../../../packages/workers/importer/protocol.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAgE,MAAM,kBAAkB,CAAC;AACvG,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,KAAK,WAAW,EAAS,MAAM,kBAAkB,CAAC;AAC3D,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAa,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAE7D,OAAO,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AAC9D,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC1C,OAAO,EAAE,KAAK,GAAG,EAAkB,KAAK,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AA0CjF,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAyBnB,CAAC;AAEH,MAAM,MAAM,gBAAgB,GAAG,QAAQ,CAAC,OAAO,QAAQ,CAAC,CAAC;AACzD,MAAM,MAAM,WAAW,GAAG,GAAG,CAAC,OAAO,QAAQ,CAAC,CAAC;AAE/C,qBAAa,cAAc;aAoBW,GAAG,EAAE,UAAU;IAnBnD,MAAM,CAAC,KAAK;;QAaT;IAEH,MAAM,CAAC,MAAM,CAAC,EAAE,GAAG,EAAE,EAAE,WAAW,CAAC,cAAc,CAAC;IAIlD,OAAO;CACR"}
1
+ {"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../../../../../packages/workers/importer/protocol.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAgE,MAAM,kBAAkB,CAAC;AACvG,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,KAAK,WAAW,EAAS,MAAM,kBAAkB,CAAC;AAC3D,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAa,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7D,OAAO,EAA+B,KAAK,GAAG,EAAE,MAAM,oBAAoB,CAAC;AAC3E,OAAO,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AAC9D,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC1C,OAAO,EAAE,KAAK,GAAG,EAAkB,KAAK,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AA0CjF,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAyBnB,CAAC;AAEH,MAAM,MAAM,gBAAgB,GAAG,QAAQ,CAAC,OAAO,QAAQ,CAAC,CAAC;AACzD,MAAM,MAAM,WAAW,GAAG,GAAG,CAAC,OAAO,QAAQ,CAAC,CAAC;AAE/C,qBAAa,cAAc;aAsBP,GAAG,EAAE,UAAU;IAC/B,yGAAyG;aACzF,kBAAkB,EAAE,GAAG;IAvBzC,MAAM,CAAC,KAAK;;;QAcT;IAEH,MAAM,CAAC,MAAM,CAAC,EAAE,GAAG,EAAE,kBAAkB,EAAE,EAAE,WAAW,CAAC,cAAc,CAAC;IAItE,OAAO;CAKR"}
@@ -3,7 +3,7 @@ import { BytesBlob } from "#@typeberry/bytes";
3
3
  import { codec } from "#@typeberry/codec";
4
4
  import { PvmBackend } from "#@typeberry/config";
5
5
  import { HASH_SIZE } from "#@typeberry/hash";
6
- import { tryAsU8, tryAsU32 } from "#@typeberry/numbers";
6
+ import { tryAsU8, tryAsU16, tryAsU32 } from "#@typeberry/numbers";
7
7
  import { StateEntries } from "#@typeberry/state-merkleization";
8
8
  import { Result } from "#@typeberry/utils";
9
9
  import { createProtocol } from "#@typeberry/workers-api";
@@ -70,6 +70,7 @@ export const protocol = createProtocol("importer", {
70
70
  });
71
71
  export class ImporterConfig {
72
72
  pvm;
73
+ dummyFinalityDepth;
73
74
  static Codec = codec.Class(ImporterConfig, {
74
75
  pvm: codec.u8.convert((i) => tryAsU8(i), (o) => {
75
76
  if (o === PvmBackend.BuiltIn) {
@@ -80,11 +81,15 @@ export class ImporterConfig {
80
81
  }
81
82
  throw new Error(`Invalid PvmBackend: ${o}`);
82
83
  }),
84
+ dummyFinalityDepth: codec.u16,
83
85
  });
84
- static create({ pvm }) {
85
- return new ImporterConfig(pvm);
86
+ static create({ pvm, dummyFinalityDepth }) {
87
+ return new ImporterConfig(pvm, dummyFinalityDepth);
86
88
  }
87
- constructor(pvm) {
89
+ constructor(pvm,
90
+ /** Dummy finality depth. 0 means disabled, any positive value enables dummy finality with that depth. */
91
+ dummyFinalityDepth = tryAsU16(0)) {
88
92
  this.pvm = pvm;
93
+ this.dummyFinalityDepth = dummyFinalityDepth;
89
94
  }
90
95
  }