@typeberry/lib 0.9.0-959889e → 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.
- package/package.json +1 -1
- package/packages/core/collections/sorted-array.d.ts +1 -1
- package/packages/core/collections/sorted-array.d.ts.map +1 -1
- package/packages/core/collections/sorted-array.js +4 -2
- package/packages/core/collections/sorted-array.test.js +10 -0
- package/packages/core/collections/sorted-set.d.ts +1 -1
- package/packages/core/collections/sorted-set.d.ts.map +1 -1
- package/packages/core/collections/sorted-set.js +6 -2
- package/packages/core/collections/sorted-set.test.js +24 -0
- package/packages/core/utils/shutdown.d.ts +3 -3
- package/packages/core/utils/shutdown.d.ts.map +1 -1
- package/packages/core/utils/shutdown.js +2 -79
- package/packages/core/utils/shutdown.node.d.ts +5 -0
- package/packages/core/utils/shutdown.node.d.ts.map +1 -0
- package/packages/core/utils/shutdown.node.js +76 -0
- package/packages/core/utils/shutdown.test.js +32 -23
- package/packages/jam/config-node/node-config.d.ts +20 -2
- package/packages/jam/config-node/node-config.d.ts.map +1 -1
- package/packages/jam/config-node/node-config.js +25 -3
- package/packages/jam/config-node/node-config.test.js +8 -1
- package/packages/jam/database/index.d.ts +1 -0
- package/packages/jam/database/index.d.ts.map +1 -1
- package/packages/jam/database/index.js +1 -0
- package/packages/jam/database/leaf-db-update.d.ts +1 -0
- package/packages/jam/database/leaf-db-update.d.ts.map +1 -1
- package/packages/jam/database/leaf-db-update.js +22 -5
- package/packages/jam/database/leaf-db-update.test.d.ts +2 -0
- package/packages/jam/database/leaf-db-update.test.d.ts.map +1 -0
- package/packages/jam/database/leaf-db-update.test.js +81 -0
- package/packages/jam/database/serialized-states-db.d.ts +4 -0
- package/packages/jam/database/serialized-states-db.d.ts.map +1 -1
- package/packages/jam/database/serialized-states-db.js +18 -2
- package/packages/jam/database/serialized-states-db.test.d.ts +2 -0
- package/packages/jam/database/serialized-states-db.test.d.ts.map +1 -0
- package/packages/jam/database/serialized-states-db.test.js +109 -0
- package/packages/jam/database/states.d.ts +11 -0
- package/packages/jam/database/states.d.ts.map +1 -1
- package/packages/jam/database/states.js +3 -0
- package/packages/jam/database/value-refs.d.ts +117 -0
- package/packages/jam/database/value-refs.d.ts.map +1 -0
- package/packages/jam/database/value-refs.js +206 -0
- package/packages/jam/database/value-refs.test.d.ts +2 -0
- package/packages/jam/database/value-refs.test.d.ts.map +1 -0
- package/packages/jam/database/value-refs.test.js +165 -0
- package/packages/jam/database-fjall/blocks.d.ts +25 -0
- package/packages/jam/database-fjall/blocks.d.ts.map +1 -0
- package/packages/jam/database-fjall/blocks.js +86 -0
- package/packages/jam/database-fjall/blocks.test.d.ts +2 -0
- package/packages/jam/database-fjall/blocks.test.d.ts.map +1 -0
- package/packages/jam/database-fjall/blocks.test.js +62 -0
- package/packages/jam/database-fjall/hybrid-states.d.ts +14 -0
- package/packages/jam/database-fjall/hybrid-states.d.ts.map +1 -1
- package/packages/jam/database-fjall/hybrid-states.js +55 -6
- package/packages/jam/database-fjall/hybrid-states.test.js +94 -2
- package/packages/jam/database-fjall/index.d.ts +2 -0
- package/packages/jam/database-fjall/index.d.ts.map +1 -1
- package/packages/jam/database-fjall/index.js +2 -0
- package/packages/jam/database-fjall/root.d.ts +14 -8
- package/packages/jam/database-fjall/root.d.ts.map +1 -1
- package/packages/jam/database-fjall/root.js +25 -9
- package/packages/jam/database-fjall/states.d.ts +31 -0
- package/packages/jam/database-fjall/states.d.ts.map +1 -0
- package/packages/jam/database-fjall/states.js +99 -0
- package/packages/jam/database-fjall/states.test.d.ts +2 -0
- package/packages/jam/database-fjall/states.test.d.ts.map +1 -0
- package/packages/jam/database-fjall/states.test.js +150 -0
- package/packages/jam/database-lmdb/hybrid-states.d.ts +12 -0
- package/packages/jam/database-lmdb/hybrid-states.d.ts.map +1 -1
- package/packages/jam/database-lmdb/hybrid-states.js +45 -4
- package/packages/jam/database-lmdb/hybrid-states.test.js +94 -2
- package/packages/jam/database-lmdb/states.d.ts +1 -0
- package/packages/jam/database-lmdb/states.d.ts.map +1 -1
- package/packages/jam/database-lmdb/states.js +5 -0
- package/packages/jam/node/export.d.ts.map +1 -1
- package/packages/jam/node/export.js +18 -9
- package/packages/jam/node/main-fuzz.d.ts.map +1 -1
- package/packages/jam/node/main-fuzz.js +85 -71
- package/packages/jam/node/main-importer.d.ts +4 -2
- package/packages/jam/node/main-importer.d.ts.map +1 -1
- package/packages/jam/node/main-importer.js +26 -12
- package/packages/jam/node/main.d.ts.map +1 -1
- package/packages/jam/node/main.js +42 -15
- package/packages/jam/node/workers.d.ts +4 -4
- package/packages/jam/node/workers.d.ts.map +1 -1
- package/packages/workers/api/config.d.ts +2 -2
- package/packages/workers/api/config.d.ts.map +1 -1
- package/packages/workers/api/config.js +1 -1
- package/packages/workers/api/port.d.ts +5 -1
- package/packages/workers/api/port.d.ts.map +1 -1
- package/packages/workers/api/port.js +56 -13
- package/packages/workers/api/port.test.js +35 -0
- package/packages/workers/api-node/config.d.ts +45 -4
- package/packages/workers/api-node/config.d.ts.map +1 -1
- package/packages/workers/api-node/config.js +127 -9
- package/packages/workers/api-node/config.test.js +52 -3
- package/packages/workers/api-node/port.d.ts +3 -0
- package/packages/workers/api-node/port.d.ts.map +1 -1
- package/packages/workers/api-node/port.js +31 -2
- package/packages/workers/api-node/port.test.js +17 -0
- package/packages/workers/api-node/protocol.d.ts +3 -3
- package/packages/workers/api-node/protocol.d.ts.map +1 -1
- package/packages/workers/api-node/protocol.js +18 -12
- package/packages/workers/block-authorship/main.js +1 -1
- package/packages/workers/importer/finality.d.ts +4 -0
- package/packages/workers/importer/finality.d.ts.map +1 -1
- package/packages/workers/importer/finality.js +10 -1
- package/packages/workers/importer/finality.test.js +6 -0
- package/packages/workers/importer/importer.d.ts.map +1 -1
- package/packages/workers/importer/importer.js +3 -0
- package/packages/workers/importer/main.d.ts +1 -1
- package/packages/workers/importer/main.d.ts.map +1 -1
- package/packages/workers/importer/main.js +1 -1
- package/packages/workers/jam-network/main.js +1 -1
|
@@ -2,12 +2,14 @@
|
|
|
2
2
|
import assert from "node:assert";
|
|
3
3
|
import * as fs from "node:fs";
|
|
4
4
|
import { afterEach, before, beforeEach, describe, it } from "node:test";
|
|
5
|
+
import { tryAsServiceId } from "#@typeberry/block";
|
|
5
6
|
import { Bytes, BytesBlob } from "#@typeberry/bytes";
|
|
6
7
|
import { tinyChainSpec } from "#@typeberry/config";
|
|
7
8
|
import { Blake2b, HASH_SIZE } from "#@typeberry/hash";
|
|
8
|
-
import { InMemoryState } from "#@typeberry/state";
|
|
9
|
+
import { InMemoryState, StorageItem, UpdateStorage, } from "#@typeberry/state";
|
|
10
|
+
import { testState } from "#@typeberry/state/test.utils.js";
|
|
9
11
|
import { StateEntries } from "#@typeberry/state-merkleization";
|
|
10
|
-
import { deepEqual, OK, Result } from "#@typeberry/utils";
|
|
12
|
+
import { asOpaqueType, deepEqual, OK, Result } from "#@typeberry/utils";
|
|
11
13
|
import { HybridSerializedStates } from "./hybrid-states.js";
|
|
12
14
|
let blake2b;
|
|
13
15
|
before(async () => {
|
|
@@ -86,3 +88,93 @@ describe("Hybrid serialized states", () => {
|
|
|
86
88
|
}
|
|
87
89
|
});
|
|
88
90
|
});
|
|
91
|
+
function hh(n) {
|
|
92
|
+
return Bytes.fill(HASH_SIZE, n).asOpaque();
|
|
93
|
+
}
|
|
94
|
+
const storageKey = asOpaqueType(BytesBlob.blobFromString("test-key"));
|
|
95
|
+
/** A state update writing a single large (non-embedded) value under `storageKey`. */
|
|
96
|
+
function storageUpdate(value) {
|
|
97
|
+
const item = StorageItem.create({ key: storageKey, value: BytesBlob.blobFromString(value) });
|
|
98
|
+
return {
|
|
99
|
+
storage: new Map([[tryAsServiceId(0), [UpdateStorage.set({ storage: item })]]]),
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
// > 32 bytes => stored in the values db (not embedded in the leaf).
|
|
103
|
+
const BIG_1 = "a".repeat(100);
|
|
104
|
+
const BIG_2 = "b".repeat(100);
|
|
105
|
+
/** `true` if every value referenced by the state can still be resolved. */
|
|
106
|
+
function canReadFully(state) {
|
|
107
|
+
if (state === null) {
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
try {
|
|
111
|
+
state.backend.intoStateEntries();
|
|
112
|
+
return true;
|
|
113
|
+
}
|
|
114
|
+
catch {
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
/** Value removals are queued, so poll for the expected outcome. */
|
|
119
|
+
async function eventually(check, what, timeoutMs = 5_000) {
|
|
120
|
+
const start = Date.now();
|
|
121
|
+
while (!check()) {
|
|
122
|
+
if (Date.now() - start > timeoutMs) {
|
|
123
|
+
assert.fail(`Timed out waiting for: ${what}`);
|
|
124
|
+
}
|
|
125
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
describe("Hybrid serialized states value refcounting", () => {
|
|
129
|
+
const spec = tinyChainSpec;
|
|
130
|
+
let dbPath = "";
|
|
131
|
+
beforeEach(() => {
|
|
132
|
+
dbPath = createTempDir();
|
|
133
|
+
});
|
|
134
|
+
afterEach(() => {
|
|
135
|
+
fs.rmSync(dbPath, { recursive: true });
|
|
136
|
+
});
|
|
137
|
+
it("removes a replaced value from disk once the replacement finalizes", async () => {
|
|
138
|
+
const states = HybridSerializedStates.new({ spec, blake2b, dbPath });
|
|
139
|
+
try {
|
|
140
|
+
await states.insertInitialState(hh(0), StateEntries.serializeInMemory(spec, blake2b, testState()));
|
|
141
|
+
const s0 = states.getState(hh(0));
|
|
142
|
+
assert.ok(s0 !== null);
|
|
143
|
+
await states.updateAndSetState(hh(1), s0, storageUpdate(BIG_1));
|
|
144
|
+
// a handle to the post-1 state, surviving the pruning below
|
|
145
|
+
const stale1 = states.getState(hh(1));
|
|
146
|
+
const s1 = states.getState(hh(1));
|
|
147
|
+
assert.ok(s1 !== null);
|
|
148
|
+
await states.updateAndSetState(hh(2), s1, storageUpdate(BIG_2));
|
|
149
|
+
states.commitFinalized([hh(1)]);
|
|
150
|
+
assert.ok(canReadFully(stale1), "still referenced by the finalized tip");
|
|
151
|
+
states.commitFinalized([hh(2)]);
|
|
152
|
+
await eventually(() => !canReadFully(stale1), "replaced value removed from lmdb");
|
|
153
|
+
assert.ok(canReadFully(states.getState(hh(2))), "the new finalized tip stays fully readable");
|
|
154
|
+
}
|
|
155
|
+
finally {
|
|
156
|
+
await states.close();
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
it("collects values of a pruned dead fork and keeps the surviving chain intact", async () => {
|
|
160
|
+
const states = HybridSerializedStates.new({ spec, blake2b, dbPath });
|
|
161
|
+
try {
|
|
162
|
+
await states.insertInitialState(hh(0), StateEntries.serializeInMemory(spec, blake2b, testState()));
|
|
163
|
+
const s0 = states.getState(hh(0));
|
|
164
|
+
assert.ok(s0 !== null);
|
|
165
|
+
await states.updateAndSetState(hh(1), s0, storageUpdate(BIG_1));
|
|
166
|
+
// a dead fork on top of genesis, inserting a different value
|
|
167
|
+
const fork = states.getState(hh(0));
|
|
168
|
+
assert.ok(fork !== null);
|
|
169
|
+
await states.updateAndSetState(hh(0xaa), fork, storageUpdate(BIG_2));
|
|
170
|
+
const staleFork = states.getState(hh(0xaa));
|
|
171
|
+
states.markUnused(hh(0xaa));
|
|
172
|
+
assert.strictEqual(states.getState(hh(0xaa)), null);
|
|
173
|
+
await eventually(() => !canReadFully(staleFork), "fork-only value removed from lmdb");
|
|
174
|
+
assert.ok(canReadFully(states.getState(hh(1))), "surviving chain is unaffected");
|
|
175
|
+
}
|
|
176
|
+
finally {
|
|
177
|
+
await states.close();
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
});
|
|
@@ -68,6 +68,7 @@ export declare class LmdbStates implements StatesDb<SerializedState<LeafDb>>, In
|
|
|
68
68
|
updateAndSetState(headerHash: HeaderHash, state: SerializedState<LeafDb>, update: Partial<State & ServicesUpdate>): Promise<Result<OK, StateUpdateError>>;
|
|
69
69
|
getStateRoot(state: SerializedState<LeafDb>): Promise<StateRootHash>;
|
|
70
70
|
getState(root: HeaderHash): SerializedState<LeafDb> | null;
|
|
71
|
+
commitFinalized(_headers: HeaderHash[]): void;
|
|
71
72
|
markUnused(header: HeaderHash): void;
|
|
72
73
|
diskSizeInBytes(): number | null;
|
|
73
74
|
close(): Promise<void>;
|
|
@@ -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;IAIpC,eAAe,IAAI,MAAM,GAAG,IAAI;IAI1B,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,eAAe,CAAC,QAAQ,EAAE,UAAU,EAAE,GAAG,IAAI;IAM7C,UAAU,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI;IAIpC,eAAe,IAAI,MAAM,GAAG,IAAI;IAI1B,KAAK;CAGZ"}
|
|
@@ -128,6 +128,11 @@ export class LmdbStates {
|
|
|
128
128
|
}
|
|
129
129
|
return SerializedState.new(this.spec, this.blake2b, leafDbResult.ok);
|
|
130
130
|
}
|
|
131
|
+
commitFinalized(_headers) {
|
|
132
|
+
// Values are never pruned here. This db survives restarts, so refcounting
|
|
133
|
+
// would need counts persisted (and crash-consistent) alongside the values.
|
|
134
|
+
// See the TODO above - not implemented until actually needed.
|
|
135
|
+
}
|
|
131
136
|
markUnused(header) {
|
|
132
137
|
this.states.removeSync(header.raw);
|
|
133
138
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"export.d.ts","sourceRoot":"","sources":["../../../../../packages/jam/node/export.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"export.d.ts","sourceRoot":"","sources":["../../../../../packages/jam/node/export.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAEjD,wBAAsB,YAAY,CAAC,aAAa,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,iBAoG9G"}
|
|
@@ -3,9 +3,10 @@ import path from "node:path";
|
|
|
3
3
|
import { Block as BlockCodec } from "#@typeberry/block";
|
|
4
4
|
import { Bytes } from "#@typeberry/bytes";
|
|
5
5
|
import { Encoder } from "#@typeberry/codec";
|
|
6
|
+
import { RegularStateBackend } from "#@typeberry/config-node";
|
|
6
7
|
import { Blake2b, HASH_SIZE } from "#@typeberry/hash";
|
|
7
8
|
import { Logger } from "#@typeberry/logger";
|
|
8
|
-
import { LmdbWorkerConfig } from "#@typeberry/workers-api-node";
|
|
9
|
+
import { FjallWorkerConfig, LmdbWorkerConfig } from "#@typeberry/workers-api-node";
|
|
9
10
|
import { getChainSpec, getDatabasePath } from "./common.js";
|
|
10
11
|
export async function exportBlocks(jamNodeConfig, output, withRelPath) {
|
|
11
12
|
const logger = Logger.new(import.meta.filename, "export");
|
|
@@ -29,14 +30,22 @@ export async function exportBlocks(jamNodeConfig, output, withRelPath) {
|
|
|
29
30
|
const blake2b = await Blake2b.createHasher();
|
|
30
31
|
const chainSpec = getChainSpec(jamNodeConfig.node.flavor);
|
|
31
32
|
const { dbPath } = getDatabasePath(blake2b, jamNodeConfig.nodeName, jamNodeConfig.node.chainSpec.genesisHeader, withRelPath(jamNodeConfig.node.databaseBasePath));
|
|
32
|
-
const config =
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
33
|
+
const config = jamNodeConfig.node.stateBackend === RegularStateBackend.Fjall
|
|
34
|
+
? FjallWorkerConfig.new({
|
|
35
|
+
nodeName: jamNodeConfig.nodeName,
|
|
36
|
+
chainSpec,
|
|
37
|
+
blake2b,
|
|
38
|
+
dbPath,
|
|
39
|
+
workerParams: undefined,
|
|
40
|
+
})
|
|
41
|
+
: LmdbWorkerConfig.new({
|
|
42
|
+
nodeName: jamNodeConfig.nodeName,
|
|
43
|
+
chainSpec,
|
|
44
|
+
blake2b,
|
|
45
|
+
dbPath,
|
|
46
|
+
workerParams: undefined,
|
|
47
|
+
});
|
|
48
|
+
const rootDb = await config.openDatabase();
|
|
40
49
|
const blocks = rootDb.getBlocksDb();
|
|
41
50
|
logger.info `📖 Gathering blocks...`;
|
|
42
51
|
const hashes = [];
|
|
@@ -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;AAIrD,OAAO,EAAE,KAAK,MAAM,EAAoC,MAAM,kBAAkB,CAAC;AAGjF,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;AAoBF;;;;;;;;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,GAAG,OAAO,CAAC;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,
|
|
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;AAIrD,OAAO,EAAE,KAAK,MAAM,EAAoC,MAAM,kBAAkB,CAAC;AAGjF,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;AAoBF;;;;;;;;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,GAAG,OAAO,CAAC;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAgLrH"}
|
|
@@ -78,6 +78,7 @@ export async function mainFuzz(fuzzConfig, withRelPath) {
|
|
|
78
78
|
// Set when close() starts. Guards resetState so a fuzz command arriving
|
|
79
79
|
// mid-shutdown can't build a fresh node that close() then orphans.
|
|
80
80
|
let isClosing = false;
|
|
81
|
+
let activeReset = null;
|
|
81
82
|
const chainSpec = getChainSpec(config.node.flavor);
|
|
82
83
|
const closeFuzzTarget = startFuzzTarget(fuzzConfig.version, fuzzConfig.socket, {
|
|
83
84
|
...getFuzzDetails(),
|
|
@@ -101,84 +102,94 @@ export async function mainFuzz(fuzzConfig, withRelPath) {
|
|
|
101
102
|
}
|
|
102
103
|
return runningNode.getStateEntries(hash);
|
|
103
104
|
},
|
|
104
|
-
resetState:
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
const
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
105
|
+
resetState: (header, state, ancestry) => {
|
|
106
|
+
const reset = (async () => {
|
|
107
|
+
if (isClosing) {
|
|
108
|
+
return Bytes.zero(HASH_SIZE).asOpaque();
|
|
109
|
+
}
|
|
110
|
+
if (runningNode !== null) {
|
|
111
|
+
const finish = runningNode.close();
|
|
112
|
+
runningNode = null;
|
|
113
|
+
await finish;
|
|
114
|
+
}
|
|
115
|
+
const buildNode = (databaseBasePath) => {
|
|
116
|
+
const isPersistent = databaseBasePath !== undefined;
|
|
117
|
+
return mainImporter({
|
|
118
|
+
...config,
|
|
119
|
+
node: {
|
|
120
|
+
...config.node,
|
|
121
|
+
databaseBasePath,
|
|
122
|
+
chainSpec: {
|
|
123
|
+
...config.node.chainSpec,
|
|
124
|
+
genesisHeader: Encoder.encodeObject(Header.Codec, header, chainSpec),
|
|
125
|
+
genesisState: new Map(state),
|
|
126
|
+
},
|
|
124
127
|
},
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
128
|
+
ancestry,
|
|
129
|
+
network: null,
|
|
130
|
+
}, withRelPath, {
|
|
131
|
+
initGenesisFromAncestry: fuzzConfig.initGenesisFromAncestry,
|
|
132
|
+
// Hybrid keeps leaf sets in RAM, so they must be windowed exactly
|
|
133
|
+
// like the in-memory backend; only the large values live on disk.
|
|
134
|
+
dummyFinalityDepth: 20,
|
|
135
|
+
pruneBlocks: true,
|
|
136
|
+
// The on-disk fuzz db is throwaway (we wipe it), so open it ephemeral and
|
|
137
|
+
// skip the fsync, we do not need durability here. On full spec ephemeral
|
|
138
|
+
// also turns on compression further down, so the big values do not grow the
|
|
139
|
+
// db too much. Tiny stays uncompressed, its db is small and speed matters more.
|
|
140
|
+
ephemeral: isPersistent,
|
|
141
|
+
stateBackend: isPersistent ? hybridStateBackend : "lmdb",
|
|
142
|
+
// Reuse the session keyspace (fjall-hybrid only, other backends
|
|
143
|
+
// ignore it). Nothing to pass for the in-memory fallback.
|
|
144
|
+
sharedFjallSession: isPersistent ? (fjallSession ?? undefined) : undefined,
|
|
145
|
+
});
|
|
146
|
+
};
|
|
147
|
+
if (fuzzDbBase !== undefined) {
|
|
148
|
+
try {
|
|
149
|
+
if (hybridStateBackend === FUZZ_DB_FJALL) {
|
|
150
|
+
// fjall-hybrid: open the values keyspace once and reuse it on every
|
|
151
|
+
// reset. The values partition is content-addressed and immutable, so
|
|
152
|
+
// it is fine that values pile up across resets, the unreferenced ones
|
|
153
|
+
// just sit there. `initializeDatabase` decides whether the db is
|
|
154
|
+
// already initialized from the in-memory blocks, which we rebuild on
|
|
155
|
+
// every reset, not from the values store, so reusing it does not
|
|
156
|
+
// resume the previous run.
|
|
157
|
+
if (fjallSession === null) {
|
|
158
|
+
// Start from a clean slate once, then keep the keyspace open.
|
|
159
|
+
await wipeFuzzDb(fuzzDbBase);
|
|
160
|
+
fjallSession = await FjallValuesSession.open(`${withRelPath(fuzzDbBase)}/${FUZZ_FJALL_VALUES_SUBDIR}`, {
|
|
161
|
+
ephemeral: true,
|
|
162
|
+
cacheSizeBytes: FUZZ_FJALL_CACHE_BYTES,
|
|
163
|
+
});
|
|
164
|
+
logger.info `🗄️ Opened reusable fjall values session at ${withRelPath(fuzzDbBase)}/${FUZZ_FJALL_VALUES_SUBDIR}`;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
// lmdb-hybrid: keep the old behaviour, wipe and reopen on every
|
|
169
|
+
// reset. A fresh db each reset makes `initializeDatabase` set up
|
|
170
|
+
// genesis again instead of resuming the previous run.
|
|
157
171
|
await wipeFuzzDb(fuzzDbBase);
|
|
158
|
-
fjallSession = await FjallValuesSession.open(`${withRelPath(fuzzDbBase)}/${FUZZ_FJALL_VALUES_SUBDIR}`, {
|
|
159
|
-
ephemeral: true,
|
|
160
|
-
cacheSizeBytes: FUZZ_FJALL_CACHE_BYTES,
|
|
161
|
-
});
|
|
162
|
-
logger.info `🗄️ Opened reusable fjall values session at ${withRelPath(fuzzDbBase)}/${FUZZ_FJALL_VALUES_SUBDIR}`;
|
|
163
172
|
}
|
|
173
|
+
runningNode = await buildNode(fuzzDbBase);
|
|
174
|
+
return await runningNode.getBestStateRootHash();
|
|
164
175
|
}
|
|
165
|
-
|
|
166
|
-
//
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
await wipeFuzzDb(fuzzDbBase);
|
|
176
|
+
catch (e) {
|
|
177
|
+
// A partially-opened db may leak on failure; acceptable for this degraded fallback (proper cleanup belongs in mainImporter).
|
|
178
|
+
logger.warn `Failed to open persistent fuzz db at ${fuzzDbBase}, falling back to in-memory: ${e}`;
|
|
179
|
+
runningNode = null;
|
|
170
180
|
}
|
|
171
|
-
runningNode = await buildNode(fuzzDbBase);
|
|
172
|
-
return await runningNode.getBestStateRootHash();
|
|
173
181
|
}
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
182
|
+
runningNode = await buildNode(undefined);
|
|
183
|
+
return await runningNode.getBestStateRootHash();
|
|
184
|
+
})();
|
|
185
|
+
activeReset = reset;
|
|
186
|
+
const clearActiveReset = () => {
|
|
187
|
+
if (activeReset === reset) {
|
|
188
|
+
activeReset = null;
|
|
178
189
|
}
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
return
|
|
190
|
+
};
|
|
191
|
+
reset.then(clearActiveReset, clearActiveReset);
|
|
192
|
+
return reset;
|
|
182
193
|
},
|
|
183
194
|
});
|
|
184
195
|
return {
|
|
@@ -189,6 +200,9 @@ export async function mainFuzz(fuzzConfig, withRelPath) {
|
|
|
189
200
|
// Drain the active session (flush + close DB). Swallow errors so a
|
|
190
201
|
// failing close still lets the process exit 0; the db is wiped next.
|
|
191
202
|
// The node references the shared fjall session, so it must close first.
|
|
203
|
+
if (activeReset !== null) {
|
|
204
|
+
await activeReset.catch((e) => logger.error `Error waiting for fuzz reset: ${e}`);
|
|
205
|
+
}
|
|
192
206
|
if (runningNode !== null) {
|
|
193
207
|
const node = runningNode;
|
|
194
208
|
runningNode = null;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type FjallValuesSession } from "#@typeberry/workers-api-node";
|
|
2
2
|
import type { JamConfig } from "./jam-config.js";
|
|
3
3
|
import type { NodeApi } from "./main.js";
|
|
4
|
-
export type StateBackend = "lmdb" | "lmdb-hybrid" | "fjall-hybrid";
|
|
4
|
+
export type StateBackend = "lmdb" | "fjall" | "lmdb-hybrid" | "fjall-hybrid";
|
|
5
5
|
export type ImporterOptions = {
|
|
6
6
|
initGenesisFromAncestry?: boolean;
|
|
7
7
|
dummyFinalityDepth?: number;
|
|
@@ -9,7 +9,9 @@ export type ImporterOptions = {
|
|
|
9
9
|
/** Open the database without fsync/compression. Only safe for throwaway dbs (e.g. fuzzing). */
|
|
10
10
|
ephemeral?: boolean;
|
|
11
11
|
/**
|
|
12
|
-
* Persistent backend used when `databaseBasePath` is set. Defaults to
|
|
12
|
+
* Persistent backend used when `databaseBasePath` is set. Defaults to config's backend (fjall unless overridden).
|
|
13
|
+
*
|
|
14
|
+
* lmdb and lmdb-hybrid are deprecated and retained as explicit fallbacks.
|
|
13
15
|
*/
|
|
14
16
|
stateBackend?: StateBackend;
|
|
15
17
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"main-importer.d.ts","sourceRoot":"","sources":["../../../../../packages/jam/node/main-importer.ts"],"names":[],"mappings":"AASA,OAAO,EACL,KAAK,kBAAkB,
|
|
1
|
+
{"version":3,"file":"main-importer.d.ts","sourceRoot":"","sources":["../../../../../packages/jam/node/main-importer.ts"],"names":[],"mappings":"AASA,OAAO,EACL,KAAK,kBAAkB,EAKxB,MAAM,6BAA6B,CAAC;AAErC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAIzC,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,OAAO,GAAG,aAAa,GAAG,cAAc,CAAC;AAE7E,MAAM,MAAM,eAAe,GAAG;IAC5B,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAClC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,+FAA+F;IAC/F,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB;;;;OAIG;IACH,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B;;;;OAIG;IACH,kBAAkB,CAAC,EAAE,kBAAkB,CAAC;CACzC,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,CAqHlB"}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { Bytes } from "#@typeberry/bytes";
|
|
2
2
|
import { PvmBackend } from "#@typeberry/config";
|
|
3
|
-
import { KnownChainSpec } from "#@typeberry/config-node";
|
|
3
|
+
import { KnownChainSpec, RegularStateBackend } from "#@typeberry/config-node";
|
|
4
4
|
import { bandersnatch, initWasm } from "#@typeberry/crypto";
|
|
5
5
|
import { Blake2b, HASH_SIZE } from "#@typeberry/hash";
|
|
6
6
|
import { createImporter, ImporterConfig } from "#@typeberry/importer";
|
|
7
7
|
import { tryAsU16 } from "#@typeberry/numbers";
|
|
8
8
|
import { CURRENT_SUITE, CURRENT_VERSION, Result, resultToString, version } from "#@typeberry/utils";
|
|
9
|
-
import { HybridWorkerConfig, InMemWorkerConfig, LmdbWorkerConfig, } from "#@typeberry/workers-api-node";
|
|
9
|
+
import { FjallWorkerConfig, HybridWorkerConfig, InMemWorkerConfig, LmdbWorkerConfig, } from "#@typeberry/workers-api-node";
|
|
10
10
|
import { getChainSpec, getDatabasePath, initializeDatabase, logger } from "./common.js";
|
|
11
11
|
const zeroHash = Bytes.zero(HASH_SIZE).asOpaque();
|
|
12
12
|
export async function mainImporter(config, withRelPath, options = {}) {
|
|
@@ -18,8 +18,13 @@ export async function mainImporter(config, withRelPath, options = {}) {
|
|
|
18
18
|
logger.info `🐇 Bandersnatch ${bandesnatchNative.isOk ? "native 🚀" : `using wasm: ${bandesnatchNative.error}`}`;
|
|
19
19
|
// Single source of truth for the states db backend: drives both the log line
|
|
20
20
|
// below and the worker config picked further down.
|
|
21
|
-
const dbBackend = config.node.databaseBasePath === undefined
|
|
21
|
+
const dbBackend = config.node.databaseBasePath === undefined
|
|
22
|
+
? "in-memory"
|
|
23
|
+
: (options.stateBackend ?? config.node.stateBackend ?? RegularStateBackend.Fjall);
|
|
22
24
|
logger.info `🗄️ States DB: ${dbBackend}.`;
|
|
25
|
+
if (dbBackend === "lmdb" || dbBackend === "lmdb-hybrid") {
|
|
26
|
+
logger.warn `🗄️ The ${dbBackend} state backend is deprecated. Use fjall unless you need a temporary fallback.`;
|
|
27
|
+
}
|
|
23
28
|
const chainSpec = getChainSpec(config.node.flavor);
|
|
24
29
|
const blake2b = await Blake2b.createHasher();
|
|
25
30
|
const nodeName = config.nodeName;
|
|
@@ -51,17 +56,26 @@ export async function mainImporter(config, withRelPath, options = {}) {
|
|
|
51
56
|
backend: dbBackend === "lmdb-hybrid" ? "lmdb" : "fjall",
|
|
52
57
|
sharedFjallSession: options.sharedFjallSession,
|
|
53
58
|
})
|
|
54
|
-
:
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
59
|
+
: dbBackend === "fjall"
|
|
60
|
+
? FjallWorkerConfig.new({
|
|
61
|
+
nodeName,
|
|
62
|
+
chainSpec,
|
|
63
|
+
blake2b,
|
|
64
|
+
dbPath,
|
|
65
|
+
workerParams,
|
|
66
|
+
ephemeral,
|
|
67
|
+
})
|
|
68
|
+
: LmdbWorkerConfig.new({
|
|
69
|
+
nodeName,
|
|
70
|
+
chainSpec,
|
|
71
|
+
blake2b,
|
|
72
|
+
dbPath,
|
|
73
|
+
workerParams,
|
|
74
|
+
ephemeral,
|
|
75
|
+
});
|
|
62
76
|
// Initialize the database with genesis state and block if there isn't one.
|
|
63
77
|
logger.info `🛢️ Opening database at ${dbPath}`;
|
|
64
|
-
const rootDb = workerConfig.openDatabase({ readonly: false });
|
|
78
|
+
const rootDb = await workerConfig.openDatabase({ readonly: false });
|
|
65
79
|
await initializeDatabase(chainSpec, blake2b, genesisHeaderHash, rootDb, config.node.chainSpec, config.ancestry, {
|
|
66
80
|
initGenesisFromAncestry: options.initGenesisFromAncestry,
|
|
67
81
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../../../../packages/jam/node/main.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAc,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEzF,OAAO,EAAE,KAAK,SAAS,EAAc,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../../../../packages/jam/node/main.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAc,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEzF,OAAO,EAAE,KAAK,SAAS,EAAc,MAAM,mBAAmB,CAAC;AAgB/D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AACnE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAkC,MAAM,EAAW,MAAM,kBAAkB,CAAC;AAYnF,OAAO,KAAK,EAAE,SAAS,EAAiB,MAAM,iBAAiB,CAAC;AAWhE,MAAM,MAAM,OAAO,GAAG;IACpB,SAAS,EAAE,SAAS,CAAC;IACrB,eAAe,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAAC;IAChE,WAAW,CAAC,KAAK,EAAE,SAAS,GAAG,OAAO,CAAC,MAAM,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC,CAAC;IACtE,oBAAoB,IAAI,OAAO,CAAC,aAAa,CAAC,CAAC;IAC/C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB,CAAC;AAEF,wBAAsB,IAAI,CACxB,MAAM,EAAE,SAAS,EACjB,WAAW,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,EAClC,SAAS,EAAE,SAAS,GAAG,IAAI,GAC1B,OAAO,CAAC,OAAO,CAAC,CA2LlB"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { isMainThread } from "node:worker_threads";
|
|
2
2
|
import { AUTHORSHIP_NETWORK_PORT } from "#@typeberry/comms-authorship-network";
|
|
3
3
|
import { PvmBackend } from "#@typeberry/config";
|
|
4
|
+
import { RegularStateBackend } from "#@typeberry/config-node";
|
|
4
5
|
import { initWasm } from "#@typeberry/crypto";
|
|
5
6
|
import { deriveBandersnatchSecretKey, deriveEd25519SecretKey, trivialSeed, } from "#@typeberry/crypto/key-derivation.js";
|
|
6
7
|
import { Blake2b } from "#@typeberry/hash";
|
|
@@ -10,7 +11,7 @@ import { Listener } from "#@typeberry/listener";
|
|
|
10
11
|
import { tryAsU16, tryAsU32 } from "#@typeberry/numbers";
|
|
11
12
|
import { CURRENT_SUITE, CURRENT_VERSION, Result, version } from "#@typeberry/utils";
|
|
12
13
|
import { DirectPort, DirectWorkerConfig } from "#@typeberry/workers-api";
|
|
13
|
-
import { InMemWorkerConfig, LmdbWorkerConfig, logHostEnvironment, ThreadPort } from "#@typeberry/workers-api-node";
|
|
14
|
+
import { FjallWorkerConfig, InMemWorkerConfig, LmdbWorkerConfig, logHostEnvironment, ThreadPort, } from "#@typeberry/workers-api-node";
|
|
14
15
|
import { getChainSpec, getDatabasePath, initializeDatabase, logger } from "./common.js";
|
|
15
16
|
import { initializeExtensions } from "./extensions.js";
|
|
16
17
|
import * as metrics from "./metrics.js";
|
|
@@ -29,9 +30,12 @@ export async function main(config, withRelPath, telemetry) {
|
|
|
29
30
|
const blake2b = await Blake2b.createHasher();
|
|
30
31
|
const nodeName = config.nodeName;
|
|
31
32
|
const isInMemory = config.node.databaseBasePath === undefined;
|
|
32
|
-
logger.info `🗄️ States DB: ${isInMemory ? "in-memory" :
|
|
33
|
+
logger.info `🗄️ States DB: ${isInMemory ? "in-memory" : config.node.stateBackend}.`;
|
|
34
|
+
if (!isInMemory && config.node.stateBackend === RegularStateBackend.Lmdb) {
|
|
35
|
+
logger.warn `🗄️ The lmdb state backend is deprecated. Use state_backend="fjall" unless you need a temporary fallback.`;
|
|
36
|
+
}
|
|
33
37
|
const { dbPath, genesisHeaderHash } = getDatabasePath(blake2b, nodeName, config.node.chainSpec.genesisHeader, withRelPath(config.node.databaseBasePath ?? "<in-memory>"));
|
|
34
|
-
const baseConfig = { nodeName, chainSpec, blake2b, dbPath };
|
|
38
|
+
const baseConfig = { nodeName, chainSpec, blake2b, dbPath, stateBackend: config.node.stateBackend };
|
|
35
39
|
const importerParams = {
|
|
36
40
|
...baseConfig,
|
|
37
41
|
workerParams: ImporterConfig.create({
|
|
@@ -42,16 +46,29 @@ export async function main(config, withRelPath, telemetry) {
|
|
|
42
46
|
};
|
|
43
47
|
const importerConfig = isInMemory
|
|
44
48
|
? { isInMemory, config: InMemWorkerConfig.new(importerParams) }
|
|
45
|
-
: { isInMemory, config:
|
|
49
|
+
: { isInMemory, config: createPersistentWorkerConfig(importerParams) };
|
|
46
50
|
// Initialize the database with genesis state and block if there isn't one.
|
|
47
51
|
logger.info `🛢️ Opening database at ${dbPath}`;
|
|
48
|
-
const rootDb = importerConfig.config.openDatabase({ readonly: false });
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
52
|
+
const rootDb = await importerConfig.config.openDatabase({ readonly: false });
|
|
53
|
+
try {
|
|
54
|
+
await initializeDatabase(chainSpec, blake2b, genesisHeaderHash, rootDb, config.node.chainSpec, config.ancestry);
|
|
55
|
+
}
|
|
56
|
+
catch (e) {
|
|
57
|
+
try {
|
|
58
|
+
await rootDb.close();
|
|
59
|
+
}
|
|
60
|
+
catch (closeError) {
|
|
61
|
+
logger.warn `Failed to close database after initialization error: ${closeError}`;
|
|
62
|
+
}
|
|
63
|
+
throw e;
|
|
64
|
+
}
|
|
65
|
+
// fjall-js shares the engine explicitly and requires every handle to close.
|
|
66
|
+
// Keep lmdb's historical main-thread handle open until shutdown.
|
|
67
|
+
let mainRootDb = rootDb;
|
|
68
|
+
if (!importerConfig.isInMemory && config.node.stateBackend === RegularStateBackend.Fjall) {
|
|
69
|
+
await rootDb.close();
|
|
70
|
+
mainRootDb = null;
|
|
71
|
+
}
|
|
55
72
|
// Start block importer
|
|
56
73
|
let importer;
|
|
57
74
|
let closeImporter;
|
|
@@ -138,8 +155,10 @@ export async function main(config, withRelPath, telemetry) {
|
|
|
138
155
|
await closeImporter();
|
|
139
156
|
logger.log `[main] ☠️ Closing the extensions`;
|
|
140
157
|
closeExtensions();
|
|
141
|
-
|
|
142
|
-
|
|
158
|
+
if (mainRootDb !== null) {
|
|
159
|
+
logger.log `[main] 🛢️ Closing the database`;
|
|
160
|
+
await mainRootDb.close();
|
|
161
|
+
}
|
|
143
162
|
logger.log `[main] 📳 Closing telemetry`;
|
|
144
163
|
await telemetry?.close();
|
|
145
164
|
logger.info `[main] ✅ Done.`;
|
|
@@ -167,7 +186,7 @@ const initAuthorship = async (importer, isAuthoring, isFastForward, params, base
|
|
|
167
186
|
statesDb: params.rootDb.getStatesDb(),
|
|
168
187
|
workerParams,
|
|
169
188
|
}), params.networkingPort)
|
|
170
|
-
: await spawnBlockGeneratorWorker(
|
|
189
|
+
: await spawnBlockGeneratorWorker(createPersistentWorkerConfig({
|
|
171
190
|
...baseConfig,
|
|
172
191
|
workerParams,
|
|
173
192
|
ports: new Map([[AUTHORSHIP_NETWORK_PORT, params.networkingPort]]),
|
|
@@ -205,7 +224,7 @@ const initNetwork = async (importer, params, baseConfig, genesisHeaderHash, netw
|
|
|
205
224
|
statesDb: params.rootDb.getStatesDb(),
|
|
206
225
|
workerParams: networkingConfig,
|
|
207
226
|
}), params.authorshipPort)
|
|
208
|
-
: await spawnNetworkWorker(
|
|
227
|
+
: await spawnNetworkWorker(createPersistentWorkerConfig({
|
|
209
228
|
...baseConfig,
|
|
210
229
|
workerParams: networkingConfig,
|
|
211
230
|
ports: new Map([[AUTHORSHIP_NETWORK_PORT, params.authorshipPort]]),
|
|
@@ -222,3 +241,11 @@ const initNetwork = async (importer, params, baseConfig, genesisHeaderHash, netw
|
|
|
222
241
|
});
|
|
223
242
|
return { closeNetwork: finish, networkApi: network, networkWorker: worker };
|
|
224
243
|
};
|
|
244
|
+
function createPersistentWorkerConfig({ stateBackend, ...params }) {
|
|
245
|
+
switch (stateBackend) {
|
|
246
|
+
case RegularStateBackend.Fjall:
|
|
247
|
+
return FjallWorkerConfig.new(params);
|
|
248
|
+
case RegularStateBackend.Lmdb:
|
|
249
|
+
return LmdbWorkerConfig.new(params);
|
|
250
|
+
}
|
|
251
|
+
}
|