@typeberry/lib 0.8.3 → 0.8.4-2e4ce67

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 (41) hide show
  1. package/package.json +2 -2
  2. package/packages/core/utils/debug.d.ts +12 -2
  3. package/packages/core/utils/debug.d.ts.map +1 -1
  4. package/packages/core/utils/debug.js +21 -4
  5. package/packages/jam/database/states.d.ts +7 -0
  6. package/packages/jam/database/states.d.ts.map +1 -1
  7. package/packages/jam/database-lmdb/hybrid-states.d.ts +3 -1
  8. package/packages/jam/database-lmdb/hybrid-states.d.ts.map +1 -1
  9. package/packages/jam/database-lmdb/hybrid-states.js +5 -2
  10. package/packages/jam/database-lmdb/root.d.ts +14 -1
  11. package/packages/jam/database-lmdb/root.d.ts.map +1 -1
  12. package/packages/jam/database-lmdb/root.js +25 -5
  13. package/packages/jam/database-lmdb/states.d.ts +1 -0
  14. package/packages/jam/database-lmdb/states.d.ts.map +1 -1
  15. package/packages/jam/database-lmdb/states.js +3 -0
  16. package/packages/jam/database-lmdb/states.test.js +4 -4
  17. package/packages/jam/node/main-fuzz.d.ts.map +1 -1
  18. package/packages/jam/node/main-fuzz.js +5 -3
  19. package/packages/jam/node/main-importer.d.ts +1 -1
  20. package/packages/jam/node/main-importer.d.ts.map +1 -1
  21. package/packages/jam/node/main-importer.js +7 -2
  22. package/packages/jam/safrole/bandersnatch-vrf.d.ts +2 -2
  23. package/packages/jam/safrole/bandersnatch-vrf.d.ts.map +1 -1
  24. package/packages/jam/safrole/bandersnatch-vrf.js +38 -20
  25. package/packages/jam/safrole/bandersnatch-vrf.test.js +9 -7
  26. package/packages/jam/safrole/safrole.js +5 -5
  27. package/packages/jam/safrole/safrole.test.js +13 -13
  28. package/packages/jam/transition/chain-stf.d.ts +2 -1
  29. package/packages/jam/transition/chain-stf.d.ts.map +1 -1
  30. package/packages/jam/transition/chain-stf.js +15 -3
  31. package/packages/workers/api-node/config.d.ts +3 -1
  32. package/packages/workers/api-node/config.d.ts.map +1 -1
  33. package/packages/workers/api-node/config.js +13 -7
  34. package/packages/workers/block-authorship/main.d.ts.map +1 -1
  35. package/packages/workers/block-authorship/main.js +7 -5
  36. package/packages/workers/importer/importer.d.ts +3 -1
  37. package/packages/workers/importer/importer.d.ts.map +1 -1
  38. package/packages/workers/importer/importer.js +41 -33
  39. package/packages/workers/importer/stats.d.ts +38 -0
  40. package/packages/workers/importer/stats.d.ts.map +1 -0
  41. package/packages/workers/importer/stats.js +69 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@typeberry/lib",
3
- "version": "0.8.3",
3
+ "version": "0.8.4-2e4ce67",
4
4
  "description": "Typeberry Library",
5
5
  "main": "./bin/lib/index.js",
6
6
  "types": "./bin/lib/index.d.ts",
@@ -267,7 +267,7 @@
267
267
  "@fluffylabs/anan-as": "^1.4.0",
268
268
  "@noble/ed25519": "2.2.3",
269
269
  "hash-wasm": "4.12.0",
270
- "@typeberry/native": "0.3.0-5dae93e",
270
+ "@typeberry/native": "0.5.1",
271
271
  "eventemitter3": "^5.0.1",
272
272
  "@opentelemetry/api": "1.9.0"
273
273
  },
@@ -39,8 +39,18 @@ export declare function assertNever(value: never): never;
39
39
  export declare function assertEmpty<T extends Record<string, never>>(value: T): void;
40
40
  /** Debug print an object. */
41
41
  export declare function inspect<T>(val: T): string;
42
- /** Utility function to measure time taken for some operation [ms]. */
43
- export declare function measure(id: string): () => string;
42
+ /**
43
+ * Utility function to measure time taken for some operation [ms].
44
+ *
45
+ * To reduce allocations, each timer can only track one entry.
46
+ *
47
+ */
48
+ export declare function measure(id: string): () => {
49
+ id: string;
50
+ start: number;
51
+ duration(): number;
52
+ toString(): string;
53
+ };
44
54
  /**
45
55
  * Format current process memory usage as a human readable string.
46
56
  *
@@ -1 +1 @@
1
- {"version":3,"file":"debug.d.ts","sourceRoot":"","sources":["../../../../../packages/core/utils/debug.ts"],"names":[],"mappings":"AAAA,wBAAgB,SAAS,YAExB;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,GAAG,cAA6F,CAAC;AAE9G;;;;;;;;;GASG;AACH,wBAAgB,KAAK,CACnB,OAAO,EAAE,oBAAoB,EAC7B,SAAS,EAAE,OAAO,EAClB,GAAG,IAAI,EAAE,OAAO,EAAE,GACjB,OAAO,CAAC,SAAS,IAAI,IAAI,CAO3B;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,KAAK,GAAG,KAAK,CAE/C;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,KAAK,EAAE,CAAC,QAKpE;AAED,6BAA6B;AAC7B,wBAAgB,OAAO,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,MAAM,CAEzC;AAkED,sEAAsE;AACtE,wBAAgB,OAAO,CAAC,EAAE,EAAE,MAAM,gBAGjC;AAcD;;;;;;;GAOG;AACH,wBAAgB,WAAW,IAAI,MAAM,CAMpC;AAED,+CAA+C;AAC/C,wBAAgB,aAAa,IAAI,MAAM,MAAM,CAc5C;AAED,mFAAmF;AACnF,8BAAsB,SAAS;IAC7B,QAAQ;CAGT;AAED,wBAAgB,WAAW,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC;;EAMpC"}
1
+ {"version":3,"file":"debug.d.ts","sourceRoot":"","sources":["../../../../../packages/core/utils/debug.ts"],"names":[],"mappings":"AAAA,wBAAgB,SAAS,YAExB;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,GAAG,cAA6F,CAAC;AAE9G;;;;;;;;;GASG;AACH,wBAAgB,KAAK,CACnB,OAAO,EAAE,oBAAoB,EAC7B,SAAS,EAAE,OAAO,EAClB,GAAG,IAAI,EAAE,OAAO,EAAE,GACjB,OAAO,CAAC,SAAS,IAAI,IAAI,CAO3B;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,KAAK,GAAG,KAAK,CAE/C;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,KAAK,EAAE,CAAC,QAKpE;AAED,6BAA6B;AAC7B,wBAAgB,OAAO,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,MAAM,CAEzC;AAkED;;;;;GAKG;AACH,wBAAgB,OAAO,CAAC,EAAE,EAAE,MAAM;;;;;EAgBjC;AAcD;;;;;;;GAOG;AACH,wBAAgB,WAAW,IAAI,MAAM,CAMpC;AAED,+CAA+C;AAC/C,wBAAgB,aAAa,IAAI,MAAM,MAAM,CAc5C;AAED,mFAAmF;AACnF,8BAAsB,SAAS;IAC7B,QAAQ;CAGT;AAED,wBAAgB,WAAW,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC;;EAMpC"}
@@ -108,14 +108,31 @@ function inspectInternal(val, seen) {
108
108
  v += oneLine ? "}" : "\n}";
109
109
  return v;
110
110
  }
111
- /** Utility function to measure time taken for some operation [ms]. */
111
+ /**
112
+ * Utility function to measure time taken for some operation [ms].
113
+ *
114
+ * To reduce allocations, each timer can only track one entry.
115
+ *
116
+ */
112
117
  export function measure(id) {
113
- const start = now();
114
- return () => `${id} took ${(now() - start).toFixed(2)}ms`;
118
+ const response = {
119
+ id,
120
+ start: 0,
121
+ duration() {
122
+ return now() - this.start;
123
+ },
124
+ toString() {
125
+ return `${this.id} took ${(this.duration()).toFixed(2)}ms`;
126
+ },
127
+ };
128
+ return () => {
129
+ response.start = now();
130
+ return response;
131
+ };
115
132
  }
116
133
  const BYTES_IN_MB = 1024 * 1024;
117
134
  const toMb = (bytes) => (bytes / BYTES_IN_MB).toFixed(1);
118
- const signedMb = (bytes) => `${bytes >= 0 ? "+" : "-"}${toMb(bytes)}`;
135
+ const signedMb = (bytes) => `${bytes >= 0 ? "+" : ""}${toMb(bytes)}`;
119
136
  /** Raw process memory usage, or `null` in environments without `process` (e.g. browser). */
120
137
  function rawMemoryUsage() {
121
138
  if (isBrowser() || typeof process.memoryUsage !== "function") {
@@ -35,6 +35,13 @@ export interface StatesDb<T extends State = State> {
35
35
  getState(header: HeaderHash): T | null;
36
36
  /** Mark state as no longer needed. Backend may remove it asynchronously. */
37
37
  markUnused(header: HeaderHash): void;
38
+ /**
39
+ * Apparent on-disk size of the database in bytes, used for monitoring.
40
+ *
41
+ * Returns `null` when the size is unknown. Backends that are not persisted
42
+ * (e.g. in-memory) may omit the method entirely.
43
+ */
44
+ diskSizeInBytes?(): number | null;
38
45
  /** Close the database and free resources. */
39
46
  close(): Promise<void>;
40
47
  }
@@ -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,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;IAQxC,OAAO,CAAC,QAAQ,CAAC,IAAI;IAPzC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAmE;IACtF,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAmB;IAE3C,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,SAAS;IAI1B,OAAO;IAID,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"}
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;;;;;OAKG;IACH,eAAe,CAAC,IAAI,MAAM,GAAG,IAAI,CAAC;IAElC,6CAA6C;IAC7C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED,qBAAa,cAAe,YAAW,QAAQ,CAAC,aAAa,CAAC;IAQxC,OAAO,CAAC,QAAQ,CAAC,IAAI;IAPzC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAmE;IACtF,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAmB;IAE3C,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,SAAS;IAI1B,OAAO;IAID,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"}
@@ -20,12 +20,13 @@ export declare class HybridSerializedStates implements StatesDb<SerializedState<
20
20
  private readonly inMemStates;
21
21
  private readonly lmdbValues;
22
22
  private readonly valuesDb;
23
- static new({ spec, blake2b, dbPath, readOnly, ephemeral, }: {
23
+ static new({ spec, blake2b, dbPath, readOnly, ephemeral, compression, }: {
24
24
  spec: ChainSpec;
25
25
  blake2b: Blake2b;
26
26
  dbPath: string;
27
27
  readOnly?: boolean;
28
28
  ephemeral?: boolean;
29
+ compression?: boolean;
29
30
  }): HybridSerializedStates;
30
31
  private constructor();
31
32
  insertInitialState(headerHash: HeaderHash, entries: StateEntries): Promise<Result<OK, StateUpdateError>>;
@@ -33,6 +34,7 @@ export declare class HybridSerializedStates implements StatesDb<SerializedState<
33
34
  getStateRoot(state: SerializedState<LeafDb>): Promise<StateRootHash>;
34
35
  getState(header: HeaderHash): SerializedState<LeafDb> | null;
35
36
  markUnused(header: HeaderHash): void;
37
+ diskSizeInBytes(): number | null;
36
38
  close(): Promise<void>;
37
39
  /** Write new large values to LMDB in one transaction. */
38
40
  private writeValues;
@@ -1 +1 @@
1
- {"version":3,"file":"hybrid-states.d.ts","sourceRoot":"","sources":["../../../../../packages/jam/database-lmdb/hybrid-states.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAGlE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,EACL,KAAK,YAAY,EACjB,MAAM,EACN,KAAK,QAAQ,EACb,gBAAgB,EAGjB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,KAAK,EAAE,cAAc,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAC9D,OAAO,EACL,eAAe,EACf,KAAK,YAAY,EAGlB,MAAM,gCAAgC,CAAC;AAExC,OAAO,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAG9C;;;;;;;GAOG;AACH,qBAAa,sBAAuB,YAAW,QAAQ,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC,EAAE,YAAY,CAAC,YAAY,CAAC;IAwBxG,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,IAAI;IAzBvB,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAyE;IACrG,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAQ;IAEnC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAW;IAEpC,MAAM,CAAC,GAAG,CAAC,EACT,IAAI,EACJ,OAAO,EACP,MAAM,EACN,QAAQ,EACR,SAAS,GACV,EAAE;QACD,IAAI,EAAE,SAAS,CAAC;QAChB,OAAO,EAAE,OAAO,CAAC;QACjB,MAAM,EAAE,MAAM,CAAC;QACf,QAAQ,CAAC,EAAE,OAAO,CAAC;QACnB,SAAS,CAAC,EAAE,OAAO,CAAC;KACrB;IAKD,OAAO;IASD,kBAAkB,CAAC,UAAU,EAAE,UAAU,EAAE,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,gBAAgB,CAAC,CAAC;IAcxG,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;IAkBlC,YAAY,CAAC,KAAK,EAAE,eAAe,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,aAAa,CAAC;IAI1E,QAAQ,CAAC,MAAM,EAAE,UAAU,GAAG,eAAe,CAAC,MAAM,CAAC,GAAG,IAAI;IAS5D,UAAU,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI;IAM9B,KAAK;IAKX,yDAAyD;YAC3C,WAAW;IAgBzB,8BAA8B;IAC9B,OAAO,CAAC,SAAS;CAOlB"}
1
+ {"version":3,"file":"hybrid-states.d.ts","sourceRoot":"","sources":["../../../../../packages/jam/database-lmdb/hybrid-states.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAGlE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,EACL,KAAK,YAAY,EACjB,MAAM,EACN,KAAK,QAAQ,EACb,gBAAgB,EAGjB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,KAAK,EAAE,cAAc,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAC9D,OAAO,EACL,eAAe,EACf,KAAK,YAAY,EAGlB,MAAM,gCAAgC,CAAC;AAExC,OAAO,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAG9C;;;;;;;GAOG;AACH,qBAAa,sBAAuB,YAAW,QAAQ,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC,EAAE,YAAY,CAAC,YAAY,CAAC;IA0BxG,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,IAAI;IA3BvB,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAyE;IACrG,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAQ;IAEnC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAW;IAEpC,MAAM,CAAC,GAAG,CAAC,EACT,IAAI,EACJ,OAAO,EACP,MAAM,EACN,QAAQ,EACR,SAAS,EACT,WAAW,GACZ,EAAE;QACD,IAAI,EAAE,SAAS,CAAC;QAChB,OAAO,EAAE,OAAO,CAAC;QACjB,MAAM,EAAE,MAAM,CAAC;QACf,QAAQ,CAAC,EAAE,OAAO,CAAC;QACnB,SAAS,CAAC,EAAE,OAAO,CAAC;QACpB,WAAW,CAAC,EAAE,OAAO,CAAC;KACvB;IAKD,OAAO;IASD,kBAAkB,CAAC,UAAU,EAAE,UAAU,EAAE,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,gBAAgB,CAAC,CAAC;IAcxG,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;IAkBlC,YAAY,CAAC,KAAK,EAAE,eAAe,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,aAAa,CAAC;IAI1E,QAAQ,CAAC,MAAM,EAAE,UAAU,GAAG,eAAe,CAAC,MAAM,CAAC,GAAG,IAAI;IAS5D,UAAU,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI;IAMpC,eAAe,IAAI,MAAM,GAAG,IAAI;IAI1B,KAAK;IAKX,yDAAyD;YAC3C,WAAW;IAgBzB,8BAA8B;IAC9B,OAAO,CAAC,SAAS;CAOlB"}
@@ -20,8 +20,8 @@ export class HybridSerializedStates {
20
20
  lmdbValues;
21
21
  // A single shared values accessor reused by every `LeafDb` we hand out.
22
22
  valuesDb;
23
- static new({ spec, blake2b, dbPath, readOnly, ephemeral, }) {
24
- const root = LmdbRoot.new(dbPath, readOnly, ephemeral);
23
+ static new({ spec, blake2b, dbPath, readOnly, ephemeral, compression, }) {
24
+ const root = LmdbRoot.new(dbPath, { readOnly, ephemeral, compression });
25
25
  return new HybridSerializedStates(spec, blake2b, root);
26
26
  }
27
27
  constructor(spec, blake2b, root) {
@@ -73,6 +73,9 @@ export class HybridSerializedStates {
73
73
  // but since they are stored on disk we should be safe.
74
74
  this.inMemStates.delete(header);
75
75
  }
76
+ diskSizeInBytes() {
77
+ return this.root.sizeInBytes();
78
+ }
76
79
  async close() {
77
80
  await this.lmdbValues.close();
78
81
  await this.root.close();
@@ -1,12 +1,25 @@
1
1
  import * as lmdb from "lmdb";
2
2
  export type SubDb = lmdb.Database<Uint8Array, lmdb.Key>;
3
+ export type LmdbRootOptions = {
4
+ readOnly?: boolean;
5
+ ephemeral?: boolean;
6
+ compression?: boolean;
7
+ };
3
8
  /** A thin abstraction over lmdb database interface. */
4
9
  export declare class LmdbRoot {
5
10
  readonly db: lmdb.RootDatabase<Uint8Array, lmdb.Key>;
6
- static new(dbPath: string, readOnly?: boolean, ephemeral?: boolean): LmdbRoot;
11
+ /** Path of the underlying LMDB data file, used to report on-disk usage. */
12
+ private readonly dataFilePath;
13
+ static new(dbPath: string, options: LmdbRootOptions): LmdbRoot;
7
14
  private constructor();
8
15
  /** Open a sub-database under the same path. */
9
16
  subDb(name: string): SubDb;
17
+ /**
18
+ * Apparent on-disk size of the LMDB data file, in bytes.
19
+ *
20
+ * Returns `null` if the file cannot be `stat`-ed (e.g. not yet created).
21
+ */
22
+ sizeInBytes(): number | null;
10
23
  /** Close the database and all sub-databases. */
11
24
  close(): Promise<void>;
12
25
  }
@@ -1 +1 @@
1
- {"version":3,"file":"root.d.ts","sourceRoot":"","sources":["../../../../../packages/jam/database-lmdb/root.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAE7B,MAAM,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC,UAAU,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC;AAExD,uDAAuD;AACvD,qBAAa,QAAQ;IACnB,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC,YAAY,CAAC,UAAU,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC;IAErD,MAAM,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,UAAQ,EAAE,SAAS,UAAQ;IAI9D,OAAO;IAkBP,+CAA+C;IAC/C,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,KAAK;IAI1B,gDAAgD;IAC1C,KAAK;CAGZ"}
1
+ {"version":3,"file":"root.d.ts","sourceRoot":"","sources":["../../../../../packages/jam/database-lmdb/root.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAE7B,MAAM,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC,UAAU,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC;AAExD,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB,CAAC;AAEF,uDAAuD;AACvD,qBAAa,QAAQ;IACnB,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC,YAAY,CAAC,UAAU,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC;IACrD,2EAA2E;IAC3E,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IAEtC,MAAM,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,eAAe;IAInD,OAAO;IAwBP,+CAA+C;IAC/C,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,KAAK;IAI1B;;;;OAIG;IACH,WAAW,IAAI,MAAM,GAAG,IAAI;IAQ5B,gDAAgD;IAC1C,KAAK;CAGZ"}
@@ -1,11 +1,18 @@
1
+ import * as fs from "node:fs";
1
2
  import * as lmdb from "lmdb";
2
3
  /** A thin abstraction over lmdb database interface. */
3
4
  export class LmdbRoot {
4
5
  db;
5
- static new(dbPath, readOnly = false, ephemeral = false) {
6
- return new LmdbRoot(dbPath, readOnly, ephemeral);
6
+ /** Path of the underlying LMDB data file, used to report on-disk usage. */
7
+ dataFilePath;
8
+ static new(dbPath, options) {
9
+ return new LmdbRoot(dbPath, options);
7
10
  }
8
- constructor(dbPath, readOnly = false, ephemeral = false) {
11
+ constructor(dbPath, { readOnly, ephemeral, compression }) {
12
+ // `lmdb.open` treats an extension-less path as a directory and stores the
13
+ // environment in `<dbPath>/data.mdb` (next to `lock.mdb`).
14
+ this.dataFilePath = `${dbPath}/data.mdb`;
15
+ const isEphemeral = ephemeral ?? false;
9
16
  this.db = lmdb.open(dbPath, {
10
17
  // experimental options
11
18
  noMemInit: true,
@@ -15,8 +22,8 @@ export class LmdbRoot {
15
22
  // durability is pointless, so we skip fsync and skip compressing the large
16
23
  // per-block leaf blobs. Both are pure overhead there and dominate the cost.
17
24
  // This trades disk space (uncompressed) and crash-durability for speed.
18
- compression: !ephemeral,
19
- noSync: ephemeral,
25
+ compression: compression ?? !isEphemeral,
26
+ noSync: isEphemeral,
20
27
  keyEncoding: "binary",
21
28
  encoding: "binary",
22
29
  readOnly,
@@ -26,6 +33,19 @@ export class LmdbRoot {
26
33
  subDb(name) {
27
34
  return this.db.openDB({ name });
28
35
  }
36
+ /**
37
+ * Apparent on-disk size of the LMDB data file, in bytes.
38
+ *
39
+ * Returns `null` if the file cannot be `stat`-ed (e.g. not yet created).
40
+ */
41
+ sizeInBytes() {
42
+ try {
43
+ return fs.statSync(this.dataFilePath).size;
44
+ }
45
+ catch {
46
+ return null;
47
+ }
48
+ }
29
49
  /** Close the database and all sub-databases. */
30
50
  async close() {
31
51
  await this.db.close();
@@ -69,6 +69,7 @@ export declare class LmdbStates implements StatesDb<SerializedState<LeafDb>>, In
69
69
  getStateRoot(state: SerializedState<LeafDb>): Promise<StateRootHash>;
70
70
  getState(root: HeaderHash): SerializedState<LeafDb> | null;
71
71
  markUnused(header: HeaderHash): void;
72
+ diskSizeInBytes(): number | null;
72
73
  close(): Promise<void>;
73
74
  }
74
75
  //# 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;IAS5F,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,IAAI;IAVvB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAQ;IAC/B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAQ;IAE/B,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ;IAI5D,OAAO;IASD,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"}
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;IAS5F,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,IAAI;IAVvB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAQ;IAC/B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAQ;IAE/B,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ;IAI5D,OAAO;IASD,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;IAIpC,eAAe,IAAI,MAAM,GAAG,IAAI;IAI1B,KAAK;CAGZ"}
@@ -131,6 +131,9 @@ export class LmdbStates {
131
131
  markUnused(header) {
132
132
  this.states.removeSync(header.raw);
133
133
  }
134
+ diskSizeInBytes() {
135
+ return this.root.sizeInBytes();
136
+ }
134
137
  async close() {
135
138
  await Promise.all([this.states.close(), this.values.close()]);
136
139
  }
@@ -37,7 +37,7 @@ describe("LMDB States database", () => {
37
37
  const headerHash = Bytes.zero(HASH_SIZE).asOpaque();
38
38
  const spec = tinyChainSpec;
39
39
  it("should import state and read state", async () => {
40
- const root = LmdbRoot.new(tmpDir);
40
+ const root = LmdbRoot.new(tmpDir, {});
41
41
  const states = LmdbStates.new(spec, blake2b, root);
42
42
  try {
43
43
  const emptyState = InMemoryState.empty(spec);
@@ -58,7 +58,7 @@ describe("LMDB States database", () => {
58
58
  }
59
59
  });
60
60
  it("should update the state", async () => {
61
- const root = LmdbRoot.new(tmpDir);
61
+ const root = LmdbRoot.new(tmpDir, {});
62
62
  const states = LmdbStates.new(spec, blake2b, root);
63
63
  try {
64
64
  const state = InMemoryState.empty(spec);
@@ -142,7 +142,7 @@ describe("LMDB States database", () => {
142
142
  deepEqual(Array.from(set), Array.from(SortedSet.fromArray(leafComparator, Array.from(trie.nodes.leaves()))));
143
143
  });
144
144
  it("should import more complex state", async () => {
145
- const root = LmdbRoot.new(tmpDir);
145
+ const root = LmdbRoot.new(tmpDir, {});
146
146
  const states = LmdbStates.new(spec, blake2b, root);
147
147
  try {
148
148
  const initialState = testState();
@@ -167,7 +167,7 @@ describe("LMDB States database", () => {
167
167
  }
168
168
  });
169
169
  it("should update more complex entries", async () => {
170
- const root = LmdbRoot.new(tmpDir);
170
+ const root = LmdbRoot.new(tmpDir, {});
171
171
  const states = LmdbStates.new(spec, blake2b, root);
172
172
  try {
173
173
  const state = testState();
@@ -1 +1 @@
1
- {"version":3,"file":"main-fuzz.d.ts","sourceRoot":"","sources":["../../../../../packages/jam/node/main-fuzz.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,KAAK,WAAW,EAAmB,MAAM,oBAAoB,CAAC;AACvE,OAAO,EAAE,EAAE,IAAI,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAOrD,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;AAOF;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CASpF;AAED,iFAAiF;AACjF,wBAAsB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE5D;AAED,wBAAgB,cAAc;;;;EAM7B;AAED,wBAAsB,QAAQ,CAAC,UAAU,EAAE,UAAU,EAAE,WAAW,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,uBAyGxF"}
1
+ {"version":3,"file":"main-fuzz.d.ts","sourceRoot":"","sources":["../../../../../packages/jam/node/main-fuzz.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,KAAK,WAAW,EAAmB,MAAM,oBAAoB,CAAC;AACvE,OAAO,EAAE,EAAE,IAAI,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAOrD,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;AAOF;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CASpF;AAED,iFAAiF;AACjF,wBAAsB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE5D;AAED,wBAAgB,cAAc;;;;EAM7B;AAED,wBAAsB,QAAQ,CAAC,UAAU,EAAE,UAAU,EAAE,WAAW,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,uBA2GxF"}
@@ -101,9 +101,11 @@ export async function mainFuzz(fuzzConfig, withRelPath) {
101
101
  // like the in-memory backend; only the large values live on disk.
102
102
  dummyFinalityDepth: 20,
103
103
  pruneBlocks: true,
104
- // The fuzz db is wiped on every reset, so durability is pointless:
105
- // skip fsync + compression to cut the per-block value write cost.
106
- ephemeralDb: isPersistent,
104
+ // Long full-spec sessions accumulate a large, never-pruned values db.
105
+ // Syncing lets the OS reclaim dirty mmap pages, and compression (full
106
+ // spec only, where values are big) bounds its on-disk/page-cache size.
107
+ // Tiny stays uncompressed since its db is small and speed matters more.
108
+ ephemeral: isPersistent,
107
109
  stateBackend: isPersistent ? "hybrid" : "lmdb",
108
110
  });
109
111
  };
@@ -5,7 +5,7 @@ export type ImporterOptions = {
5
5
  dummyFinalityDepth?: number;
6
6
  pruneBlocks?: boolean;
7
7
  /** Open the LMDB database without fsync/compression. Only safe for throwaway dbs (e.g. fuzzing). */
8
- ephemeralDb?: boolean;
8
+ ephemeral?: boolean;
9
9
  /** Persistent backend to use when `databaseBasePath` is set. Defaults to full LMDB. */
10
10
  stateBackend?: "lmdb" | "hybrid";
11
11
  };
@@ -1 +1 @@
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;IAC5B,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,oGAAoG;IACpG,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,uFAAuF;IACvF,YAAY,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAC;CAClC,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+FlB"}
1
+ {"version":3,"file":"main-importer.d.ts","sourceRoot":"","sources":["../../../../../packages/jam/node/main-importer.ts"],"names":[],"mappings":"AAWA,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;IAC5B,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,oGAAoG;IACpG,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,uFAAuF;IACvF,YAAY,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAC;CAClC,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,CAoGlB"}
@@ -1,5 +1,6 @@
1
1
  import { Bytes } from "#@typeberry/bytes";
2
2
  import { PvmBackend } from "#@typeberry/config";
3
+ import { KnownChainSpec } from "#@typeberry/config-node";
3
4
  import { bandersnatch, initWasm } from "#@typeberry/crypto";
4
5
  import { Blake2b, HASH_SIZE } from "#@typeberry/hash";
5
6
  import { createImporter, ImporterConfig } from "#@typeberry/importer";
@@ -28,6 +29,9 @@ export async function mainImporter(config, withRelPath, options = {}) {
28
29
  dummyFinalityDepth: tryAsU16(options.dummyFinalityDepth ?? 0),
29
30
  pruneBlocks: options.pruneBlocks ?? false,
30
31
  });
32
+ const ephemeral = options.ephemeral ?? false;
33
+ // enable compression when running full test suite
34
+ const compression = ephemeral && config.node.flavor === KnownChainSpec.Full;
31
35
  const workerConfig = dbBackend === "in-memory"
32
36
  ? InMemWorkerConfig.new({
33
37
  nodeName,
@@ -42,7 +46,8 @@ export async function mainImporter(config, withRelPath, options = {}) {
42
46
  blake2b,
43
47
  dbPath,
44
48
  workerParams,
45
- ephemeral: options.ephemeralDb ?? false,
49
+ ephemeral,
50
+ compression,
46
51
  })
47
52
  : LmdbWorkerConfig.new({
48
53
  nodeName,
@@ -50,7 +55,7 @@ export async function mainImporter(config, withRelPath, options = {}) {
50
55
  blake2b,
51
56
  dbPath,
52
57
  workerParams,
53
- ephemeral: options.ephemeralDb ?? false,
58
+ ephemeral,
54
59
  });
55
60
  // Initialize the database with genesis state and block if there isn't one.
56
61
  logger.info `🛢️ Opening database at ${dbPath}`;
@@ -21,8 +21,8 @@ declare function verifySeal(bandersnatch: BandernsatchWasm, authorKey: Bandersna
21
21
  declare function getRingCommitment(bandersnatch: BandernsatchWasm, validators: BandersnatchKey[]): Promise<Result<BandersnatchRingRoot, null>>;
22
22
  declare function verifyTickets(bandersnatch: BandernsatchWasm, numberOfValidators: number, epochRoot: BandersnatchRingRoot, tickets: readonly SignedTicket[], entropy: EntropyHash): Promise<{
23
23
  isValid: boolean;
24
- entropyHash: EntropyHash;
25
- }[]>;
24
+ tickets: EntropyHash[];
25
+ }>;
26
26
  declare function generateSeal(bandersnatch: BandernsatchWasm, authorKey: BandersnatchSecretSeed, input: BytesBlob, auxData: BytesBlob): Promise<Result<BandersnatchVrfSignature, null>>;
27
27
  export type VrfOutputHash = Opaque<OpaqueHash, "VRF Output Hash">;
28
28
  declare function getVrfOutputHash(bandersnatch: BandernsatchWasm, authorKey: BandersnatchSecretSeed, input: BytesBlob): Promise<Result<VrfOutputHash, null>>;
@@ -1 +1 @@
1
- {"version":3,"file":"bandersnatch-vrf.d.ts","sourceRoot":"","sources":["../../../../../packages/jam/safrole/bandersnatch-vrf.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,YAAY,EAAsB,MAAM,6BAA6B,CAAC;AAC/E,OAAO,EAAS,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,KAAK,EAAE,eAAe,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AACjF,OAAO,EAIL,KAAK,oBAAoB,EACzB,KAAK,wBAAwB,EAC9B,MAAM,mCAAmC,CAAC;AAC3C,OAAO,EAAa,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7D,OAAO,EAAE,KAAK,MAAM,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAwB/D,QAAA,MAAM,SAAS;;;;;;;;CAQd,CAAC;AAKF,eAAe,SAAS,CAAC;AAEzB,iBAAe,iBAAiB,CAC9B,YAAY,EAAE,gBAAgB,EAC9B,SAAS,EAAE,eAAe,EAC1B,SAAS,EAAE,wBAAwB,EACnC,OAAO,EAAE,SAAS,EAClB,qBAAqB,EAAE,SAAS,EAChC,gBAAgB,EAAE,wBAAwB,EAC1C,oBAAoB,EAAE,SAAS,GAC9B,OAAO,CAAC,MAAM,CAAC,CAAC,WAAW,EAAE,WAAW,CAAC,EAAE,IAAI,CAAC,CAAC,CAkBnD;AAED,iBAAe,UAAU,CACvB,YAAY,EAAE,gBAAgB,EAC9B,SAAS,EAAE,eAAe,EAC1B,SAAS,EAAE,wBAAwB,EACnC,OAAO,EAAE,SAAS,EAClB,qBAAqB,EAAE,SAAS,GAC/B,OAAO,CAAC,MAAM,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC,CAapC;AAED,iBAAS,iBAAiB,CACxB,YAAY,EAAE,gBAAgB,EAC9B,UAAU,EAAE,eAAe,EAAE,GAC5B,OAAO,CAAC,MAAM,CAAC,oBAAoB,EAAE,IAAI,CAAC,CAAC,CAmB7C;AAkBD,iBAAe,aAAa,CAC1B,YAAY,EAAE,gBAAgB,EAC9B,kBAAkB,EAAE,MAAM,EAC1B,SAAS,EAAE,oBAAoB,EAC/B,OAAO,EAAE,SAAS,YAAY,EAAE,EAChC,OAAO,EAAE,WAAW,GACnB,OAAO,CAAC;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,WAAW,EAAE,WAAW,CAAA;CAAE,EAAE,CAAC,CAqB3D;AAED,iBAAe,YAAY,CACzB,YAAY,EAAE,gBAAgB,EAC9B,SAAS,EAAE,sBAAsB,EACjC,KAAK,EAAE,SAAS,EAChB,OAAO,EAAE,SAAS,GACjB,OAAO,CAAC,MAAM,CAAC,wBAAwB,EAAE,IAAI,CAAC,CAAC,CAQjD;AAED,MAAM,MAAM,aAAa,GAAG,MAAM,CAAC,UAAU,EAAE,iBAAiB,CAAC,CAAC;AAElE,iBAAe,gBAAgB,CAC7B,YAAY,EAAE,gBAAgB,EAC9B,SAAS,EAAE,sBAAsB,EACjC,KAAK,EAAE,SAAS,GACf,OAAO,CAAC,MAAM,CAAC,aAAa,EAAE,IAAI,CAAC,CAAC,CAQtC;AAKD;;GAEG;AACH,iBAAe,eAAe,CAC5B,YAAY,EAAE,gBAAgB,EAC9B,QAAQ,EAAE,eAAe,EAAE,EAC3B,cAAc,EAAE,MAAM,EACtB,GAAG,EAAE,sBAAsB,EAC3B,OAAO,EAAE,WAAW,EACpB,mBAAmB,EAAE,MAAM,GAC1B,OAAO,CAAC,MAAM,CAAC,YAAY,EAAE,EAAE,IAAI,CAAC,CAAC,CA0CvC"}
1
+ {"version":3,"file":"bandersnatch-vrf.d.ts","sourceRoot":"","sources":["../../../../../packages/jam/safrole/bandersnatch-vrf.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,YAAY,EAAsB,MAAM,6BAA6B,CAAC;AAC/E,OAAO,EAAS,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,KAAK,EAAE,eAAe,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AACjF,OAAO,EAIL,KAAK,oBAAoB,EACzB,KAAK,wBAAwB,EAC9B,MAAM,mCAAmC,CAAC;AAC3C,OAAO,EAAa,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7D,OAAO,EAAE,KAAK,MAAM,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAwC/D,QAAA,MAAM,SAAS;;;;;;;;CAQd,CAAC;AAKF,eAAe,SAAS,CAAC;AAIzB,iBAAe,iBAAiB,CAC9B,YAAY,EAAE,gBAAgB,EAC9B,SAAS,EAAE,eAAe,EAC1B,SAAS,EAAE,wBAAwB,EACnC,OAAO,EAAE,SAAS,EAClB,qBAAqB,EAAE,SAAS,EAChC,gBAAgB,EAAE,wBAAwB,EAC1C,oBAAoB,EAAE,SAAS,GAC9B,OAAO,CAAC,MAAM,CAAC,CAAC,WAAW,EAAE,WAAW,CAAC,EAAE,IAAI,CAAC,CAAC,CAkBnD;AAED,iBAAe,UAAU,CACvB,YAAY,EAAE,gBAAgB,EAC9B,SAAS,EAAE,eAAe,EAC1B,SAAS,EAAE,wBAAwB,EACnC,OAAO,EAAE,SAAS,EAClB,qBAAqB,EAAE,SAAS,GAC/B,OAAO,CAAC,MAAM,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC,CAapC;AAED,iBAAS,iBAAiB,CACxB,YAAY,EAAE,gBAAgB,EAC9B,UAAU,EAAE,eAAe,EAAE,GAC5B,OAAO,CAAC,MAAM,CAAC,oBAAoB,EAAE,IAAI,CAAC,CAAC,CAe7C;AAgBD,iBAAe,aAAa,CAC1B,YAAY,EAAE,gBAAgB,EAC9B,kBAAkB,EAAE,MAAM,EAC1B,SAAS,EAAE,oBAAoB,EAC/B,OAAO,EAAE,SAAS,YAAY,EAAE,EAChC,OAAO,EAAE,WAAW,GACnB,OAAO,CAAC;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,WAAW,EAAE,CAAA;CAAE,CAAC,CA0BvD;AAGD,iBAAe,YAAY,CACzB,YAAY,EAAE,gBAAgB,EAC9B,SAAS,EAAE,sBAAsB,EACjC,KAAK,EAAE,SAAS,EAChB,OAAO,EAAE,SAAS,GACjB,OAAO,CAAC,MAAM,CAAC,wBAAwB,EAAE,IAAI,CAAC,CAAC,CAQjD;AAED,MAAM,MAAM,aAAa,GAAG,MAAM,CAAC,UAAU,EAAE,iBAAiB,CAAC,CAAC;AAGlE,iBAAe,gBAAgB,CAC7B,YAAY,EAAE,gBAAgB,EAC9B,SAAS,EAAE,sBAAsB,EACjC,KAAK,EAAE,SAAS,GACf,OAAO,CAAC,MAAM,CAAC,aAAa,EAAE,IAAI,CAAC,CAAC,CAQtC;AAKD;;GAEG;AACH,iBAAe,eAAe,CAC5B,YAAY,EAAE,gBAAgB,EAC9B,QAAQ,EAAE,eAAe,EAAE,EAC3B,cAAc,EAAE,MAAM,EACtB,GAAG,EAAE,sBAAsB,EAC3B,OAAO,EAAE,WAAW,EACpB,mBAAmB,EAAE,MAAM,GAC1B,OAAO,CAAC,MAAM,CAAC,YAAY,EAAE,EAAE,IAAI,CAAC,CAAC,CA0CvC"}
@@ -17,8 +17,23 @@ var ResultValues;
17
17
  * to overcome that we cache the results of getting ring commitment.
18
18
  * Note we can also tentatively populate this cache, before we even
19
19
  * reach the epoch change block.
20
+ *
21
+ * Keep number of entries low here, since matching is done by fully
22
+ * comparing the keys.
23
+ * To avoid array re-allocation we keep it's size constant and use
24
+ * index.
20
25
  */
21
- const ringCommitmentCache = [];
26
+ let ringCommitmentIndex = 0;
27
+ const ringCommitmentCache = [
28
+ {
29
+ keys: BytesBlob.empty(),
30
+ value: Promise.resolve(Result.error(null, () => "")),
31
+ },
32
+ {
33
+ keys: BytesBlob.empty(),
34
+ value: Promise.resolve(Result.error(null, () => "")),
35
+ },
36
+ ];
22
37
  const FUNCTIONS = {
23
38
  verifySeal,
24
39
  verifyHeaderSeals,
@@ -32,10 +47,11 @@ const FUNCTIONS = {
32
47
  // Ideally we would just export functions and figure out how to mock
33
48
  // properly in ESM.
34
49
  export default FUNCTIONS;
50
+ const VRF_SEAL_VERIFICATION_FAILED = () => "Bandersnatch VRF seal verification failed";
35
51
  async function verifyHeaderSeals(bandersnatch, authorKey, signature, payload, encodedUnsealedHeader, entropySignature, entropyPayloadPrefix) {
36
52
  const sealResult = await bandersnatch.verifyHeaderSeals(authorKey.raw, signature.raw, payload.raw, encodedUnsealedHeader.raw, entropySignature.raw, entropyPayloadPrefix.raw);
37
53
  if (sealResult[RESULT_INDEX] === ResultValues.Error) {
38
- return Result.error(null, () => "Bandersnatch VRF seal verification failed");
54
+ return Result.error(null, VRF_SEAL_VERIFICATION_FAILED);
39
55
  }
40
56
  return Result.ok([
41
57
  Bytes.fromBlob(sealResult.subarray(1, 33), HASH_SIZE).asOpaque(),
@@ -45,59 +61,61 @@ async function verifyHeaderSeals(bandersnatch, authorKey, signature, payload, en
45
61
  async function verifySeal(bandersnatch, authorKey, signature, payload, encodedUnsealedHeader) {
46
62
  const sealResult = await bandersnatch.verifySeal(authorKey.raw, signature.raw, payload.raw, encodedUnsealedHeader.raw);
47
63
  if (sealResult[RESULT_INDEX] === ResultValues.Error) {
48
- return Result.error(null, () => "Bandersnatch VRF seal verification failed");
64
+ return Result.error(null, VRF_SEAL_VERIFICATION_FAILED);
49
65
  }
50
66
  return Result.ok(Bytes.fromBlob(sealResult.subarray(1), HASH_SIZE).asOpaque());
51
67
  }
52
68
  function getRingCommitment(bandersnatch, validators) {
53
69
  const keys = BytesBlob.blobFromParts(validators.map((x) => x.raw));
54
- // We currently compare the large bytes blob, but the number of entries in the cache
55
- // must be low. If the cache ever grows larger, we should rather consider hashing the keys.
56
- const MAX_CACHE_ENTRIES = 3;
57
70
  const cacheEntry = ringCommitmentCache.find((v) => v.keys.isEqualTo(keys));
58
71
  if (cacheEntry !== undefined) {
59
72
  return cacheEntry.value;
60
73
  }
61
74
  const value = getRingCommitmentNoCache(bandersnatch, keys);
62
- ringCommitmentCache.push({
75
+ ringCommitmentCache[ringCommitmentIndex] = {
63
76
  keys,
64
77
  value,
65
- });
66
- if (ringCommitmentCache.length > MAX_CACHE_ENTRIES) {
67
- ringCommitmentCache.shift();
68
- }
78
+ };
79
+ // move the index to point at next entry to override.
80
+ ringCommitmentIndex = (ringCommitmentIndex + 1) % ringCommitmentCache.length;
69
81
  return value;
70
82
  }
83
+ const RING_COMMITMENT_FAILED = () => "Bandersnatch ring commitment calculation failed";
71
84
  async function getRingCommitmentNoCache(bandersnatch, keys) {
72
85
  const commitmentResult = await bandersnatch.getRingCommitment(keys.raw);
73
86
  if (commitmentResult[RESULT_INDEX] === ResultValues.Error) {
74
- return Result.error(null, () => "Bandersnatch ring commitment calculation failed");
87
+ return Result.error(null, RING_COMMITMENT_FAILED);
75
88
  }
76
89
  return Result.ok(Bytes.fromBlob(commitmentResult.subarray(1), BANDERSNATCH_RING_ROOT_BYTES).asOpaque());
77
90
  }
78
- // One byte for result discriminator (`ResultValues`) and the rest is entropy hash.
79
- const TICKET_RESULT_LENGTH = 1 + HASH_SIZE;
80
91
  async function verifyTickets(bandersnatch, numberOfValidators, epochRoot, tickets, entropy) {
81
92
  const contextLength = entropy.length + JAM_TICKET_SEAL.length + 1;
82
93
  const ticketsData = BytesBlob.blobFromParts(tickets.map((ticket) => BytesBlob.blobFromParts([ticket.signature.raw, JAM_TICKET_SEAL, entropy.raw, Uint8Array.of(ticket.attempt)])
83
94
  .raw)).raw;
84
95
  const verificationResult = await bandersnatch.batchVerifyTicket(numberOfValidators, epochRoot.raw, ticketsData, contextLength);
85
- return Array.from(BytesBlob.blobFrom(verificationResult).chunks(TICKET_RESULT_LENGTH)).map((result) => ({
86
- isValid: result.raw[RESULT_INDEX] === ResultValues.Ok,
87
- entropyHash: Bytes.fromBlob(result.raw.subarray(1, TICKET_RESULT_LENGTH), HASH_SIZE).asOpaque(),
88
- }));
96
+ const isValid = verificationResult[RESULT_INDEX] === ResultValues.Ok;
97
+ // NOTE: in case of failure, the hashes will be all zeros, but we can safely
98
+ // keep the same code path.
99
+ const chunks = BytesBlob.blobFrom(verificationResult.subarray(1)).chunks(HASH_SIZE);
100
+ const results = [];
101
+ for (const entropyHash of chunks) {
102
+ results.push(Bytes.fromBlob(entropyHash.raw, HASH_SIZE).asOpaque());
103
+ }
104
+ return { isValid, tickets: results };
89
105
  }
106
+ const SEAL_FAILED_ERROR = () => "Seal generation failed";
90
107
  async function generateSeal(bandersnatch, authorKey, input, auxData) {
91
108
  const result = await bandersnatch.generateSeal(authorKey.raw, input.raw, auxData.raw);
92
109
  if (result[RESULT_INDEX] === ResultValues.Error) {
93
- return Result.error(null, () => "Seal generation failed");
110
+ return Result.error(null, SEAL_FAILED_ERROR);
94
111
  }
95
112
  return Result.ok(Bytes.fromBlob(result.subarray(1), BANDERSNATCH_VRF_SIGNATURE_BYTES).asOpaque());
96
113
  }
114
+ const VRF_OUTPUT_FAILED = () => "VRF output hash generation failed";
97
115
  async function getVrfOutputHash(bandersnatch, authorKey, input) {
98
116
  const result = await bandersnatch.getVrfOutputHash(authorKey.raw, input.raw);
99
117
  if (result[RESULT_INDEX] === ResultValues.Error) {
100
- return Result.error(null, () => "VRF output hash generation failed");
118
+ return Result.error(null, VRF_OUTPUT_FAILED);
101
119
  }
102
120
  return Result.ok(Bytes.fromBlob(result.subarray(1), HASH_SIZE).asOpaque());
103
121
  }
@@ -71,8 +71,8 @@ describe("Bandersnatch verification", () => {
71
71
  "0x3a5d10abc80dda33fe3f40b3bb2e3eefd3e97dda3d617a860c9d94eb70b832ad",
72
72
  ].map((x) => Bytes.parseBytes(x, HASH_SIZE));
73
73
  const result = await bandersnatchVrf.verifyTickets(await bandersnatchWasm, bandersnatchKeys.length, commitment, tickets, entropy);
74
- assert.strictEqual(result.every((x) => x.isValid), true);
75
- assert.deepStrictEqual(result.map((x) => x.entropyHash.toString()), expectedIds.map((x) => x.toString()));
74
+ assert.strictEqual(result.isValid, true);
75
+ assert.deepStrictEqual(result.tickets.map((x) => x.toString()), expectedIds.map((x) => x.toString()));
76
76
  });
77
77
  it("should detect that one signature is incorrect", async () => {
78
78
  const tickets = [
@@ -90,14 +90,16 @@ describe("Bandersnatch verification", () => {
90
90
  },
91
91
  ];
92
92
  const entropy = Bytes.parseBytes("0xbb30a42c1e62f0afda5f0a4e8a562f7a13a24cea00ee81917b86b89e801314aa", HASH_SIZE).asOpaque();
93
+ // Batch verification fails as a whole when any signature is invalid,
94
+ // and in that case all returned entropy hashes are zeroed out.
93
95
  const expectedIds = [
94
96
  "0x0000000000000000000000000000000000000000000000000000000000000000",
95
- "0x13fecb426e0a73b84b58b9a0832b11582dc971e79c5399e69f0baf1a244c7787",
96
- "0x3a5d10abc80dda33fe3f40b3bb2e3eefd3e97dda3d617a860c9d94eb70b832ad",
97
+ "0x0000000000000000000000000000000000000000000000000000000000000000",
98
+ "0x0000000000000000000000000000000000000000000000000000000000000000",
97
99
  ].map((x) => Bytes.parseBytes(x, HASH_SIZE));
98
100
  const result = await bandersnatchVrf.verifyTickets(await bandersnatchWasm, bandersnatchKeys.length, commitment, tickets, entropy);
99
- assert.deepStrictEqual(result.map((x) => x.isValid), [false, true, true]);
100
- assert.deepStrictEqual(result.map((x) => x.entropyHash.toString()), expectedIds.map((x) => x.toString()));
101
+ assert.strictEqual(result.isValid, false);
102
+ assert.deepStrictEqual(result.tickets.map((x) => x.toString()), expectedIds.map((x) => x.toString()));
101
103
  });
102
104
  });
103
105
  describe("verifySeal", () => {
@@ -168,7 +170,7 @@ describe("Bandersnatch verification", () => {
168
170
  const commitment = await bandersnatchVrf.getRingCommitment(await bandersnatchWasm, ringKeys);
169
171
  assert.ok(commitment.isOk);
170
172
  const verifyResult = await bandersnatchVrf.verifyTickets(await bandersnatchWasm, ringKeys.length, commitment.ok, genResult.ok, entropy);
171
- assert.ok(verifyResult.every((r) => r.isValid), "Generated tickets should pass verification");
173
+ assert.ok(verifyResult.isValid, "Generated tickets should pass verification");
172
174
  });
173
175
  });
174
176
  });
@@ -260,15 +260,15 @@ export class Safrole {
260
260
  */
261
261
  // TODO [ToDr] Verify that ticket attempt is in correct range.
262
262
  const verificationResult = extrinsic.length === 0
263
- ? []
263
+ ? { isValid: true, tickets: [] }
264
264
  : await bandersnatchVrf.verifyTickets(await this.bandersnatch, validators.length, epochRoot, extrinsic, entropy);
265
+ if (!verificationResult.isValid) {
266
+ return Result.error(SafroleErrorCode.BadTicketProof, () => "Safrole: invalid ticket proof in extrinsic");
267
+ }
265
268
  const tickets = extrinsic.map((ticket, i) => ({
266
- id: verificationResult[i].entropyHash,
269
+ id: verificationResult.tickets[i],
267
270
  attempt: ticket.attempt,
268
271
  }));
269
- if (!verificationResult.every((x) => x.isValid)) {
270
- return Result.error(SafroleErrorCode.BadTicketProof, () => "Safrole: invalid ticket proof in extrinsic");
271
- }
272
272
  /**
273
273
  * Verify if tickets are sorted and unique
274
274
  *
@@ -67,10 +67,10 @@ const fakeSealingKeys = {
67
67
  describe("Safrole", () => {
68
68
  let blake2b;
69
69
  beforeEach(async () => {
70
- mock.method(bandersnatchVrf, "verifyTickets", () => Promise.resolve([
71
- { isValid: true, entropyHash: Bytes.zero(HASH_SIZE) },
72
- { isValid: true, entropyHash: Bytes.fill(HASH_SIZE, 1) },
73
- ]));
70
+ mock.method(bandersnatchVrf, "verifyTickets", () => Promise.resolve({
71
+ isValid: true,
72
+ tickets: [Bytes.zero(HASH_SIZE), Bytes.fill(HASH_SIZE, 1)],
73
+ }));
74
74
  blake2b = await Blake2b.createHasher();
75
75
  });
76
76
  afterEach(() => {
@@ -146,7 +146,7 @@ describe("Safrole", () => {
146
146
  }
147
147
  });
148
148
  it("should return bad ticket proof error", async () => {
149
- mock.method(bandersnatchVrf, "verifyTickets", () => Promise.resolve([{ isValid: false, entropyHash: Bytes.zero(HASH_SIZE) }]));
149
+ mock.method(bandersnatchVrf, "verifyTickets", () => Promise.resolve({ isValid: false, tickets: [Bytes.zero(HASH_SIZE)] }));
150
150
  const punishSet = SortedSet.fromArray(hashComparator);
151
151
  const state = {
152
152
  timeslot: tryAsTimeSlot(1),
@@ -188,10 +188,10 @@ describe("Safrole", () => {
188
188
  }
189
189
  });
190
190
  it("should return duplicated ticket error", async () => {
191
- mock.method(bandersnatchVrf, "verifyTickets", () => Promise.resolve([
192
- { isValid: true, entropyHash: Bytes.zero(HASH_SIZE) },
193
- { isValid: true, entropyHash: Bytes.zero(HASH_SIZE) },
194
- ]));
191
+ mock.method(bandersnatchVrf, "verifyTickets", () => Promise.resolve({
192
+ isValid: true,
193
+ tickets: [Bytes.zero(HASH_SIZE), Bytes.zero(HASH_SIZE)],
194
+ }));
195
195
  const punishSet = SortedSet.fromArray(hashComparator);
196
196
  const state = {
197
197
  timeslot: tryAsTimeSlot(1),
@@ -237,10 +237,10 @@ describe("Safrole", () => {
237
237
  }
238
238
  });
239
239
  it("should return bad ticket order error", async () => {
240
- mock.method(bandersnatchVrf, "verifyTickets", () => Promise.resolve([
241
- { isValid: true, entropyHash: Bytes.fill(HASH_SIZE, 1) },
242
- { isValid: true, entropyHash: Bytes.zero(HASH_SIZE) },
243
- ]));
240
+ mock.method(bandersnatchVrf, "verifyTickets", () => Promise.resolve({
241
+ isValid: true,
242
+ tickets: [Bytes.fill(HASH_SIZE, 1), Bytes.zero(HASH_SIZE)],
243
+ }));
244
244
  const punishSet = SortedSet.fromArray(hashComparator);
245
245
  const state = {
246
246
  timeslot: tryAsTimeSlot(1),
@@ -6,7 +6,7 @@ import type { DisputesErrorCode } from "#@typeberry/disputes/disputes-error-code
6
6
  import type { SafroleErrorCode, SafroleStateUpdate } from "#@typeberry/safrole/safrole.js";
7
7
  import { type SafroleSealError } from "#@typeberry/safrole/safrole-seal.js";
8
8
  import type { State, WithStateView } from "#@typeberry/state";
9
- import { type ErrorResult, Result, type TaggedError } from "#@typeberry/utils";
9
+ import { type ErrorResult, measure, Result, type TaggedError } from "#@typeberry/utils";
10
10
  import { type ACCUMULATION_ERROR, type AccumulateOptions, type AccumulateStateUpdate } from "./accumulate/index.js";
11
11
  import { type AssurancesError, type AssurancesStateUpdate } from "./assurances.js";
12
12
  import { type AuthorizationStateUpdate } from "./authorization.js";
@@ -59,6 +59,7 @@ export declare class OnChain {
59
59
  readonly chainSpec: ChainSpec;
60
60
  readonly state: State & WithStateView;
61
61
  readonly hasher: TransitionHasher;
62
+ readonly measureAccumulate: ReturnType<typeof measure>;
62
63
  /** Wire up a full on-chain STF from its dependencies. */
63
64
  static assemble(args: {
64
65
  chainSpec: ChainSpec;
@@ -1 +1 @@
1
- {"version":3,"file":"chain-stf.d.ts","sourceRoot":"","sources":["../../../../../packages/jam/transition/chain-stf.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"chain-stf.d.ts","sourceRoot":"","sources":["../../../../../packages/jam/transition/chain-stf.ts"],"names":[],"mappings":"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"}
@@ -84,6 +84,7 @@ export class OnChain {
84
84
  chainSpec;
85
85
  state;
86
86
  hasher;
87
+ measureAccumulate;
87
88
  /** Wire up a full on-chain STF from its dependencies. */
88
89
  static assemble(args) {
89
90
  return new OnChain(args.chainSpec, args.state, args.hasher, args.options, args.headerChain);
@@ -104,14 +105,25 @@ export class OnChain {
104
105
  this.accumulateOutput = new AccumulateOutput();
105
106
  this.preimages = new Preimages(state, hasher.blake2b);
106
107
  this.authorization = new Authorization(chainSpec, state);
108
+ this.measureAccumulate = measure(`import:accumulate (${PvmBackend[options.pvm]})`);
107
109
  }
108
110
  /** Pre-populate things worth caching for the next epoch. */
109
111
  async prepareForNextEpoch() {
110
112
  if (await this.isReadyForNextEpoch) {
111
113
  return;
112
114
  }
115
+ const timeslot = this.state.timeslot;
116
+ logger.log `#${timeslot} preparing for next epoch`;
113
117
  const ready = this.safrole.prepareValidatorKeysForNextEpoch(this.state.disputesRecords.punishSet);
114
- this.isReadyForNextEpoch = ready.then((_) => true);
118
+ this.isReadyForNextEpoch = ready.then((x) => {
119
+ if (x.isOk) {
120
+ logger.log `#${timeslot} next epoch ready`;
121
+ }
122
+ else {
123
+ logger.log `#${timeslot} ${x.details()}`;
124
+ }
125
+ return true;
126
+ });
115
127
  }
116
128
  async verifySeal(timeSlot, block) {
117
129
  const sealState = this.safrole.getSafroleSealState(timeSlot);
@@ -205,14 +217,14 @@ export class OnChain {
205
217
  }
206
218
  const { preimages, ...preimagesRest } = preimagesResult.ok;
207
219
  assertEmpty(preimagesRest);
208
- const timerAccumulate = measure(`import:accumulate (${PvmBackend[this.accumulate.options.pvm]})`);
220
+ const timerAccumulate = this.measureAccumulate();
209
221
  // accumulate
210
222
  const accumulateResult = await this.accumulate.transition({
211
223
  slot: timeSlot,
212
224
  reports: availableReports,
213
225
  entropy: entropy[0],
214
226
  });
215
- logger.log `${timerAccumulate()}`;
227
+ logger.log `#${timeSlot} ${timerAccumulate}`;
216
228
  if (accumulateResult.isError) {
217
229
  return stfError(StfErrorKind.Accumulate, accumulateResult);
218
230
  }
@@ -91,13 +91,15 @@ export declare class HybridWorkerConfig<T = undefined> implements WorkerConfig<T
91
91
  readonly blake2b: Blake2b;
92
92
  readonly dbPath: string;
93
93
  readonly ephemeral: boolean;
94
- static new<T>({ nodeName, chainSpec, workerParams, blake2b, dbPath, ephemeral, }: {
94
+ readonly compression: boolean;
95
+ static new<T>({ nodeName, chainSpec, workerParams, blake2b, dbPath, ephemeral, compression, }: {
95
96
  nodeName: string;
96
97
  chainSpec: ChainSpec;
97
98
  workerParams: T;
98
99
  blake2b: Blake2b;
99
100
  dbPath: string;
100
101
  ephemeral?: boolean;
102
+ compression?: boolean;
101
103
  }): HybridWorkerConfig<T>;
102
104
  private readonly blocks;
103
105
  private readonly states;
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../../../../packages/workers/api-node/config.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../../../../packages/workers/api-node/config.ts"],"names":[],"mappings":"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"}
@@ -32,9 +32,9 @@ export class LmdbWorkerConfig {
32
32
  });
33
33
  }
34
34
  constructor(nodeName, chainSpec, workerParams, dbPath, blake2b, ports,
35
- // When set, the underlying LMDB skips fsync and compression. Only safe for
36
- // throwaway databases (the fuzz target wipes on reset). Not transferred to
37
- // worker threads, so the durable main node path always gets the default.
35
+ // When set, the underlying LMDB skips fsync. Only safe for throwaway
36
+ // databases (the fuzz target wipes on reset). Not transferred to worker
37
+ // threads, so the durable main node path always gets the default.
38
38
  ephemeral = false) {
39
39
  this.nodeName = nodeName;
40
40
  this.chainSpec = chainSpec;
@@ -45,7 +45,10 @@ export class LmdbWorkerConfig {
45
45
  this.ephemeral = ephemeral;
46
46
  }
47
47
  openDatabase(options = { readonly: true }) {
48
- const lmdb = LmdbRoot.new(this.dbPath, options.readonly, this.ephemeral);
48
+ const lmdb = LmdbRoot.new(this.dbPath, {
49
+ readOnly: options.readonly,
50
+ ephemeral: this.ephemeral,
51
+ });
49
52
  return {
50
53
  getBlocksDb: () => LmdbBlocks.new(this.chainSpec, lmdb),
51
54
  getStatesDb: () => LmdbStates.new(this.chainSpec, this.blake2b, lmdb),
@@ -125,24 +128,27 @@ export class HybridWorkerConfig {
125
128
  blake2b;
126
129
  dbPath;
127
130
  ephemeral;
128
- static new({ nodeName, chainSpec, workerParams, blake2b, dbPath, ephemeral = false, }) {
129
- return new HybridWorkerConfig(nodeName, chainSpec, workerParams, blake2b, dbPath, ephemeral);
131
+ compression;
132
+ static new({ nodeName, chainSpec, workerParams, blake2b, dbPath, ephemeral = false, compression = true, }) {
133
+ return new HybridWorkerConfig(nodeName, chainSpec, workerParams, blake2b, dbPath, ephemeral, compression);
130
134
  }
131
135
  blocks;
132
136
  states;
133
- constructor(nodeName, chainSpec, workerParams, blake2b, dbPath, ephemeral) {
137
+ constructor(nodeName, chainSpec, workerParams, blake2b, dbPath, ephemeral, compression = true) {
134
138
  this.nodeName = nodeName;
135
139
  this.chainSpec = chainSpec;
136
140
  this.workerParams = workerParams;
137
141
  this.blake2b = blake2b;
138
142
  this.dbPath = dbPath;
139
143
  this.ephemeral = ephemeral;
144
+ this.compression = compression;
140
145
  this.blocks = InMemoryBlocks.new();
141
146
  this.states = HybridSerializedStates.new({
142
147
  spec: this.chainSpec,
143
148
  blake2b: this.blake2b,
144
149
  dbPath: this.dbPath,
145
150
  ephemeral: this.ephemeral,
151
+ compression: this.compression,
146
152
  readOnly: false,
147
153
  });
148
154
  }
@@ -1 +1 @@
1
- {"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../../../../packages/workers/block-authorship/main.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qCAAqC,CAAC;AAiB3E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAE3D,OAAO,KAAK,EAAE,qBAAqB,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAK9E,KAAK,MAAM,GAAG,YAAY,CAAC,qBAAqB,CAAC,CAAC;AAwBlD,wBAAsB,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,iBAAiB,EAAE,eAAe,EAAE,eAAe,iBAiapG"}
1
+ {"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../../../../packages/workers/block-authorship/main.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qCAAqC,CAAC;AAiB3E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAE3D,OAAO,KAAK,EAAE,qBAAqB,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAK9E,KAAK,MAAM,GAAG,YAAY,CAAC,qBAAqB,CAAC,CAAC;AAwBlD,wBAAsB,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,iBAAiB,EAAE,eAAe,EAAE,eAAe,iBAmapG"}
@@ -232,13 +232,15 @@ export async function main(config, comms, networkingComms) {
232
232
  */
233
233
  async function verifyAndAddToPool(epochIndex, tickets, state) {
234
234
  const results = await bandersnatchVrf.verifyTickets(bandersnatch, state.designatedValidatorData.length, state.epochRoot, tickets, getTicketEntropy(epochIndex, state));
235
- if (results.length !== tickets.length) {
236
- logger.error `verifyTickets returned ${results.length} results for ${tickets.length} tickets`;
235
+ if (results.tickets.length !== tickets.length) {
236
+ logger.error `verifyTickets returned ${results.tickets.length} results for ${tickets.length} tickets`;
237
237
  return false;
238
238
  }
239
- const verified = tickets
240
- .map((ticket, i) => ({ ticket, id: results[i].entropyHash }))
241
- .filter((_, i) => results[i].isValid);
239
+ // Batch verification: either the whole batch is valid or none of the tickets are.
240
+ if (!results.isValid) {
241
+ return false;
242
+ }
243
+ const verified = tickets.map((ticket, i) => ({ ticket, id: results.tickets[i] }));
242
244
  addToPool(epochIndex, verified);
243
245
  return verified.length > 0;
244
246
  }
@@ -9,6 +9,7 @@ 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
11
  import type { Finalizer } from "./finality.js";
12
+ import { type ImporterEventsListener } from "./stats.js";
12
13
  export declare enum ImporterErrorKind {
13
14
  Verifier = 0,
14
15
  Stf = 1,
@@ -29,6 +30,7 @@ export type ImporterArgs = {
29
30
  blocks: BlocksDb;
30
31
  states: StatesDb<SerializedState<LeafDb>>;
31
32
  options?: ImporterOptions;
33
+ events?: ImporterEventsListener;
32
34
  };
33
35
  export declare class Importer {
34
36
  private readonly verifier;
@@ -36,12 +38,12 @@ export declare class Importer {
36
38
  private readonly state;
37
39
  private currentHash;
38
40
  private readonly metrics;
39
- private readonly memory;
40
41
  private readonly hasher;
41
42
  private readonly logger;
42
43
  private readonly blocks;
43
44
  private readonly states;
44
45
  private readonly options;
46
+ private readonly events;
45
47
  /**
46
48
  * Build an {@link Importer} connected to the best state loaded from `states`.
47
49
  *
@@ -1 +1 @@
1
- {"version":3,"file":"importer.d.ts","sourceRoot":"","sources":["../../../../../packages/workers/importer/importer.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"importer.d.ts","sourceRoot":"","sources":["../../../../../packages/workers/importer/importer.ts"],"names":[],"mappings":"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"}
@@ -2,8 +2,9 @@ import { tryAsTimeSlot } from "#@typeberry/block";
2
2
  import { WithHash } from "#@typeberry/hash";
3
3
  import { BlockVerifier, BlockVerifierError } from "#@typeberry/transition/block-verifier.js";
4
4
  import { DbHeaderChain, OnChain } from "#@typeberry/transition/chain-stf.js";
5
- import { measure, memoryTracker, now, Result, resultToString, } from "#@typeberry/utils";
5
+ import { measure, Result, resultToString } from "#@typeberry/utils";
6
6
  import * as metrics from "./metrics.js";
7
+ import { ImporterStats } from "./stats.js";
7
8
  export var ImporterErrorKind;
8
9
  (function (ImporterErrorKind) {
9
10
  ImporterErrorKind[ImporterErrorKind["Verifier"] = 0] = "Verifier";
@@ -11,6 +12,12 @@ export var ImporterErrorKind;
11
12
  ImporterErrorKind[ImporterErrorKind["Update"] = 2] = "Update";
12
13
  })(ImporterErrorKind || (ImporterErrorKind = {}));
13
14
  const importerError = (kind, nested) => Result.taggedError(ImporterErrorKind, kind, nested);
15
+ const MEASURE = {
16
+ importVerify: measure("import:verify"),
17
+ importStf: measure("import:stf"),
18
+ importState: measure("import:state"),
19
+ importDb: measure("import:db"),
20
+ };
14
21
  export class Importer {
15
22
  verifier;
16
23
  stf;
@@ -19,12 +26,12 @@ export class Importer {
19
26
  // Hash of the block that we have the posterior state for in `state`.
20
27
  currentHash;
21
28
  metrics;
22
- memory = memoryTracker();
23
29
  hasher;
24
30
  logger;
25
31
  blocks;
26
32
  states;
27
33
  options;
34
+ events;
28
35
  /**
29
36
  * Build an {@link Importer} connected to the best state loaded from `states`.
30
37
  *
@@ -45,6 +52,7 @@ export class Importer {
45
52
  this.blocks = args.blocks;
46
53
  this.states = args.states;
47
54
  this.options = args.options ?? {};
55
+ this.events = args.events ?? ImporterStats.new(args.logger, () => this.states.diskSizeInBytes?.() ?? null);
48
56
  this.metrics = metrics.createMetrics();
49
57
  this.verifier = BlockVerifier.new(args.hasher, args.blocks);
50
58
  this.stf = OnChain.assemble({
@@ -57,7 +65,7 @@ export class Importer {
57
65
  this.state = state;
58
66
  this.currentHash = currentBestHeaderHash;
59
67
  this.prepareForNextEpoch();
60
- args.logger.info `😎 Best time slot: ${state.timeslot} (header hash: ${currentBestHeaderHash})`;
68
+ this.events.onStart(currentBestHeaderHash, state);
61
69
  }
62
70
  /** Do some extra work for preparation for the next epoch. */
63
71
  async prepareForNextEpoch() {
@@ -76,42 +84,44 @@ export class Importer {
76
84
  return res;
77
85
  }
78
86
  async importBlock(block) {
79
- const timer = measure("importBlock");
80
87
  const timeSlot = extractTimeSlot(block);
88
+ const onEnd = this.events.onBlockImportingStarted(timeSlot);
81
89
  this.metrics.recordBlockImportingStarted(timeSlot);
82
- const startTime = now();
83
- const maybeBestHeader = await this.importBlockInternal(block);
84
- const duration = now() - startTime;
85
- if (maybeBestHeader.isOk) {
86
- if (timeSlot % 100 === 0) {
87
- this.logger.info `📊 mem #${timeSlot}: ${this.memory()}`;
88
- }
89
- const bestHeader = maybeBestHeader.ok;
90
- this.logger.info `🧊 Best block: #${timeSlot} (${bestHeader.hash})`;
91
- this.logger.log `${timer()}`;
92
- this.metrics.recordBlockImportComplete(duration, true);
90
+ let maybeBestHeader = null;
91
+ try {
92
+ maybeBestHeader = await this.importBlockInternal(block);
93
93
  return maybeBestHeader;
94
94
  }
95
- this.logger.log `❌ Rejected block #${timeSlot}: ${resultToString(maybeBestHeader)}`;
96
- this.logger.log `${timer()}`;
97
- this.metrics.recordBlockImportComplete(duration, false);
98
- return maybeBestHeader;
95
+ finally {
96
+ const isOk = maybeBestHeader?.isOk ?? false;
97
+ const duration = onEnd(isOk);
98
+ // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
99
+ if (maybeBestHeader?.isOk) {
100
+ const bestHeader = maybeBestHeader.ok;
101
+ this.logger.info `🧊 Best block: #${timeSlot} (${bestHeader.hash})`;
102
+ this.metrics.recordBlockImportComplete(duration, true);
103
+ }
104
+ else {
105
+ this.logger
106
+ .log `❌ Rejected block #${timeSlot}: ${maybeBestHeader !== null ? resultToString(maybeBestHeader) : "exception"}`;
107
+ this.metrics.recordBlockImportComplete(duration, false);
108
+ }
109
+ }
99
110
  }
100
111
  async importBlockInternal(block) {
101
112
  const logger = this.logger;
102
113
  logger.log `🧱 Attempting to import a new block`;
103
- const timerVerify = measure("import:verify");
104
- const verifyStart = now();
114
+ const timerVerify = MEASURE.importVerify();
105
115
  const hash = await this.verifier.verifyBlock(block, {
106
116
  skipParentAndStateRoot: this.options.initGenesisFromAncestry ?? false,
107
117
  });
108
- const verifyDuration = now() - verifyStart;
109
- logger.log `${timerVerify()}`;
110
118
  if (hash.isError) {
119
+ logger.log `${timerVerify}`;
111
120
  this.metrics.recordBlockVerificationFailed(resultToString(hash));
112
121
  return importerError(ImporterErrorKind.Verifier, hash);
113
122
  }
114
- this.metrics.recordBlockVerified(verifyDuration);
123
+ logger.log `${hash.ok} ${timerVerify}`;
124
+ this.metrics.recordBlockVerified(timerVerify.duration());
115
125
  // TODO [ToDr] This is incomplete/temporary fork support!
116
126
  const parentHash = block.header.view().parentHeaderHash.materialize();
117
127
  if (!this.currentHash.isEqualTo(parentHash)) {
@@ -130,19 +140,17 @@ export class Importer {
130
140
  const timeSlot = block.header.view().timeSlotIndex.materialize();
131
141
  const headerHash = hash.ok;
132
142
  logger.log `🧱 Verified block: Got hash ${headerHash} for block at slot ${timeSlot}.`;
133
- const timerStf = measure("import:stf");
134
- const stfStart = now();
143
+ const timerStf = MEASURE.importStf();
135
144
  const res = await this.stf.transition(block, headerHash);
136
- const stfDuration = now() - stfStart;
137
- logger.log `${timerStf()}`;
145
+ logger.log `${headerHash} ${timerStf}`;
138
146
  if (res.isError) {
139
147
  this.metrics.recordBlockExecutionFailed(resultToString(res));
140
148
  return importerError(ImporterErrorKind.Stf, res);
141
149
  }
142
- this.metrics.recordBlockExecuted(stfDuration, 0);
150
+ this.metrics.recordBlockExecuted(timerStf.duration(), 0);
143
151
  // modify the state
144
152
  const update = res.ok;
145
- const timerState = measure("import:state");
153
+ const timerState = MEASURE.importState();
146
154
  const updateResult = await this.states.updateAndSetState(headerHash, this.state, update);
147
155
  if (updateResult.isError) {
148
156
  logger.error `🧱 Unable to update state: ${resultToString(updateResult)}`;
@@ -150,9 +158,9 @@ export class Importer {
150
158
  }
151
159
  this.prepareForNextEpoch();
152
160
  this.currentHash = headerHash;
153
- logger.log `${timerState()}`;
161
+ logger.log `${timerState}`;
154
162
  // insert new state and the block to DB.
155
- const timerDb = measure("import:db");
163
+ const timerDb = MEASURE.importDb();
156
164
  const writeBlocks = this.blocks.insertBlock(WithHash.new(headerHash, block));
157
165
  // Computation of the state root may happen asynchronously,
158
166
  // but we still need to wait for it before next block can be imported
@@ -160,7 +168,7 @@ export class Importer {
160
168
  logger.log `🧱 Storing post-state-root for ${headerHash}: ${stateRoot}.`;
161
169
  const writeStateRoot = this.blocks.setPostStateRoot(headerHash, stateRoot);
162
170
  await Promise.all([writeBlocks, writeStateRoot]);
163
- logger.log `${timerDb()}`;
171
+ logger.log `${headerHash} ${timerDb}`;
164
172
  // finally update the best block
165
173
  await this.blocks.setBestHeaderHash(headerHash);
166
174
  // check for finality and prune old states (and optionally blocks)
@@ -0,0 +1,38 @@
1
+ import type { HeaderHash, TimeSlot } from "#@typeberry/block";
2
+ import type { LeafDb } from "#@typeberry/database";
3
+ import type { Logger } from "#@typeberry/logger";
4
+ import type { SerializedState } from "#@typeberry/state-merkleization";
5
+ /** Reports the current on-disk database size in bytes, or `null` when unknown. */
6
+ export type DbSizeProvider = () => number | null;
7
+ /** Events happening during block imports. */
8
+ export interface ImporterEventsListener {
9
+ /**
10
+ * Invoked when we start importing a block.
11
+ *
12
+ * Must return a callback that will be triggered at the end of block import.
13
+ * The callback is expected to return duration between start and end events.
14
+ */
15
+ onBlockImportingStarted(timeSlot: TimeSlot): (isOk: boolean) => number;
16
+ /** Initial state of the importer. */
17
+ onStart(currentBestHeaderHash: HeaderHash, currentBestState: SerializedState<LeafDb>): void;
18
+ }
19
+ export declare class ImporterStats implements ImporterEventsListener {
20
+ private readonly logger;
21
+ /** Reports the current on-disk database size in bytes, or `null` if unknown. */
22
+ private readonly dbSizeInBytes;
23
+ /** How often we are going to print the stats (i.e. every `maxBlocks` blocks) */
24
+ private readonly maxBlocks;
25
+ /** Alternatively print stats when we reach `${maxTimeMs}` of total block execution. */
26
+ private readonly maxTimeMs;
27
+ private readonly memory;
28
+ private showDiskStats;
29
+ private totalTimePrev;
30
+ private totalTime;
31
+ private totalBlocksPrev;
32
+ private totalBlocks;
33
+ static new(logger: Logger, dbSizeInBytes?: DbSizeProvider): ImporterStats;
34
+ private constructor();
35
+ onStart(currentBestHeaderHash: HeaderHash, currentBestState: SerializedState<LeafDb>): void;
36
+ onBlockImportingStarted(timeSlot: TimeSlot): (isOk: boolean) => number;
37
+ }
38
+ //# sourceMappingURL=stats.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stats.d.ts","sourceRoot":"","sources":["../../../../../packages/workers/importer/stats.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC7D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AAGtE,kFAAkF;AAClF,MAAM,MAAM,cAAc,GAAG,MAAM,MAAM,GAAG,IAAI,CAAC;AAWjD,6CAA6C;AAC7C,MAAM,WAAW,sBAAsB;IACrC;;;;;OAKG;IACH,uBAAuB,CAAC,QAAQ,EAAE,QAAQ,GAAG,CAAC,IAAI,EAAE,OAAO,KAAK,MAAM,CAAC;IAEvE,qCAAqC;IACrC,OAAO,CAAC,qBAAqB,EAAE,UAAU,EAAE,gBAAgB,EAAE,eAAe,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC;CAC7F;AAED,qBAAa,aAAc,YAAW,sBAAsB;IAaxD,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,gFAAgF;IAChF,OAAO,CAAC,QAAQ,CAAC,aAAa;IAC9B,gFAAgF;IAChF,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,uFAAuF;IACvF,OAAO,CAAC,QAAQ,CAAC,SAAS;IAlB5B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAmB;IAC1C,OAAO,CAAC,aAAa,CAAQ;IAC7B,OAAO,CAAC,aAAa,CAAK;IAC1B,OAAO,CAAC,SAAS,CAAK;IACtB,OAAO,CAAC,eAAe,CAAK;IAC5B,OAAO,CAAC,WAAW,CAAK;IAExB,MAAM,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,aAAa,GAAE,cAA2B;IAIrE,OAAO;IAUP,OAAO,CAAC,qBAAqB,EAAE,UAAU,EAAE,gBAAgB,EAAE,eAAe,CAAC,MAAM,CAAC;IAIpF,uBAAuB,CAAC,QAAQ,EAAE,QAAQ,IAGhC,MAAM,OAAO;CAiCxB"}
@@ -0,0 +1,69 @@
1
+ import { memoryTracker, now } from "#@typeberry/utils";
2
+ /** Format a database size for the stats line, e.g. ` db=12.34GB`. Empty when unknown. */
3
+ function formatDbSize(bytes) {
4
+ if (bytes === null) {
5
+ return "";
6
+ }
7
+ const mb = bytes / (1024 * 1024);
8
+ return mb >= 1024 ? ` db=${(mb / 1024).toFixed(2)}GB` : ` db=${mb.toFixed(1)}MB`;
9
+ }
10
+ export class ImporterStats {
11
+ logger;
12
+ dbSizeInBytes;
13
+ maxBlocks;
14
+ maxTimeMs;
15
+ memory = memoryTracker();
16
+ showDiskStats = true;
17
+ totalTimePrev = 0;
18
+ totalTime = 0;
19
+ totalBlocksPrev = 0;
20
+ totalBlocks = 0;
21
+ static new(logger, dbSizeInBytes = () => null) {
22
+ return new ImporterStats(logger, dbSizeInBytes);
23
+ }
24
+ constructor(logger,
25
+ /** Reports the current on-disk database size in bytes, or `null` if unknown. */
26
+ dbSizeInBytes = () => null,
27
+ /** How often we are going to print the stats (i.e. every `maxBlocks` blocks) */
28
+ maxBlocks = 100,
29
+ /** Alternatively print stats when we reach `${maxTimeMs}` of total block execution. */
30
+ maxTimeMs = 5000) {
31
+ this.logger = logger;
32
+ this.dbSizeInBytes = dbSizeInBytes;
33
+ this.maxBlocks = maxBlocks;
34
+ this.maxTimeMs = maxTimeMs;
35
+ }
36
+ onStart(currentBestHeaderHash, currentBestState) {
37
+ this.logger.info `😎 Best time slot: ${currentBestState.timeslot} (header hash: ${currentBestHeaderHash})`;
38
+ }
39
+ onBlockImportingStarted(timeSlot) {
40
+ const start = now();
41
+ return (isOk) => {
42
+ const duration = now() - start;
43
+ const label = isOk ? "import" : "reject";
44
+ this.logger.log `⏱️ ${label} #${timeSlot} took ${duration.toFixed(2)}ms`;
45
+ this.totalTime += duration;
46
+ this.totalBlocks += 1;
47
+ if (this.totalBlocks >= this.maxBlocks || this.totalTime >= this.maxTimeMs) {
48
+ // disk data (every second output)
49
+ if (this.showDiskStats) {
50
+ this.logger.info `💾 disk at #${timeSlot}: ${formatDbSize(this.dbSizeInBytes())}`;
51
+ }
52
+ this.showDiskStats = !this.showDiskStats;
53
+ // memory
54
+ this.logger.info `📊 mem at #${timeSlot}: ${this.memory()}`;
55
+ // compute block statistics (rolling window of last two rounds)
56
+ const importedBlocks = this.totalBlocks + this.totalBlocksPrev;
57
+ const importTime = this.totalTime + this.totalTimePrev;
58
+ const blocksPerSecond = (importedBlocks / importTime) * 1000;
59
+ // carry over current round
60
+ this.totalBlocksPrev = this.totalBlocks;
61
+ this.totalTimePrev = this.totalTime;
62
+ this.totalBlocks = 0;
63
+ this.totalTime = 0;
64
+ this.logger.info `⏱️ time at #${timeSlot}: ${blocksPerSecond.toFixed(2)}bps`;
65
+ }
66
+ return duration;
67
+ };
68
+ }
69
+ }