@typeberry/lib 0.9.0-6850f84 → 0.9.0-a723180

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 (71) hide show
  1. package/package.json +1 -1
  2. package/packages/jam/config-node/node-config.d.ts +20 -2
  3. package/packages/jam/config-node/node-config.d.ts.map +1 -1
  4. package/packages/jam/config-node/node-config.js +25 -3
  5. package/packages/jam/config-node/node-config.test.js +8 -1
  6. package/packages/jam/database/serialized-states-db.d.ts +4 -1
  7. package/packages/jam/database/serialized-states-db.d.ts.map +1 -1
  8. package/packages/jam/database/serialized-states-db.js +17 -5
  9. package/packages/jam/database/serialized-states-db.test.d.ts +2 -0
  10. package/packages/jam/database/serialized-states-db.test.d.ts.map +1 -0
  11. package/packages/jam/database/serialized-states-db.test.js +109 -0
  12. package/packages/jam/database-fjall/blocks.d.ts +25 -0
  13. package/packages/jam/database-fjall/blocks.d.ts.map +1 -0
  14. package/packages/jam/database-fjall/blocks.js +86 -0
  15. package/packages/jam/database-fjall/blocks.test.d.ts +2 -0
  16. package/packages/jam/database-fjall/blocks.test.d.ts.map +1 -0
  17. package/packages/jam/database-fjall/blocks.test.js +62 -0
  18. package/packages/jam/database-fjall/hybrid-states.d.ts.map +1 -1
  19. package/packages/jam/database-fjall/hybrid-states.js +11 -2
  20. package/packages/jam/database-fjall/hybrid-states.test.js +94 -2
  21. package/packages/jam/database-fjall/index.d.ts +2 -0
  22. package/packages/jam/database-fjall/index.d.ts.map +1 -1
  23. package/packages/jam/database-fjall/index.js +2 -0
  24. package/packages/jam/database-fjall/root.d.ts +14 -8
  25. package/packages/jam/database-fjall/root.d.ts.map +1 -1
  26. package/packages/jam/database-fjall/root.js +25 -9
  27. package/packages/jam/database-fjall/states.d.ts +31 -0
  28. package/packages/jam/database-fjall/states.d.ts.map +1 -0
  29. package/packages/jam/database-fjall/states.js +99 -0
  30. package/packages/jam/database-fjall/states.test.d.ts +2 -0
  31. package/packages/jam/database-fjall/states.test.d.ts.map +1 -0
  32. package/packages/jam/database-fjall/states.test.js +150 -0
  33. package/packages/jam/database-lmdb/hybrid-states.test.js +94 -2
  34. package/packages/jam/node/export.d.ts.map +1 -1
  35. package/packages/jam/node/export.js +18 -9
  36. package/packages/jam/node/main-importer.d.ts +4 -2
  37. package/packages/jam/node/main-importer.d.ts.map +1 -1
  38. package/packages/jam/node/main-importer.js +26 -12
  39. package/packages/jam/node/main.d.ts.map +1 -1
  40. package/packages/jam/node/main.js +42 -15
  41. package/packages/jam/node/workers.d.ts +4 -4
  42. package/packages/jam/node/workers.d.ts.map +1 -1
  43. package/packages/workers/api/config.d.ts +2 -2
  44. package/packages/workers/api/config.d.ts.map +1 -1
  45. package/packages/workers/api/config.js +1 -1
  46. package/packages/workers/api/port.d.ts +5 -1
  47. package/packages/workers/api/port.d.ts.map +1 -1
  48. package/packages/workers/api/port.js +56 -13
  49. package/packages/workers/api/port.test.js +35 -0
  50. package/packages/workers/api-node/config.d.ts +45 -4
  51. package/packages/workers/api-node/config.d.ts.map +1 -1
  52. package/packages/workers/api-node/config.js +127 -9
  53. package/packages/workers/api-node/config.test.js +52 -3
  54. package/packages/workers/api-node/port.d.ts +3 -0
  55. package/packages/workers/api-node/port.d.ts.map +1 -1
  56. package/packages/workers/api-node/port.js +31 -2
  57. package/packages/workers/api-node/port.test.js +17 -0
  58. package/packages/workers/api-node/protocol.d.ts +3 -3
  59. package/packages/workers/api-node/protocol.d.ts.map +1 -1
  60. package/packages/workers/api-node/protocol.js +18 -12
  61. package/packages/workers/block-authorship/main.js +1 -1
  62. package/packages/workers/importer/finality.d.ts +4 -0
  63. package/packages/workers/importer/finality.d.ts.map +1 -1
  64. package/packages/workers/importer/finality.js +10 -1
  65. package/packages/workers/importer/finality.test.js +6 -0
  66. package/packages/workers/importer/importer.d.ts.map +1 -1
  67. package/packages/workers/importer/importer.js +3 -0
  68. package/packages/workers/importer/main.d.ts +1 -1
  69. package/packages/workers/importer/main.d.ts.map +1 -1
  70. package/packages/workers/importer/main.js +1 -1
  71. package/packages/workers/jam-network/main.js +1 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@typeberry/lib",
3
- "version": "0.9.0-6850f84",
3
+ "version": "0.9.0-a723180",
4
4
  "description": "Typeberry Library",
5
5
  "main": "./bin/lib/index.js",
6
6
  "types": "./bin/lib/index.d.ts",
@@ -1,4 +1,3 @@
1
- import type { JsonObject } from "#@typeberry/block-json";
2
1
  import { PvmBackend } from "#@typeberry/config";
3
2
  import { type FromJson } from "#@typeberry/json-parser";
4
3
  import { AuthorshipOptions } from "./authorship.js";
@@ -20,7 +19,23 @@ export declare enum KnownChainSpec {
20
19
  /** Full chain spec. */
21
20
  Full = "full"
22
21
  }
22
+ /** Persistent regular-node database backend. */
23
+ export declare enum RegularStateBackend {
24
+ /** @deprecated lmdb remains available as an explicit fallback, but fjall is the default backend. */
25
+ Lmdb = "lmdb",
26
+ Fjall = "fjall"
27
+ }
23
28
  export declare const knownChainSpecFromJson: FromJson<KnownChainSpec>;
29
+ export declare const regularStateBackendFromJson: FromJson<RegularStateBackend>;
30
+ type NodeConfigurationJson = {
31
+ $schema: string;
32
+ version: number;
33
+ flavor: KnownChainSpec;
34
+ chain_spec: JipChainSpec;
35
+ database_base_path?: string;
36
+ state_backend?: RegularStateBackend;
37
+ authorship: AuthorshipOptions;
38
+ };
24
39
  export declare class NodeConfiguration {
25
40
  readonly $schema: string;
26
41
  readonly version: number;
@@ -28,10 +43,13 @@ export declare class NodeConfiguration {
28
43
  readonly chainSpec: JipChainSpec;
29
44
  /** If database path is not provided, we load an in-memory db. */
30
45
  readonly databaseBasePath: string | undefined;
46
+ /** Persistent database backend used when `databaseBasePath` is set. */
47
+ readonly stateBackend: RegularStateBackend;
31
48
  readonly authorship: AuthorshipOptions;
32
49
  static fromJson: import("@typeberry/json-parser").FromJsonWithParser<unknown, NodeConfiguration>;
33
- static new({ $schema, version, flavor, chain_spec, database_base_path, authorship }: JsonObject<NodeConfiguration>): NodeConfiguration;
50
+ static new({ $schema, version, flavor, chain_spec, database_base_path, state_backend, authorship, }: NodeConfigurationJson): NodeConfiguration;
34
51
  private constructor();
35
52
  }
36
53
  export declare function loadConfig(config: string[], withRelPath: (p: string) => string): NodeConfiguration;
54
+ export {};
37
55
  //# sourceMappingURL=node-config.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"node-config.d.ts","sourceRoot":"","sources":["../../../../../packages/jam/config-node/node-config.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAE/C,OAAO,EAAE,KAAK,QAAQ,EAAuB,MAAM,wBAAwB,CAAC;AAG5E,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAInD,+DAA+D;AAC/D,eAAO,MAAM,eAAe,QAAQ,CAAC;AACrC,eAAO,MAAM,eAAe,aAAa,CAAC;AAE1C,2BAA2B;AAC3B,eAAO,MAAM,cAAc,YAAY,CAAC;AAExC,eAAO,MAAM,aAAa;;;;CAIzB,CAAC;AAEF,0BAA0B;AAC1B,oBAAY,cAAc;IACxB,uBAAuB;IACvB,IAAI,SAAS;IACb,uBAAuB;IACvB,IAAI,SAAS;CACd;AAED,eAAO,MAAM,sBAAsB,EAS7B,QAAQ,CAAC,cAAc,CAAC,CAAC;AAE/B,qBAAa,iBAAiB;aAqBV,OAAO,EAAE,MAAM;aACf,OAAO,EAAE,MAAM;aACf,MAAM,EAAE,cAAc;aACtB,SAAS,EAAE,YAAY;IACvC,iEAAiE;aACjD,gBAAgB,EAAE,MAAM,GAAG,SAAS;aACpC,UAAU,EAAE,iBAAiB;IA1B/C,MAAM,CAAC,QAAQ,kFAUb;IAEF,MAAM,CAAC,GAAG,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,kBAAkB,EAAE,UAAU,EAAE,EAAE,UAAU,CAAC,iBAAiB,CAAC;IAOlH,OAAO;CASR;AAED,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,WAAW,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,GAAG,iBAAiB,CAuDlG"}
1
+ {"version":3,"file":"node-config.d.ts","sourceRoot":"","sources":["../../../../../packages/jam/config-node/node-config.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAE/C,OAAO,EAAE,KAAK,QAAQ,EAAuB,MAAM,wBAAwB,CAAC;AAG5E,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAInD,+DAA+D;AAC/D,eAAO,MAAM,eAAe,QAAQ,CAAC;AACrC,eAAO,MAAM,eAAe,aAAa,CAAC;AAE1C,2BAA2B;AAC3B,eAAO,MAAM,cAAc,YAAY,CAAC;AAExC,eAAO,MAAM,aAAa;;;;CAIzB,CAAC;AAEF,0BAA0B;AAC1B,oBAAY,cAAc;IACxB,uBAAuB;IACvB,IAAI,SAAS;IACb,uBAAuB;IACvB,IAAI,SAAS;CACd;AAED,gDAAgD;AAChD,oBAAY,mBAAmB;IAC7B,oGAAoG;IACpG,IAAI,SAAS;IACb,KAAK,UAAU;CAChB;AAED,eAAO,MAAM,sBAAsB,EAS7B,QAAQ,CAAC,cAAc,CAAC,CAAC;AAE/B,eAAO,MAAM,2BAA2B,EASlC,QAAQ,CAAC,mBAAmB,CAAC,CAAC;AAEpC,KAAK,qBAAqB,GAAG;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,cAAc,CAAC;IACvB,UAAU,EAAE,YAAY,CAAC;IACzB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,aAAa,CAAC,EAAE,mBAAmB,CAAC;IACpC,UAAU,EAAE,iBAAiB,CAAC;CAC/B,CAAC;AAEF,qBAAa,iBAAiB;aAsCV,OAAO,EAAE,MAAM;aACf,OAAO,EAAE,MAAM;aACf,MAAM,EAAE,cAAc;aACtB,SAAS,EAAE,YAAY;IACvC,iEAAiE;aACjD,gBAAgB,EAAE,MAAM,GAAG,SAAS;IACpD,uEAAuE;aACvD,YAAY,EAAE,mBAAmB;aACjC,UAAU,EAAE,iBAAiB;IA7C/C,MAAM,CAAC,QAAQ,kFAWb;IAEF,MAAM,CAAC,GAAG,CAAC,EACT,OAAO,EACP,OAAO,EACP,MAAM,EACN,UAAU,EACV,kBAAkB,EAClB,aAAa,EACb,UAAU,GACX,EAAE,qBAAqB;IAexB,OAAO;CAWR;AAED,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,WAAW,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,GAAG,iBAAiB,CAuDlG"}
@@ -26,6 +26,13 @@ export var KnownChainSpec;
26
26
  /** Full chain spec. */
27
27
  KnownChainSpec["Full"] = "full";
28
28
  })(KnownChainSpec || (KnownChainSpec = {}));
29
+ /** Persistent regular-node database backend. */
30
+ export var RegularStateBackend;
31
+ (function (RegularStateBackend) {
32
+ /** @deprecated lmdb remains available as an explicit fallback, but fjall is the default backend. */
33
+ RegularStateBackend["Lmdb"] = "lmdb";
34
+ RegularStateBackend["Fjall"] = "fjall";
35
+ })(RegularStateBackend || (RegularStateBackend = {}));
29
36
  export const knownChainSpecFromJson = json.fromString((input, ctx) => {
30
37
  switch (input) {
31
38
  case KnownChainSpec.Tiny:
@@ -36,12 +43,23 @@ export const knownChainSpecFromJson = json.fromString((input, ctx) => {
36
43
  throw Error(`unknown network flavor: ${input} at ${ctx}`);
37
44
  }
38
45
  });
46
+ export const regularStateBackendFromJson = json.fromString((input, ctx) => {
47
+ switch (input) {
48
+ case RegularStateBackend.Lmdb:
49
+ return RegularStateBackend.Lmdb;
50
+ case RegularStateBackend.Fjall:
51
+ return RegularStateBackend.Fjall;
52
+ default:
53
+ throw Error(`unknown state backend: ${input} at ${ctx}`);
54
+ }
55
+ });
39
56
  export class NodeConfiguration {
40
57
  $schema;
41
58
  version;
42
59
  flavor;
43
60
  chainSpec;
44
61
  databaseBasePath;
62
+ stateBackend;
45
63
  authorship;
46
64
  static fromJson = json.object({
47
65
  $schema: "string",
@@ -49,22 +67,26 @@ export class NodeConfiguration {
49
67
  flavor: knownChainSpecFromJson,
50
68
  chain_spec: JipChainSpec.fromJson,
51
69
  database_base_path: json.optional("string"),
70
+ state_backend: json.optional(regularStateBackendFromJson),
52
71
  authorship: AuthorshipOptions.fromJson,
53
72
  }, NodeConfiguration.new);
54
- static new({ $schema, version, flavor, chain_spec, database_base_path, authorship }) {
73
+ static new({ $schema, version, flavor, chain_spec, database_base_path, state_backend, authorship, }) {
55
74
  if (version !== 1) {
56
75
  throw new Error("Only version=1 config is supported.");
57
76
  }
58
- return new NodeConfiguration($schema, version, flavor, chain_spec, database_base_path ?? undefined, authorship);
77
+ return new NodeConfiguration($schema, version, flavor, chain_spec, database_base_path ?? undefined, state_backend ?? RegularStateBackend.Fjall, authorship);
59
78
  }
60
79
  constructor($schema, version, flavor, chainSpec,
61
80
  /** If database path is not provided, we load an in-memory db. */
62
- databaseBasePath, authorship) {
81
+ databaseBasePath,
82
+ /** Persistent database backend used when `databaseBasePath` is set. */
83
+ stateBackend, authorship) {
63
84
  this.$schema = $schema;
64
85
  this.version = version;
65
86
  this.flavor = flavor;
66
87
  this.chainSpec = chainSpec;
67
88
  this.databaseBasePath = databaseBasePath;
89
+ this.stateBackend = stateBackend;
68
90
  this.authorship = authorship;
69
91
  }
70
92
  }
@@ -5,7 +5,7 @@ import { configs } from "#@typeberry/configs";
5
5
  import polkajamConfig from "#@typeberry/configs/typeberry-polkajam-dev.json" with { type: "json" };
6
6
  import { parseFromJson } from "#@typeberry/json-parser";
7
7
  import { workspacePathFix } from "#@typeberry/utils";
8
- import { KnownChainSpec, loadConfig, NodeConfiguration } from "./node-config.js";
8
+ import { KnownChainSpec, loadConfig, NodeConfiguration, RegularStateBackend } from "./node-config.js";
9
9
  const withRelPath = workspacePathFix(`${import.meta.dirname}/../..`);
10
10
  const NODE_CONFIG_TEST = {
11
11
  $schema: "https://typeberry.dev/schemas/config-v1.schema.json",
@@ -40,6 +40,13 @@ describe("Importing Node Configuration", () => {
40
40
  it("should read the database base path", () => {
41
41
  assert.deepStrictEqual(config.databaseBasePath, "/tmp/jam-node-db");
42
42
  });
43
+ it("defaults to the fjall state backend", () => {
44
+ assert.deepStrictEqual(config.stateBackend, RegularStateBackend.Fjall);
45
+ });
46
+ it("should read the state backend", () => {
47
+ const lmdbConfig = parseFromJson({ ...NODE_CONFIG_TEST, state_backend: "lmdb" }, NodeConfiguration.fromJson);
48
+ assert.deepStrictEqual(lmdbConfig.stateBackend, RegularStateBackend.Lmdb);
49
+ });
43
50
  it("should read the chain spec", () => {
44
51
  assert.deepStrictEqual(config.chainSpec.id, "testnet");
45
52
  assert(config.chainSpec.bootnodes !== undefined);
@@ -15,6 +15,8 @@ export declare class InMemorySerializedStates implements StatesDb<SerializedStat
15
15
  private readonly blake2b;
16
16
  private readonly db;
17
17
  private readonly valuesDb;
18
+ private readonly refsStore;
19
+ private readonly refs;
18
20
  static new({ chainSpec }: {
19
21
  chainSpec: ChainSpec;
20
22
  }): Promise<InMemorySerializedStates>;
@@ -27,8 +29,9 @@ export declare class InMemorySerializedStates implements StatesDb<SerializedStat
27
29
  getStateRoot(state: SerializedState<LeafDb>): Promise<StateRootHash>;
28
30
  updateAndSetState(header: HeaderHash, state: SerializedState<LeafDb>, update: Partial<State & ServicesUpdate>): Promise<Result<OK, StateUpdateError>>;
29
31
  getState(header: HeaderHash): SerializedState<LeafDb> | null;
30
- commitFinalized(_headers: HeaderHash[]): void;
32
+ commitFinalized(headers: HeaderHash[]): void;
31
33
  markUnused(header: HeaderHash): void;
34
+ private applyRefs;
32
35
  close(): Promise<void>;
33
36
  }
34
37
  //# 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;IAkB5D,eAAe,CAAC,QAAQ,EAAE,UAAU,EAAE,GAAG,IAAI;IAK7C,UAAU,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI;IAI9B,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;AAG5E,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;IAgB1G,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAhB1B,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAyE;IAC5F,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA8D;IACvF,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAgC;IAC1D,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAiC;WAEzC,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;IAkBxG,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;IAwBxC,QAAQ,CAAC,MAAM,EAAE,UAAU,GAAG,eAAe,CAAC,MAAM,CAAC,GAAG,IAAI;IAkB5D,eAAe,CAAC,OAAO,EAAE,UAAU,EAAE,GAAG,IAAI;IAI5C,UAAU,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI;IAKpC,OAAO,CAAC,SAAS;IAOX,KAAK;CACZ"}
@@ -5,12 +5,15 @@ import { leafComparator } from "#@typeberry/trie";
5
5
  import { OK, Result } from "#@typeberry/utils";
6
6
  import { LeafDb } from "./leaf-db.js";
7
7
  import { updateLeafs } from "./leaf-db-update.js";
8
+ import { InMemoryValueRefsStore, ValueRefs } from "./value-refs.js";
8
9
  /** In-memory serialized-states db. */
9
10
  export class InMemorySerializedStates {
10
11
  spec;
11
12
  blake2b;
12
13
  db = HashDictionary.new();
13
14
  valuesDb = HashDictionary.new();
15
+ refsStore = new InMemoryValueRefsStore();
16
+ refs = new ValueRefs(this.refsStore);
14
17
  static async new({ chainSpec }) {
15
18
  const blake2b = await Blake2b.createHasher();
16
19
  return new InMemorySerializedStates(chainSpec, blake2b);
@@ -30,6 +33,7 @@ export class InMemorySerializedStates {
30
33
  this.valuesDb.set(val[0], val[1]);
31
34
  }
32
35
  this.db.set(headerHash, leafs);
36
+ this.applyRefs(this.refs.onInitial(values.map((v) => v[0])));
33
37
  return Result.ok(OK);
34
38
  }
35
39
  async getStateRoot(state) {
@@ -40,17 +44,19 @@ export class InMemorySerializedStates {
40
44
  const updatedValues = serializeStateUpdate(this.spec, blake2b, update);
41
45
  // make sure to clone the leafs before writing, since the collection is re-used.
42
46
  const newLeafs = SortedSet.fromSortedArray(leafComparator, state.backend.leafs.array);
43
- const { values, leafs } = updateLeafs(newLeafs, blake2b, updatedValues);
47
+ const { values, removed, leafs } = updateLeafs(newLeafs, blake2b, updatedValues);
44
48
  // make sure to reset the cache and re-create leafsdb lookup
45
49
  state.updateBackend(LeafDb.fromLeaves(leafs, state.backend.db));
46
50
  // insert values to the db
47
51
  // valuesdb can be shared between all states because it's just
48
- // <valuehash> -> <value> mapping and existence is managed by trie leafs.
52
+ // <valuehash> -> <value> mapping and existence is managed by value
53
+ // refcounting, driven by the per-block deltas recorded below.
49
54
  for (const val of values) {
50
55
  this.valuesDb.set(val[0], val[1]);
51
56
  }
52
57
  // store new set of leaves
53
58
  this.db.set(header, leafs);
59
+ this.applyRefs(this.refs.onImport(header, { inserted: values.map((v) => v[0]), removed }));
54
60
  return Result.ok(OK);
55
61
  }
56
62
  getState(header) {
@@ -70,12 +76,18 @@ export class InMemorySerializedStates {
70
76
  });
71
77
  return SerializedState.new(this.spec, this.blake2b, leafDb);
72
78
  }
73
- commitFinalized(_headers) {
74
- // No value pruning here: this in-memory db keeps every value in a plain map
75
- // and is not the long-running fuzz target the refcounting is meant for.
79
+ commitFinalized(headers) {
80
+ this.applyRefs(this.refs.commitFinalized(headers));
76
81
  }
77
82
  markUnused(header) {
83
+ this.applyRefs(this.refs.releaseUnfinalized(header));
78
84
  this.db.delete(header);
79
85
  }
86
+ applyRefs(update) {
87
+ this.refsStore.apply(update);
88
+ for (const v of update.removeValues) {
89
+ this.valuesDb.delete(v);
90
+ }
91
+ }
80
92
  async close() { }
81
93
  }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=serialized-states-db.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serialized-states-db.test.d.ts","sourceRoot":"","sources":["../../../../../packages/jam/database/serialized-states-db.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,109 @@
1
+ import assert from "node:assert";
2
+ import { before, describe, it } from "node:test";
3
+ import { tryAsServiceId } from "#@typeberry/block";
4
+ import { Bytes, BytesBlob } from "#@typeberry/bytes";
5
+ import { tinyChainSpec } from "#@typeberry/config";
6
+ import { Blake2b, HASH_SIZE } from "#@typeberry/hash";
7
+ import { StorageItem, UpdateStorage } from "#@typeberry/state";
8
+ import { testState } from "#@typeberry/state/test.utils.js";
9
+ import { StateEntries } from "#@typeberry/state-merkleization";
10
+ import { asOpaqueType, deepEqual, OK, Result } from "#@typeberry/utils";
11
+ import { InMemorySerializedStates } from "./serialized-states-db.js";
12
+ let blake2b;
13
+ before(async () => {
14
+ blake2b = await Blake2b.createHasher();
15
+ });
16
+ const spec = tinyChainSpec;
17
+ function hh(n) {
18
+ return Bytes.fill(HASH_SIZE, n).asOpaque();
19
+ }
20
+ const storageKey = asOpaqueType(BytesBlob.blobFromString("test-key"));
21
+ /** A state update writing a single large (non-embedded) value under `storageKey`. */
22
+ function storageUpdate(value) {
23
+ const item = StorageItem.create({ key: storageKey, value: BytesBlob.blobFromString(value) });
24
+ return {
25
+ storage: new Map([[tryAsServiceId(0), [UpdateStorage.set({ storage: item })]]]),
26
+ };
27
+ }
28
+ // > 32 bytes => stored in the values db (not embedded in the leaf).
29
+ const BIG_1 = "a".repeat(100);
30
+ const BIG_2 = "b".repeat(100);
31
+ async function newStates() {
32
+ const states = InMemorySerializedStates.withHasher({ chainSpec: spec, blake2b });
33
+ const res = await states.insertInitialState(hh(0), StateEntries.serializeInMemory(spec, blake2b, testState()));
34
+ deepEqual(res, Result.ok(OK));
35
+ return states;
36
+ }
37
+ /** `true` if every value referenced by the state can still be resolved. */
38
+ function canReadFully(state) {
39
+ if (state === null) {
40
+ return false;
41
+ }
42
+ try {
43
+ state.backend.intoStateEntries();
44
+ return true;
45
+ }
46
+ catch {
47
+ return false;
48
+ }
49
+ }
50
+ describe("InMemorySerializedStates value refcounting", () => {
51
+ it("keeps a replaced value until the block replacing it finalizes", async () => {
52
+ const states = await newStates();
53
+ const s0 = states.getState(hh(0));
54
+ assert.ok(s0 !== null);
55
+ await states.updateAndSetState(hh(1), s0, storageUpdate(BIG_1));
56
+ // a handle to the post-1 state, surviving the pruning below
57
+ const stale1 = states.getState(hh(1));
58
+ const s1 = states.getState(hh(1));
59
+ assert.ok(s1 !== null);
60
+ await states.updateAndSetState(hh(2), s1, storageUpdate(BIG_2));
61
+ states.commitFinalized([hh(1)]);
62
+ assert.ok(canReadFully(stale1), "still referenced by the finalized tip");
63
+ states.commitFinalized([hh(2)]);
64
+ assert.ok(!canReadFully(stale1), "replaced value is dropped once the replacement finalizes");
65
+ assert.ok(canReadFully(states.getState(hh(2))), "the new finalized tip stays fully readable");
66
+ });
67
+ it("collects values of a pruned dead fork and keeps the surviving chain intact", async () => {
68
+ const states = await newStates();
69
+ const s0 = states.getState(hh(0));
70
+ assert.ok(s0 !== null);
71
+ await states.updateAndSetState(hh(1), s0, storageUpdate(BIG_1));
72
+ // a dead fork on top of genesis, inserting a different value
73
+ const fork = states.getState(hh(0));
74
+ assert.ok(fork !== null);
75
+ await states.updateAndSetState(hh(0xaa), fork, storageUpdate(BIG_2));
76
+ const staleFork = states.getState(hh(0xaa));
77
+ states.markUnused(hh(0xaa));
78
+ assert.strictEqual(states.getState(hh(0xaa)), null);
79
+ assert.ok(!canReadFully(staleFork), "fork-only value is collected");
80
+ assert.ok(canReadFully(states.getState(hh(1))), "surviving chain is unaffected");
81
+ });
82
+ it("does not collect a value shared with a pruned fork", async () => {
83
+ const states = await newStates();
84
+ const s0 = states.getState(hh(0));
85
+ assert.ok(s0 !== null);
86
+ await states.updateAndSetState(hh(1), s0, storageUpdate(BIG_1));
87
+ // the fork writes the very same value
88
+ const fork = states.getState(hh(0));
89
+ assert.ok(fork !== null);
90
+ await states.updateAndSetState(hh(0xaa), fork, storageUpdate(BIG_1));
91
+ states.markUnused(hh(0xaa));
92
+ assert.ok(canReadFully(states.getState(hh(1))), "value still referenced by the surviving chain");
93
+ });
94
+ it("follows the importer lifecycle: commit finalized, then prune", async () => {
95
+ const states = await newStates();
96
+ const s0 = states.getState(hh(0));
97
+ assert.ok(s0 !== null);
98
+ await states.updateAndSetState(hh(1), s0, storageUpdate(BIG_1));
99
+ const s1 = states.getState(hh(1));
100
+ assert.ok(s1 !== null);
101
+ await states.updateAndSetState(hh(2), s1, storageUpdate(BIG_2));
102
+ // finality round: 1 and 2 finalized, genesis and 1 pruned
103
+ states.commitFinalized([hh(1), hh(2)]);
104
+ states.markUnused(hh(0));
105
+ states.markUnused(hh(1));
106
+ assert.strictEqual(states.getState(hh(1)), null);
107
+ assert.ok(canReadFully(states.getState(hh(2))), "finalized tip fully readable after pruning");
108
+ });
109
+ });
@@ -0,0 +1,25 @@
1
+ import { type BlockView, type ExtrinsicView, type HeaderHash, type HeaderView, type StateRootHash } from "#@typeberry/block";
2
+ import type { ChainSpec } from "#@typeberry/config";
3
+ import type { BlocksDb } from "#@typeberry/database/blocks.js";
4
+ import { type WithHash } from "#@typeberry/hash";
5
+ import { type FjallRoot } from "./root.js";
6
+ /** fjall-backed block storage. */
7
+ export declare class FjallBlocks implements BlocksDb {
8
+ private readonly chainSpec;
9
+ private readonly root;
10
+ private readonly headers;
11
+ private readonly extrinsics;
12
+ private readonly postStateRoots;
13
+ static open(chainSpec: ChainSpec, root: FjallRoot): Promise<FjallBlocks>;
14
+ private constructor();
15
+ setPostStateRoot(hash: HeaderHash, postStateRoot: StateRootHash): Promise<void>;
16
+ getPostStateRoot(hash: HeaderHash): StateRootHash | null;
17
+ insertBlock(block: WithHash<HeaderHash, BlockView>): Promise<void>;
18
+ setBestHeaderHash(hash: HeaderHash): Promise<void>;
19
+ getBestHeaderHash(): HeaderHash;
20
+ getHeader(hash: HeaderHash): HeaderView | null;
21
+ getExtrinsic(hash: HeaderHash): ExtrinsicView | null;
22
+ markUnused(hash: HeaderHash): void;
23
+ close(): Promise<void>;
24
+ }
25
+ //# sourceMappingURL=blocks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"blocks.d.ts","sourceRoot":"","sources":["../../../../../packages/jam/database-fjall/blocks.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,SAAS,EAEd,KAAK,aAAa,EAElB,KAAK,UAAU,EACf,KAAK,UAAU,EACf,KAAK,aAAa,EACnB,MAAM,kBAAkB,CAAC;AAG1B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,+BAA+B,CAAC;AAC9D,OAAO,EAAa,KAAK,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAE3D,OAAO,EAAuB,KAAK,SAAS,EAAgC,MAAM,WAAW,CAAC;AAK9F,kCAAkC;AAClC,qBAAa,WAAY,YAAW,QAAQ;IAWxC,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B,OAAO,CAAC,QAAQ,CAAC,cAAc;WAdpB,IAAI,CAAC,SAAS,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,GAAG,OAAO,CAAC,WAAW,CAAC;IAS9E,OAAO;IAQD,gBAAgB,CAAC,IAAI,EAAE,UAAU,EAAE,aAAa,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;IAIrF,gBAAgB,CAAC,IAAI,EAAE,UAAU,GAAG,aAAa,GAAG,IAAI;IAQlD,WAAW,CAAC,KAAK,EAAE,QAAQ,CAAC,UAAU,EAAE,SAAS,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAOlE,iBAAiB,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAKxD,iBAAiB,IAAI,UAAU;IAQ/B,SAAS,CAAC,IAAI,EAAE,UAAU,GAAG,UAAU,GAAG,IAAI;IAQ9C,YAAY,CAAC,IAAI,EAAE,UAAU,GAAG,aAAa,GAAG,IAAI;IAQpD,UAAU,CAAC,IAAI,EAAE,UAAU,GAAG,IAAI;IAQ5B,KAAK;CACZ"}
@@ -0,0 +1,86 @@
1
+ import { Extrinsic, Header, } from "#@typeberry/block";
2
+ import { Bytes } from "#@typeberry/bytes";
3
+ import { Decoder } from "#@typeberry/codec";
4
+ import { HASH_SIZE } from "#@typeberry/hash";
5
+ import { Logger } from "#@typeberry/logger";
6
+ import { toUint8Array } from "./root.js";
7
+ const BEST_BLOCK = new TextEncoder().encode("best hash and posterior state root");
8
+ const logger = Logger.new(import.meta.filename, "db");
9
+ /** fjall-backed block storage. */
10
+ export class FjallBlocks {
11
+ chainSpec;
12
+ root;
13
+ headers;
14
+ extrinsics;
15
+ postStateRoots;
16
+ static async open(chainSpec, root) {
17
+ const [headers, extrinsics, postStateRoots] = await Promise.all([
18
+ root.partition("headers"),
19
+ root.partition("extrinsics"),
20
+ root.partition("postStateRoots"),
21
+ ]);
22
+ return new FjallBlocks(chainSpec, root, headers, extrinsics, postStateRoots);
23
+ }
24
+ constructor(chainSpec, root, headers, extrinsics, postStateRoots) {
25
+ this.chainSpec = chainSpec;
26
+ this.root = root;
27
+ this.headers = headers;
28
+ this.extrinsics = extrinsics;
29
+ this.postStateRoots = postStateRoots;
30
+ }
31
+ async setPostStateRoot(hash, postStateRoot) {
32
+ await writable(this.postStateRoots, this.root).insert(hash.raw, postStateRoot.raw);
33
+ }
34
+ getPostStateRoot(hash) {
35
+ const postStateRoot = toUint8Array(this.postStateRoots.get(hash.raw));
36
+ if (postStateRoot === null) {
37
+ return null;
38
+ }
39
+ return Bytes.fromBlob(postStateRoot, HASH_SIZE).asOpaque();
40
+ }
41
+ async insertBlock(block) {
42
+ await Promise.all([
43
+ writable(this.headers, this.root).insert(block.hash.raw, block.data.header.view().encoded().raw),
44
+ writable(this.extrinsics, this.root).insert(block.hash.raw, block.data.extrinsic.view().encoded().raw),
45
+ ]);
46
+ }
47
+ async setBestHeaderHash(hash) {
48
+ await writable(this.headers, this.root).insert(BEST_BLOCK, hash.raw);
49
+ await this.root.persist();
50
+ }
51
+ getBestHeaderHash() {
52
+ const bestHeaderHash = toUint8Array(this.headers.get(BEST_BLOCK));
53
+ if (bestHeaderHash === null) {
54
+ return Bytes.zero(HASH_SIZE).asOpaque();
55
+ }
56
+ return Bytes.fromBlob(bestHeaderHash, HASH_SIZE).asOpaque();
57
+ }
58
+ getHeader(hash) {
59
+ const data = toUint8Array(this.headers.get(hash.raw));
60
+ if (data === null) {
61
+ return null;
62
+ }
63
+ return Decoder.decodeObject(Header.Codec.View, data, this.chainSpec);
64
+ }
65
+ getExtrinsic(hash) {
66
+ const data = toUint8Array(this.extrinsics.get(hash.raw));
67
+ if (data === null) {
68
+ return null;
69
+ }
70
+ return Decoder.decodeObject(Extrinsic.Codec.View, data, this.chainSpec);
71
+ }
72
+ markUnused(hash) {
73
+ void Promise.all([
74
+ writable(this.headers, this.root).remove(hash.raw),
75
+ writable(this.extrinsics, this.root).remove(hash.raw),
76
+ writable(this.postStateRoots, this.root).remove(hash.raw),
77
+ ]).catch((e) => logger.warn `Failed to prune block ${hash}: ${e}`);
78
+ }
79
+ async close() { }
80
+ }
81
+ function writable(partition, root) {
82
+ if (root.readOnly) {
83
+ throw new Error("Cannot write through a read-only fjall partition.");
84
+ }
85
+ return partition;
86
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=blocks.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"blocks.test.d.ts","sourceRoot":"","sources":["../../../../../packages/jam/database-fjall/blocks.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,62 @@
1
+ import assert from "node:assert";
2
+ import * as fs from "node:fs";
3
+ import { afterEach, before, beforeEach, describe, it } from "node:test";
4
+ import { testBlockView } from "#@typeberry/block/test-helpers.js";
5
+ import { Bytes } from "#@typeberry/bytes";
6
+ import { tinyChainSpec } from "#@typeberry/config";
7
+ import { Blake2b, HASH_SIZE, WithHash } from "#@typeberry/hash";
8
+ import { FjallBlocks } from "./blocks.js";
9
+ import { FjallRoot } from "./root.js";
10
+ let blake2b;
11
+ before(async () => {
12
+ blake2b = await Blake2b.createHasher();
13
+ });
14
+ describe("Fjall blocks database", () => {
15
+ let tmpDir = "";
16
+ beforeEach(() => {
17
+ tmpDir = fs.mkdtempSync("typeberry-fjall-blocks-");
18
+ });
19
+ afterEach(() => {
20
+ fs.rmSync(tmpDir, { recursive: true, force: true });
21
+ });
22
+ it("sets and retrieves the best header hash", async () => {
23
+ const root = await FjallRoot.open(tmpDir, { ephemeral: true });
24
+ const blocks = await FjallBlocks.open(tinyChainSpec, root);
25
+ try {
26
+ await blocks.setBestHeaderHash(Bytes.fill(HASH_SIZE, 5).asOpaque());
27
+ assert.strictEqual(blocks.getBestHeaderHash().toString(), "0x0505050505050505050505050505050505050505050505050505050505050505");
28
+ }
29
+ finally {
30
+ await blocks.close();
31
+ await root.close();
32
+ }
33
+ });
34
+ it("sets and retrieves post state roots", async () => {
35
+ const root = await FjallRoot.open(tmpDir, { ephemeral: true });
36
+ const blocks = await FjallBlocks.open(tinyChainSpec, root);
37
+ try {
38
+ await blocks.setPostStateRoot(Bytes.fill(HASH_SIZE, 5).asOpaque(), Bytes.fill(HASH_SIZE, 10).asOpaque());
39
+ assert.strictEqual(blocks.getPostStateRoot(Bytes.fill(HASH_SIZE, 1).asOpaque())?.toString(), undefined);
40
+ assert.strictEqual(blocks.getPostStateRoot(Bytes.fill(HASH_SIZE, 5).asOpaque())?.toString(), "0x0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a");
41
+ }
42
+ finally {
43
+ await blocks.close();
44
+ await root.close();
45
+ }
46
+ });
47
+ it("stores and retrieves a block", async () => {
48
+ const root = await FjallRoot.open(tmpDir, { ephemeral: true });
49
+ const blocks = await FjallBlocks.open(tinyChainSpec, root);
50
+ try {
51
+ const block = testBlockView();
52
+ const headerHash = blake2b.hashBytes(block.header.view().encoded()).asOpaque();
53
+ await blocks.insertBlock(WithHash.new(headerHash, block));
54
+ assert.deepStrictEqual(blocks.getHeader(headerHash)?.materialize(), block.header.materialize());
55
+ assert.deepStrictEqual(blocks.getExtrinsic(headerHash)?.materialize(), block.extrinsic.materialize());
56
+ }
57
+ finally {
58
+ await blocks.close();
59
+ await root.close();
60
+ }
61
+ });
62
+ });
@@ -1 +1 @@
1
- {"version":3,"file":"hybrid-states.d.ts","sourceRoot":"","sources":["../../../../../packages/jam/database-fjall/hybrid-states.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"hybrid-states.d.ts","sourceRoot":"","sources":["../../../../../packages/jam/database-fjall/hybrid-states.ts"],"names":[],"mappings":"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"}
@@ -27,9 +27,18 @@ export class FjallValuesSession {
27
27
  }
28
28
  /** Open (or create) the keyspace at `dbPath` and its `values` partition. */
29
29
  static async open(dbPath, options = {}) {
30
+ if (options.readOnly === true) {
31
+ throw new Error("FjallValuesSession requires a writable keyspace.");
32
+ }
30
33
  const root = await FjallRoot.open(dbPath, options);
31
- const values = await root.partition("values");
32
- return new FjallValuesSession(root, values);
34
+ try {
35
+ const values = await root.writablePartition("values");
36
+ return new FjallValuesSession(root, values);
37
+ }
38
+ catch (e) {
39
+ await root.close();
40
+ throw e;
41
+ }
33
42
  }
34
43
  /** Flush the journal to disk (a no-op for ephemeral keyspaces). */
35
44
  async persist() {