@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 +1 -1
- package/packages/jam/database-lmdb/hybrid-states.d.ts +42 -0
- package/packages/jam/database-lmdb/hybrid-states.d.ts.map +1 -0
- package/packages/jam/database-lmdb/hybrid-states.js +105 -0
- package/packages/jam/database-lmdb/hybrid-states.test.d.ts +2 -0
- package/packages/jam/database-lmdb/hybrid-states.test.d.ts.map +1 -0
- package/packages/jam/database-lmdb/hybrid-states.test.js +88 -0
- package/packages/jam/database-lmdb/index.d.ts +1 -0
- package/packages/jam/database-lmdb/index.d.ts.map +1 -1
- package/packages/jam/database-lmdb/index.js +1 -0
- package/packages/jam/node/main-fuzz.d.ts.map +1 -1
- package/packages/jam/node/main-fuzz.js +6 -5
- package/packages/jam/node/main-importer.d.ts +2 -0
- package/packages/jam/node/main-importer.d.ts.map +1 -1
- package/packages/jam/node/main-importer.js +18 -9
- package/packages/workers/api-node/config.d.ts +35 -0
- package/packages/workers/api-node/config.d.ts.map +1 -1
- package/packages/workers/api-node/config.js +52 -1
package/package.json
CHANGED
|
@@ -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 @@
|
|
|
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 +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 +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,
|
|
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
|
-
|
|
103
|
-
|
|
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
|
|
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;
|
|
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
|
-
:
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
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
|
+
}
|