@typeberry/lib 0.7.3-ffe481c → 0.7.4-2289aab

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@typeberry/lib",
3
- "version": "0.7.3-ffe481c",
3
+ "version": "0.7.4-2289aab",
4
4
  "description": "Typeberry Library",
5
5
  "main": "./bin/lib/index.js",
6
6
  "types": "./bin/lib/index.d.ts",
@@ -0,0 +1,42 @@
1
+ import type { HeaderHash, StateRootHash } from "#@typeberry/block";
2
+ import type { ChainSpec } from "#@typeberry/config";
3
+ import { type InitStatesDb, LeafDb, type StatesDb, StateUpdateError } from "#@typeberry/database";
4
+ import type { Blake2b } from "#@typeberry/hash";
5
+ import type { ServicesUpdate, State } from "#@typeberry/state";
6
+ import { SerializedState, type StateEntries } from "#@typeberry/state-merkleization";
7
+ import { OK, Result } from "#@typeberry/utils";
8
+ /**
9
+ * Hybrid serialized-states db.
10
+ *
11
+ * States (leafs) are kept in-memory, but large values are persisted to lmdb.
12
+ * Reads go straight to lmdb, which keeps its own page cache.
13
+ * NOTE: this DB is designed for long fuzzing and to be used with pruning to
14
+ * keep the heap usage bounded.
15
+ */
16
+ export declare class HybridSerializedStates implements StatesDb<SerializedState<LeafDb>>, InitStatesDb<StateEntries> {
17
+ private readonly spec;
18
+ private readonly blake2b;
19
+ private readonly root;
20
+ private readonly inMemStates;
21
+ private readonly lmdbValues;
22
+ private readonly valuesDb;
23
+ static new({ spec, blake2b, dbPath, readOnly, ephemeral, }: {
24
+ spec: ChainSpec;
25
+ blake2b: Blake2b;
26
+ dbPath: string;
27
+ readOnly?: boolean;
28
+ ephemeral?: boolean;
29
+ }): HybridSerializedStates;
30
+ private constructor();
31
+ insertInitialState(headerHash: HeaderHash, entries: StateEntries): Promise<Result<OK, StateUpdateError>>;
32
+ updateAndSetState(header: HeaderHash, state: SerializedState<LeafDb>, update: Partial<State & ServicesUpdate>): Promise<Result<OK, StateUpdateError>>;
33
+ getStateRoot(state: SerializedState<LeafDb>): Promise<StateRootHash>;
34
+ getState(header: HeaderHash): SerializedState<LeafDb> | null;
35
+ markUnused(header: HeaderHash): void;
36
+ close(): Promise<void>;
37
+ /** Write new large values to LMDB in one transaction. */
38
+ private writeValues;
39
+ /** Read a value from LMDB. */
40
+ private readValue;
41
+ }
42
+ //# sourceMappingURL=hybrid-states.d.ts.map
@@ -0,0 +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"}
@@ -0,0 +1,105 @@
1
+ import { HashDictionary, SortedSet } from "#@typeberry/collections";
2
+ import { LeafDb, StateUpdateError, updateLeafs, } from "#@typeberry/database";
3
+ import { SerializedState, StateEntryUpdateAction, serializeStateUpdate, } from "#@typeberry/state-merkleization";
4
+ import { leafComparator } from "#@typeberry/trie";
5
+ import { OK, Result } from "#@typeberry/utils";
6
+ import { LmdbRoot } from "./root.js";
7
+ /**
8
+ * Hybrid serialized-states db.
9
+ *
10
+ * States (leafs) are kept in-memory, but large values are persisted to lmdb.
11
+ * Reads go straight to lmdb, which keeps its own page cache.
12
+ * NOTE: this DB is designed for long fuzzing and to be used with pruning to
13
+ * keep the heap usage bounded.
14
+ */
15
+ export class HybridSerializedStates {
16
+ spec;
17
+ blake2b;
18
+ root;
19
+ inMemStates = HashDictionary.new();
20
+ lmdbValues;
21
+ // A single shared values accessor reused by every `LeafDb` we hand out.
22
+ valuesDb;
23
+ static new({ spec, blake2b, dbPath, readOnly, ephemeral, }) {
24
+ const root = LmdbRoot.new(dbPath, readOnly, ephemeral);
25
+ return new HybridSerializedStates(spec, blake2b, root);
26
+ }
27
+ constructor(spec, blake2b, root) {
28
+ this.spec = spec;
29
+ this.blake2b = blake2b;
30
+ this.root = root;
31
+ this.lmdbValues = this.root.subDb("values");
32
+ this.valuesDb = { get: (key) => this.readValue(key) };
33
+ }
34
+ async insertInitialState(headerHash, entries) {
35
+ const { values, leafs } = updateLeafs(SortedSet.fromArray(leafComparator, []), this.blake2b, Array.from(entries, (x) => [StateEntryUpdateAction.Insert, x[0], x[1]]));
36
+ const res = await this.writeValues(values);
37
+ if (res.isError) {
38
+ return res;
39
+ }
40
+ this.inMemStates.set(headerHash, leafs);
41
+ return Result.ok(OK);
42
+ }
43
+ async updateAndSetState(header, state, update) {
44
+ const updatedValues = serializeStateUpdate(this.spec, this.blake2b, update);
45
+ // Clone the leaf set before mutating: the previous state keeps using its own.
46
+ const newLeafs = SortedSet.fromSortedArray(leafComparator, state.backend.leafs.array);
47
+ const { values, leafs } = updateLeafs(newLeafs, this.blake2b, updatedValues);
48
+ const res = await this.writeValues(values);
49
+ if (res.isError) {
50
+ // Leave the caller's state untouched: its new leaves would reference
51
+ // values that never reached disk.
52
+ return res;
53
+ }
54
+ // Re-create the lookup with the shared values accessor only once the new
55
+ // values are durably written.
56
+ state.updateBackend(LeafDb.fromLeaves(leafs, this.valuesDb));
57
+ this.inMemStates.set(header, leafs);
58
+ return Result.ok(OK);
59
+ }
60
+ async getStateRoot(state) {
61
+ return state.backend.getStateRoot(this.blake2b);
62
+ }
63
+ getState(header) {
64
+ const leafs = this.inMemStates.get(header);
65
+ if (leafs === undefined) {
66
+ return null;
67
+ }
68
+ const leafDb = LeafDb.fromLeaves(leafs, this.valuesDb);
69
+ return SerializedState.new(this.spec, this.blake2b, leafDb);
70
+ }
71
+ markUnused(header) {
72
+ // We only remove the state from memory - values are not pruned at all,
73
+ // but since they are stored on disk we should be safe.
74
+ this.inMemStates.delete(header);
75
+ }
76
+ async close() {
77
+ await this.lmdbValues.close();
78
+ await this.root.close();
79
+ }
80
+ /** Write new large values to LMDB in one transaction. */
81
+ async writeValues(values) {
82
+ if (values.length === 0) {
83
+ return Result.ok(OK);
84
+ }
85
+ try {
86
+ await this.lmdbValues.transaction(() => {
87
+ for (const [hash, val] of values) {
88
+ this.lmdbValues.put(hash.raw, val.raw);
89
+ }
90
+ });
91
+ }
92
+ catch (e) {
93
+ return Result.error(StateUpdateError.Commit, () => `Failed to commit values: ${e}`);
94
+ }
95
+ return Result.ok(OK);
96
+ }
97
+ /** Read a value from LMDB. */
98
+ readValue(key) {
99
+ const val = this.lmdbValues.get(key.raw);
100
+ if (val === undefined) {
101
+ throw new Error(`Missing value at key: ${key}`);
102
+ }
103
+ return val;
104
+ }
105
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=hybrid-states.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hybrid-states.test.d.ts","sourceRoot":"","sources":["../../../../../packages/jam/database-lmdb/hybrid-states.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,88 @@
1
+ // packages/jam/database-lmdb/hybrid-states.test.ts
2
+ import assert from "node:assert";
3
+ import * as fs from "node:fs";
4
+ import { afterEach, before, beforeEach, describe, it } from "node:test";
5
+ import { Bytes, BytesBlob } from "#@typeberry/bytes";
6
+ import { tinyChainSpec } from "#@typeberry/config";
7
+ import { Blake2b, HASH_SIZE } from "#@typeberry/hash";
8
+ import { InMemoryState } from "#@typeberry/state";
9
+ import { StateEntries } from "#@typeberry/state-merkleization";
10
+ import { deepEqual, OK, Result } from "#@typeberry/utils";
11
+ import { HybridSerializedStates } from "./hybrid-states.js";
12
+ let blake2b;
13
+ before(async () => {
14
+ blake2b = await Blake2b.createHasher();
15
+ });
16
+ function createTempDir(suffix = "hybrid") {
17
+ return fs.mkdtempSync(`typeberry-${suffix}`);
18
+ }
19
+ describe("Hybrid serialized states", () => {
20
+ const spec = tinyChainSpec;
21
+ const headerHash = Bytes.zero(HASH_SIZE).asOpaque();
22
+ let dbPath = "";
23
+ beforeEach(() => {
24
+ dbPath = createTempDir();
25
+ });
26
+ afterEach(() => {
27
+ fs.rmSync(dbPath, { recursive: true });
28
+ });
29
+ it("round-trips an initial state through the on-disk values store", async () => {
30
+ const states = HybridSerializedStates.new({
31
+ spec,
32
+ blake2b,
33
+ dbPath,
34
+ });
35
+ try {
36
+ const empty = InMemoryState.empty(spec);
37
+ const serialized = StateEntries.serializeInMemory(spec, blake2b, empty);
38
+ const expectedRoot = serialized.getRootHash(blake2b);
39
+ const res = await states.insertInitialState(headerHash, serialized);
40
+ deepEqual(res, Result.ok(OK));
41
+ const state = states.getState(headerHash);
42
+ assert.ok(state !== null);
43
+ const stateRoot = await states.getStateRoot(state);
44
+ assert.strictEqual(`${stateRoot}`, `${expectedRoot}`);
45
+ deepEqual(InMemoryState.copyFrom(spec, state, new Map()), empty);
46
+ }
47
+ finally {
48
+ await states.close();
49
+ }
50
+ });
51
+ it("reads large values back from disk", async () => {
52
+ const states = HybridSerializedStates.new({ spec, blake2b, dbPath });
53
+ try {
54
+ // > 32 bytes => stored in the values db (not embedded in the leaf).
55
+ const big1 = BytesBlob.blobFromString("x".repeat(100));
56
+ const big2 = BytesBlob.blobFromString("y".repeat(100));
57
+ const key1 = Bytes.fill(HASH_SIZE, 1).asOpaque();
58
+ const key2 = Bytes.fill(HASH_SIZE, 2).asOpaque();
59
+ const entries = StateEntries.fromEntriesUnsafe([
60
+ [key1, big1],
61
+ [key2, big2],
62
+ ]);
63
+ const res = await states.insertInitialState(headerHash, entries);
64
+ deepEqual(res, Result.ok(OK));
65
+ const state = states.getState(headerHash);
66
+ assert.ok(state !== null);
67
+ assert.strictEqual(`${state.backend.get(key2)}`, `${big2}`);
68
+ assert.strictEqual(`${state.backend.get(key1)}`, `${big1}`);
69
+ }
70
+ finally {
71
+ await states.close();
72
+ }
73
+ });
74
+ it("drops the leaf set on markUnused while values stay on disk", async () => {
75
+ const states = HybridSerializedStates.new({ spec, blake2b, dbPath });
76
+ try {
77
+ const empty = InMemoryState.empty(spec);
78
+ const serialized = StateEntries.serializeInMemory(spec, blake2b, empty);
79
+ await states.insertInitialState(headerHash, serialized);
80
+ assert.ok(states.getState(headerHash) !== null);
81
+ states.markUnused(headerHash);
82
+ assert.strictEqual(states.getState(headerHash), null);
83
+ }
84
+ finally {
85
+ await states.close();
86
+ }
87
+ });
88
+ });
@@ -1,4 +1,5 @@
1
1
  export * from "./blocks.js";
2
+ export * from "./hybrid-states.js";
2
3
  export * from "./root.js";
3
4
  export * from "./states.js";
4
5
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../packages/jam/database-lmdb/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,WAAW,CAAC;AAC1B,cAAc,aAAa,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../packages/jam/database-lmdb/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,oBAAoB,CAAC;AACnC,cAAc,WAAW,CAAC;AAC1B,cAAc,aAAa,CAAC"}
@@ -1,3 +1,4 @@
1
1
  export * from "./blocks.js";
2
+ export * from "./hybrid-states.js";
2
3
  export * from "./root.js";
3
4
  export * from "./states.js";
@@ -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,uBAwGxF"}
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"}
@@ -81,8 +81,6 @@ export async function mainFuzz(fuzzConfig, withRelPath) {
81
81
  await finish;
82
82
  }
83
83
  const buildNode = (databaseBasePath) => {
84
- // Enable state/blocks pruning only when running in memory.
85
- // For disk backend, we store everything.
86
84
  const isPersistent = databaseBasePath !== undefined;
87
85
  return mainImporter({
88
86
  ...config,
@@ -99,11 +97,14 @@ export async function mainFuzz(fuzzConfig, withRelPath) {
99
97
  network: null,
100
98
  }, withRelPath, {
101
99
  initGenesisFromAncestry: fuzzConfig.initGenesisFromAncestry,
102
- dummyFinalityDepth: isPersistent ? 0 : 10_000,
103
- pruneBlocks: !isPersistent,
100
+ // Hybrid keeps leaf sets in RAM, so they must be windowed exactly
101
+ // like the in-memory backend; only the large values live on disk.
102
+ dummyFinalityDepth: 10_000,
103
+ pruneBlocks: true,
104
104
  // The fuzz db is wiped on every reset, so durability is pointless:
105
- // skip fsync + compression to cut the per-block leaf write cost.
105
+ // skip fsync + compression to cut the per-block value write cost.
106
106
  ephemeralDb: isPersistent,
107
+ stateBackend: isPersistent ? "hybrid" : "lmdb",
107
108
  });
108
109
  };
109
110
  if (fuzzDbBase !== undefined) {
@@ -6,6 +6,8 @@ export type ImporterOptions = {
6
6
  pruneBlocks?: boolean;
7
7
  /** Open the LMDB database without fsync/compression. Only safe for throwaway dbs (e.g. fuzzing). */
8
8
  ephemeralDb?: boolean;
9
+ /** Persistent backend to use when `databaseBasePath` is set. Defaults to full LMDB. */
10
+ stateBackend?: "lmdb" | "hybrid";
9
11
  };
10
12
  export declare function mainImporter(config: JamConfig, withRelPath: (v: string) => string, options?: ImporterOptions): Promise<NodeApi>;
11
13
  //# sourceMappingURL=main-importer.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"main-importer.d.ts","sourceRoot":"","sources":["../../../../../packages/jam/node/main-importer.ts"],"names":[],"mappings":"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;CACvB,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,CAiFlB"}
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,CA0FlB"}
@@ -5,7 +5,7 @@ import { Blake2b, HASH_SIZE } from "#@typeberry/hash";
5
5
  import { createImporter, ImporterConfig } from "#@typeberry/importer";
6
6
  import { tryAsU16 } from "#@typeberry/numbers";
7
7
  import { CURRENT_SUITE, CURRENT_VERSION, Result, resultToString, version } from "#@typeberry/utils";
8
- import { InMemWorkerConfig, LmdbWorkerConfig } from "#@typeberry/workers-api-node";
8
+ import { HybridWorkerConfig, InMemWorkerConfig, LmdbWorkerConfig } from "#@typeberry/workers-api-node";
9
9
  import { getChainSpec, getDatabasePath, initializeDatabase, logger } from "./common.js";
10
10
  const zeroHash = Bytes.zero(HASH_SIZE).asOpaque();
11
11
  export async function mainImporter(config, withRelPath, options = {}) {
@@ -31,14 +31,23 @@ export async function mainImporter(config, withRelPath, options = {}) {
31
31
  blake2b,
32
32
  workerParams,
33
33
  })
34
- : LmdbWorkerConfig.new({
35
- nodeName,
36
- chainSpec,
37
- blake2b,
38
- dbPath,
39
- workerParams,
40
- ephemeral: options.ephemeralDb ?? false,
41
- });
34
+ : options.stateBackend === "hybrid"
35
+ ? HybridWorkerConfig.new({
36
+ nodeName,
37
+ chainSpec,
38
+ blake2b,
39
+ dbPath,
40
+ workerParams,
41
+ ephemeral: options.ephemeralDb ?? false,
42
+ })
43
+ : LmdbWorkerConfig.new({
44
+ nodeName,
45
+ chainSpec,
46
+ blake2b,
47
+ dbPath,
48
+ workerParams,
49
+ ephemeral: options.ephemeralDb ?? false,
50
+ });
42
51
  // Initialize the database with genesis state and block if there isn't one.
43
52
  logger.info `🛢️ Opening database at ${dbPath}`;
44
53
  const rootDb = workerConfig.openDatabase({ readonly: false });
@@ -71,4 +71,39 @@ export declare class InMemWorkerConfig<T = undefined> implements WorkerConfig<T,
71
71
  readonly: boolean;
72
72
  }): RootDb<BlocksDb, SerializedStatesDb>;
73
73
  }
74
+ /**
75
+ * Hybrid worker config for the fuzz target: in-memory blocks and leaf sets,
76
+ * but large values persisted to LMDB.
77
+ *
78
+ * Like `InMemWorkerConfig`, the blocks and leaf sets are shared across the
79
+ * open/close/reopen dance that genesis init performs, so `openDatabase`
80
+ * returns the same instances and a no-op close. The LMDB root is opened once
81
+ * here and closed by `HybridSerializedStates.close()` at importer teardown.
82
+ *
83
+ * In-process only: it holds shared mutable state (the in-memory leaf
84
+ * dictionary) and so is not thread-transferable. The fuzz target runs the
85
+ * importer in-process via `createImporter`, not in a spawned worker.
86
+ */
87
+ export declare class HybridWorkerConfig<T = undefined> implements WorkerConfig<T, BlocksDb, SerializedStatesDb> {
88
+ readonly nodeName: string;
89
+ readonly chainSpec: ChainSpec;
90
+ readonly workerParams: T;
91
+ readonly blake2b: Blake2b;
92
+ readonly dbPath: string;
93
+ readonly ephemeral: boolean;
94
+ static new<T>({ nodeName, chainSpec, workerParams, blake2b, dbPath, ephemeral, }: {
95
+ nodeName: string;
96
+ chainSpec: ChainSpec;
97
+ workerParams: T;
98
+ blake2b: Blake2b;
99
+ dbPath: string;
100
+ ephemeral?: boolean;
101
+ }): HybridWorkerConfig<T>;
102
+ private readonly blocks;
103
+ private readonly states;
104
+ private constructor();
105
+ openDatabase(_options?: {
106
+ readonly: boolean;
107
+ }): RootDb<BlocksDb, SerializedStatesDb>;
108
+ }
74
109
  //# sourceMappingURL=config.d.ts.map
@@ -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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"}
@@ -1,7 +1,7 @@
1
1
  import { Decoder, Encoder } from "#@typeberry/codec";
2
2
  import { ChainSpec } from "#@typeberry/config";
3
3
  import { InMemoryBlocks, InMemorySerializedStates, } from "#@typeberry/database";
4
- import { LmdbBlocks, LmdbRoot, LmdbStates } from "#@typeberry/database-lmdb";
4
+ import { HybridSerializedStates, LmdbBlocks, LmdbRoot, LmdbStates } from "#@typeberry/database-lmdb";
5
5
  import { Blake2b } from "#@typeberry/hash";
6
6
  import { ThreadPort } from "./port.js";
7
7
  /** A worker config that's usable in node.js and uses LMDB database backend. */
@@ -105,3 +105,54 @@ export class InMemWorkerConfig {
105
105
  };
106
106
  }
107
107
  }
108
+ /**
109
+ * Hybrid worker config for the fuzz target: in-memory blocks and leaf sets,
110
+ * but large values persisted to LMDB.
111
+ *
112
+ * Like `InMemWorkerConfig`, the blocks and leaf sets are shared across the
113
+ * open/close/reopen dance that genesis init performs, so `openDatabase`
114
+ * returns the same instances and a no-op close. The LMDB root is opened once
115
+ * here and closed by `HybridSerializedStates.close()` at importer teardown.
116
+ *
117
+ * In-process only: it holds shared mutable state (the in-memory leaf
118
+ * dictionary) and so is not thread-transferable. The fuzz target runs the
119
+ * importer in-process via `createImporter`, not in a spawned worker.
120
+ */
121
+ export class HybridWorkerConfig {
122
+ nodeName;
123
+ chainSpec;
124
+ workerParams;
125
+ blake2b;
126
+ dbPath;
127
+ ephemeral;
128
+ static new({ nodeName, chainSpec, workerParams, blake2b, dbPath, ephemeral = false, }) {
129
+ return new HybridWorkerConfig(nodeName, chainSpec, workerParams, blake2b, dbPath, ephemeral);
130
+ }
131
+ blocks;
132
+ states;
133
+ constructor(nodeName, chainSpec, workerParams, blake2b, dbPath, ephemeral) {
134
+ this.nodeName = nodeName;
135
+ this.chainSpec = chainSpec;
136
+ this.workerParams = workerParams;
137
+ this.blake2b = blake2b;
138
+ this.dbPath = dbPath;
139
+ this.ephemeral = ephemeral;
140
+ this.blocks = InMemoryBlocks.new();
141
+ this.states = HybridSerializedStates.new({
142
+ spec: this.chainSpec,
143
+ blake2b: this.blake2b,
144
+ dbPath: this.dbPath,
145
+ ephemeral: this.ephemeral,
146
+ readOnly: false,
147
+ });
148
+ }
149
+ openDatabase(_options = { readonly: true }) {
150
+ return {
151
+ getBlocksDb: () => this.blocks,
152
+ getStatesDb: () => this.states,
153
+ // Leaf sets and blocks live in memory; the LMDB values store is closed
154
+ // via states.close() at importer teardown, so this is a no-op.
155
+ close: async () => { },
156
+ };
157
+ }
158
+ }