@typeberry/lib 0.8.4 → 0.9.0-1c3f157

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 (217) hide show
  1. package/package.json +6 -4
  2. package/packages/configs/index.d.ts +30 -1
  3. package/packages/configs/index.d.ts.map +1 -1
  4. package/packages/configs/index.js +4 -2
  5. package/packages/configs/typeberry-dev-full.json +29 -0
  6. package/packages/core/bytes/bytes.d.ts +1 -0
  7. package/packages/core/bytes/bytes.d.ts.map +1 -1
  8. package/packages/core/bytes/bytes.js +8 -0
  9. package/packages/core/collections/sorted-array.d.ts +1 -1
  10. package/packages/core/collections/sorted-array.d.ts.map +1 -1
  11. package/packages/core/collections/sorted-array.js +4 -2
  12. package/packages/core/collections/sorted-array.test.js +10 -0
  13. package/packages/core/collections/sorted-set.d.ts +1 -1
  14. package/packages/core/collections/sorted-set.d.ts.map +1 -1
  15. package/packages/core/collections/sorted-set.js +6 -2
  16. package/packages/core/collections/sorted-set.test.js +24 -0
  17. package/packages/core/utils/debug.d.ts +4 -2
  18. package/packages/core/utils/debug.d.ts.map +1 -1
  19. package/packages/core/utils/debug.js +18 -13
  20. package/packages/core/utils/debug.test.js +12 -6
  21. package/packages/core/utils/index.d.ts +1 -0
  22. package/packages/core/utils/index.d.ts.map +1 -1
  23. package/packages/core/utils/index.js +1 -0
  24. package/packages/core/utils/shutdown.d.ts +18 -0
  25. package/packages/core/utils/shutdown.d.ts.map +1 -0
  26. package/packages/core/utils/shutdown.js +3 -0
  27. package/packages/core/utils/shutdown.node.d.ts +5 -0
  28. package/packages/core/utils/shutdown.node.d.ts.map +1 -0
  29. package/packages/core/utils/shutdown.node.js +76 -0
  30. package/packages/core/utils/shutdown.test.d.ts +2 -0
  31. package/packages/core/utils/shutdown.test.d.ts.map +1 -0
  32. package/packages/core/utils/shutdown.test.js +164 -0
  33. package/packages/extensions/ipc/server.d.ts.map +1 -1
  34. package/packages/extensions/ipc/server.js +7 -0
  35. package/packages/jam/config-node/node-config.d.ts +22 -3
  36. package/packages/jam/config-node/node-config.d.ts.map +1 -1
  37. package/packages/jam/config-node/node-config.js +33 -6
  38. package/packages/jam/config-node/node-config.test.js +11 -4
  39. package/packages/jam/database/index.d.ts +1 -0
  40. package/packages/jam/database/index.d.ts.map +1 -1
  41. package/packages/jam/database/index.js +1 -0
  42. package/packages/jam/database/leaf-db-update.d.ts +1 -0
  43. package/packages/jam/database/leaf-db-update.d.ts.map +1 -1
  44. package/packages/jam/database/leaf-db-update.js +22 -5
  45. package/packages/jam/database/leaf-db-update.test.d.ts +2 -0
  46. package/packages/jam/database/leaf-db-update.test.d.ts.map +1 -0
  47. package/packages/jam/database/leaf-db-update.test.js +81 -0
  48. package/packages/jam/database/serialized-states-db.d.ts +1 -0
  49. package/packages/jam/database/serialized-states-db.d.ts.map +1 -1
  50. package/packages/jam/database/serialized-states-db.js +4 -0
  51. package/packages/jam/database/states.d.ts +11 -0
  52. package/packages/jam/database/states.d.ts.map +1 -1
  53. package/packages/jam/database/states.js +3 -0
  54. package/packages/jam/database/value-refs.d.ts +117 -0
  55. package/packages/jam/database/value-refs.d.ts.map +1 -0
  56. package/packages/jam/database/value-refs.js +206 -0
  57. package/packages/jam/database/value-refs.test.d.ts +2 -0
  58. package/packages/jam/database/value-refs.test.d.ts.map +1 -0
  59. package/packages/jam/database/value-refs.test.js +165 -0
  60. package/packages/jam/database-fjall/blocks.d.ts +25 -0
  61. package/packages/jam/database-fjall/blocks.d.ts.map +1 -0
  62. package/packages/jam/database-fjall/blocks.js +86 -0
  63. package/packages/jam/database-fjall/blocks.test.d.ts +2 -0
  64. package/packages/jam/database-fjall/blocks.test.d.ts.map +1 -0
  65. package/packages/jam/database-fjall/blocks.test.js +62 -0
  66. package/packages/jam/database-fjall/hybrid-states.d.ts +96 -0
  67. package/packages/jam/database-fjall/hybrid-states.d.ts.map +1 -0
  68. package/packages/jam/database-fjall/hybrid-states.js +218 -0
  69. package/packages/jam/database-fjall/hybrid-states.test.d.ts +2 -0
  70. package/packages/jam/database-fjall/hybrid-states.test.d.ts.map +1 -0
  71. package/packages/jam/database-fjall/hybrid-states.test.js +113 -0
  72. package/packages/jam/database-fjall/index.d.ts +5 -0
  73. package/packages/jam/database-fjall/index.d.ts.map +1 -0
  74. package/packages/jam/database-fjall/index.js +4 -0
  75. package/packages/jam/database-fjall/root.d.ts +64 -0
  76. package/packages/jam/database-fjall/root.d.ts.map +1 -0
  77. package/packages/jam/database-fjall/root.js +105 -0
  78. package/packages/jam/database-fjall/states.d.ts +31 -0
  79. package/packages/jam/database-fjall/states.d.ts.map +1 -0
  80. package/packages/jam/database-fjall/states.js +99 -0
  81. package/packages/jam/database-fjall/states.test.d.ts +2 -0
  82. package/packages/jam/database-fjall/states.test.d.ts.map +1 -0
  83. package/packages/jam/database-fjall/states.test.js +150 -0
  84. package/packages/jam/database-lmdb/hybrid-states.d.ts +12 -0
  85. package/packages/jam/database-lmdb/hybrid-states.d.ts.map +1 -1
  86. package/packages/jam/database-lmdb/hybrid-states.js +45 -4
  87. package/packages/jam/database-lmdb/states.d.ts +1 -0
  88. package/packages/jam/database-lmdb/states.d.ts.map +1 -1
  89. package/packages/jam/database-lmdb/states.js +5 -0
  90. package/packages/jam/jamnp-s/tasks/ticket-distribution.d.ts +18 -10
  91. package/packages/jam/jamnp-s/tasks/ticket-distribution.d.ts.map +1 -1
  92. package/packages/jam/jamnp-s/tasks/ticket-distribution.js +44 -68
  93. package/packages/jam/jamnp-s/tasks/ticket-distribution.test.js +30 -8
  94. package/packages/jam/node/export.d.ts.map +1 -1
  95. package/packages/jam/node/export.js +18 -9
  96. package/packages/jam/node/main-fuzz.d.ts +4 -1
  97. package/packages/jam/node/main-fuzz.d.ts.map +1 -1
  98. package/packages/jam/node/main-fuzz.js +142 -55
  99. package/packages/jam/node/main-importer.d.ts +15 -3
  100. package/packages/jam/node/main-importer.d.ts.map +1 -1
  101. package/packages/jam/node/main-importer.js +30 -14
  102. package/packages/jam/node/main.d.ts.map +1 -1
  103. package/packages/jam/node/main.js +42 -15
  104. package/packages/jam/node/workers.d.ts +4 -4
  105. package/packages/jam/node/workers.d.ts.map +1 -1
  106. package/packages/jam/safrole/bandersnatch-vrf.d.ts +24 -4
  107. package/packages/jam/safrole/bandersnatch-vrf.d.ts.map +1 -1
  108. package/packages/jam/safrole/bandersnatch-vrf.js +63 -26
  109. package/packages/jam/safrole/bandersnatch-vrf.test.js +12 -9
  110. package/packages/jam/safrole/bandersnatch-wasm.d.ts +10 -0
  111. package/packages/jam/safrole/bandersnatch-wasm.d.ts.map +1 -1
  112. package/packages/jam/safrole/bandersnatch-wasm.js +12 -0
  113. package/packages/jam/safrole/safrole.js +5 -5
  114. package/packages/jam/safrole/safrole.test.js +13 -13
  115. package/packages/jam/ticket-pool/index.d.ts +4 -0
  116. package/packages/jam/ticket-pool/index.d.ts.map +1 -0
  117. package/packages/jam/ticket-pool/index.js +3 -0
  118. package/packages/jam/ticket-pool/pending-ticket-pool.d.ts +30 -0
  119. package/packages/jam/ticket-pool/pending-ticket-pool.d.ts.map +1 -0
  120. package/packages/jam/ticket-pool/pending-ticket-pool.js +56 -0
  121. package/packages/jam/ticket-pool/pending-ticket-pool.test.d.ts +2 -0
  122. package/packages/jam/ticket-pool/pending-ticket-pool.test.d.ts.map +1 -0
  123. package/packages/jam/ticket-pool/pending-ticket-pool.test.js +67 -0
  124. package/packages/jam/ticket-pool/ticket-validator.d.ts +47 -0
  125. package/packages/jam/ticket-pool/ticket-validator.d.ts.map +1 -0
  126. package/packages/jam/ticket-pool/ticket-validator.js +34 -0
  127. package/packages/jam/ticket-pool/ticket-validator.test.d.ts +2 -0
  128. package/packages/jam/ticket-pool/ticket-validator.test.d.ts.map +1 -0
  129. package/packages/jam/ticket-pool/ticket-validator.test.js +35 -0
  130. package/packages/jam/ticket-pool/verified-ticket-pool.d.ts +26 -0
  131. package/packages/jam/ticket-pool/verified-ticket-pool.d.ts.map +1 -0
  132. package/packages/jam/ticket-pool/verified-ticket-pool.js +41 -0
  133. package/packages/jam/ticket-pool/verified-ticket-pool.test.d.ts +2 -0
  134. package/packages/jam/ticket-pool/verified-ticket-pool.test.d.ts.map +1 -0
  135. package/packages/jam/ticket-pool/verified-ticket-pool.test.js +54 -0
  136. package/packages/workers/api/config.d.ts +2 -2
  137. package/packages/workers/api/config.d.ts.map +1 -1
  138. package/packages/workers/api/config.js +1 -1
  139. package/packages/workers/api/port.d.ts +5 -1
  140. package/packages/workers/api/port.d.ts.map +1 -1
  141. package/packages/workers/api/port.js +56 -13
  142. package/packages/workers/api/port.test.js +35 -0
  143. package/packages/workers/api-node/config.d.ts +65 -9
  144. package/packages/workers/api-node/config.d.ts.map +1 -1
  145. package/packages/workers/api-node/config.js +151 -26
  146. package/packages/workers/api-node/config.test.js +88 -2
  147. package/packages/workers/api-node/port.d.ts +3 -0
  148. package/packages/workers/api-node/port.d.ts.map +1 -1
  149. package/packages/workers/api-node/port.js +31 -2
  150. package/packages/workers/api-node/port.test.js +17 -0
  151. package/packages/workers/api-node/protocol.d.ts +3 -3
  152. package/packages/workers/api-node/protocol.d.ts.map +1 -1
  153. package/packages/workers/api-node/protocol.js +18 -12
  154. package/packages/workers/block-authorship/{generator.d.ts → block-generator.d.ts} +5 -5
  155. package/packages/workers/block-authorship/block-generator.d.ts.map +1 -0
  156. package/packages/workers/block-authorship/{generator.js → block-generator.js} +3 -3
  157. package/packages/workers/block-authorship/block-generator.test.d.ts +2 -0
  158. package/packages/workers/block-authorship/block-generator.test.d.ts.map +1 -0
  159. package/packages/workers/block-authorship/{generator.test.js → block-generator.test.js} +8 -8
  160. package/packages/workers/block-authorship/epoch-authoring-slots.d.ts +35 -0
  161. package/packages/workers/block-authorship/epoch-authoring-slots.d.ts.map +1 -0
  162. package/packages/workers/block-authorship/epoch-authoring-slots.js +86 -0
  163. package/packages/workers/block-authorship/epoch-tracker.d.ts +29 -0
  164. package/packages/workers/block-authorship/epoch-tracker.d.ts.map +1 -0
  165. package/packages/workers/block-authorship/epoch-tracker.js +80 -0
  166. package/packages/workers/block-authorship/index.d.ts.map +1 -1
  167. package/packages/workers/block-authorship/index.js +1 -1
  168. package/packages/workers/block-authorship/main.d.ts +3 -0
  169. package/packages/workers/block-authorship/main.d.ts.map +1 -1
  170. package/packages/workers/block-authorship/main.js +198 -316
  171. package/packages/workers/block-authorship/ticket-generator/bootstrap-main.d.ts +2 -0
  172. package/packages/workers/block-authorship/ticket-generator/bootstrap-main.d.ts.map +1 -0
  173. package/packages/workers/block-authorship/ticket-generator/bootstrap-main.js +23 -0
  174. package/packages/workers/block-authorship/ticket-generator/index.d.ts +16 -0
  175. package/packages/workers/block-authorship/ticket-generator/index.d.ts.map +1 -0
  176. package/packages/workers/block-authorship/ticket-generator/index.js +62 -0
  177. package/packages/workers/block-authorship/ticket-generator/protocol.d.ts +50 -0
  178. package/packages/workers/block-authorship/ticket-generator/protocol.d.ts.map +1 -0
  179. package/packages/workers/block-authorship/ticket-generator/protocol.js +54 -0
  180. package/packages/workers/block-authorship/{ticket-generator.d.ts → ticket-generator/ticket-generator.d.ts} +4 -0
  181. package/packages/workers/block-authorship/ticket-generator/ticket-generator.d.ts.map +1 -0
  182. package/packages/workers/block-authorship/{ticket-generator.js → ticket-generator/ticket-generator.js} +19 -9
  183. package/packages/workers/block-authorship/ticket-generator/ticket-generator.test.d.ts.map +1 -0
  184. package/packages/workers/block-authorship/{ticket-generator.test.js → ticket-generator/ticket-generator.test.js} +13 -9
  185. package/packages/workers/block-authorship/ticket-generator/worker-pool.d.ts +36 -0
  186. package/packages/workers/block-authorship/ticket-generator/worker-pool.d.ts.map +1 -0
  187. package/packages/workers/block-authorship/ticket-generator/worker-pool.js +111 -0
  188. package/packages/workers/block-authorship/ticket-validator.d.ts +31 -0
  189. package/packages/workers/block-authorship/ticket-validator.d.ts.map +1 -0
  190. package/packages/workers/block-authorship/ticket-validator.js +59 -0
  191. package/packages/workers/comms-authorship-network/protocol.d.ts +14 -4
  192. package/packages/workers/comms-authorship-network/protocol.d.ts.map +1 -1
  193. package/packages/workers/comms-authorship-network/protocol.js +12 -6
  194. package/packages/workers/comms-authorship-network/tickets-message.d.ts +0 -14
  195. package/packages/workers/comms-authorship-network/tickets-message.d.ts.map +1 -1
  196. package/packages/workers/comms-authorship-network/tickets-message.js +0 -17
  197. package/packages/workers/importer/finality.d.ts +2 -0
  198. package/packages/workers/importer/finality.d.ts.map +1 -1
  199. package/packages/workers/importer/finality.js +8 -1
  200. package/packages/workers/importer/importer.d.ts +2 -2
  201. package/packages/workers/importer/importer.d.ts.map +1 -1
  202. package/packages/workers/importer/importer.js +6 -5
  203. package/packages/workers/importer/main.d.ts +1 -1
  204. package/packages/workers/importer/main.d.ts.map +1 -1
  205. package/packages/workers/importer/main.js +1 -1
  206. package/packages/workers/importer/stats.d.ts +1 -3
  207. package/packages/workers/importer/stats.d.ts.map +1 -1
  208. package/packages/workers/importer/stats.js +12 -12
  209. package/packages/workers/jam-network/main.d.ts.map +1 -1
  210. package/packages/workers/jam-network/main.js +26 -5
  211. package/packages/workers/block-authorship/generator.d.ts.map +0 -1
  212. package/packages/workers/block-authorship/generator.test.d.ts +0 -2
  213. package/packages/workers/block-authorship/generator.test.d.ts.map +0 -1
  214. package/packages/workers/block-authorship/ticket-generator.d.ts.map +0 -1
  215. package/packages/workers/block-authorship/ticket-generator.test.d.ts.map +0 -1
  216. /package/packages/configs/{typeberry-dev.json → typeberry-dev-tiny.json} +0 -0
  217. /package/packages/workers/block-authorship/{ticket-generator.test.d.ts → ticket-generator/ticket-generator.test.d.ts} +0 -0
@@ -8,7 +8,8 @@ import { tinyChainSpec } from "#@typeberry/config";
8
8
  import { BANDERSNATCH_PROOF_BYTES } from "#@typeberry/crypto";
9
9
  import { Logger } from "#@typeberry/logger";
10
10
  import { createTestPeerPair, MockNetwork } from "#@typeberry/networking/testing.js";
11
- import { OK } from "#@typeberry/utils";
11
+ import { AcceptTicketsValidator, ValidationError } from "#@typeberry/ticket-pool";
12
+ import { OK, Result } from "#@typeberry/utils";
12
13
  import { Connections } from "../peers.js";
13
14
  import { StreamManager } from "../stream-manager.js";
14
15
  import { TicketDistributionTask } from "./ticket-distribution.js";
@@ -34,6 +35,9 @@ describe("TicketDistributionTask", () => {
34
35
  const receivedTickets = [];
35
36
  // Use real TicketDistributionTask
36
37
  const ticketTask = TicketDistributionTask.start(streamManager, connections, tinyChainSpec);
38
+ // Default validator accepts every ticket so the test asserts purely on distribution
39
+ // behaviour. Tests that exercise the rejection path overwrite this.
40
+ ticketTask.setTicketValidator(new AcceptTicketsValidator());
37
41
  // Intercept received tickets by wrapping onTicketReceived behavior
38
42
  // The task already adds received tickets to pending queue via addTicket,
39
43
  // so we can track them by checking the pending queue growth or by
@@ -217,7 +221,7 @@ describe("TicketDistributionTask", () => {
217
221
  assert.strictEqual(peer2.receivedTickets.length, 1);
218
222
  assert.deepStrictEqual(peer2.receivedTickets[0].ticket, ticket);
219
223
  });
220
- it("should NOT redistribute ticket if validation callback returns false", async () => {
224
+ it("should NOT redistribute ticket if validator rejects", async () => {
221
225
  const self = await init("self");
222
226
  const peer1 = await init("peer1");
223
227
  const peer2 = await init("peer2");
@@ -225,35 +229,53 @@ describe("TicketDistributionTask", () => {
225
229
  self.openConnection(peer2);
226
230
  await tick();
227
231
  // Validation always rejects
228
- self.ticketTask.setOnTicketReceived(async () => false);
232
+ self.ticketTask.setTicketValidator({
233
+ validate: async () => Result.error(ValidationError.InvalidProof, () => "rejected"),
234
+ });
229
235
  const ticket = createTestTicket(0);
230
236
  peer1.ticketTask.addTicket(TEST_EPOCH, ticket);
231
237
  peer1.ticketTask.maintainDistribution();
232
238
  await tick();
233
- // self.addTicket was NOT called (callback returned false), so nothing to redistribute
239
+ // self.addTicket was NOT called (validator rejected), so nothing to redistribute
234
240
  assert.strictEqual(self.receivedTickets.length, 0);
235
241
  self.ticketTask.maintainDistribution();
236
242
  await tick();
237
243
  assert.strictEqual(peer2.receivedTickets.length, 0);
238
244
  });
239
- it("should redistribute ticket if validation callback returns true", async () => {
245
+ it("should redistribute ticket if validator accepts", async () => {
240
246
  const self = await init("self");
241
247
  const peer1 = await init("peer1");
242
248
  const peer2 = await init("peer2");
243
249
  self.openConnection(peer1);
244
250
  self.openConnection(peer2);
245
251
  await tick();
246
- // Validation always accepts
247
- self.ticketTask.setOnTicketReceived(async () => true);
252
+ // Default init() already wires an AcceptTicketsValidator
248
253
  const ticket = createTestTicket(0);
249
254
  peer1.ticketTask.addTicket(TEST_EPOCH, ticket);
250
255
  peer1.ticketTask.maintainDistribution();
251
256
  await tick();
252
- // self.addTicket WAS called (callback returned true)
257
+ // self.addTicket WAS called
253
258
  assert.strictEqual(self.receivedTickets.length, 1);
254
259
  self.ticketTask.maintainDistribution();
255
260
  await tick();
256
261
  assert.strictEqual(peer2.receivedTickets.length, 1);
257
262
  assert.deepStrictEqual(peer2.receivedTickets[0].ticket, ticket);
258
263
  });
264
+ it("replacePool overwrites the redistribution pool", async () => {
265
+ const self = await init("self");
266
+ const peer1 = await init("peer1");
267
+ self.openConnection(peer1);
268
+ await tick();
269
+ // Locally added tickets first
270
+ self.ticketTask.addTicket(TEST_EPOCH, createTestTicket(0));
271
+ self.ticketTask.addTicket(TEST_EPOCH, createTestTicket(1));
272
+ // Pool dump replaces with a different set
273
+ const dump = [createTestTicket(2), createTestTicket(3)];
274
+ self.ticketTask.replacePool(TEST_EPOCH, dump);
275
+ self.ticketTask.maintainDistribution();
276
+ await tick();
277
+ assert.strictEqual(peer1.receivedTickets.length, 2);
278
+ assert.deepStrictEqual(peer1.receivedTickets[0].ticket, dump[0]);
279
+ assert.deepStrictEqual(peer1.receivedTickets[1].ticket, dump[1]);
280
+ });
259
281
  });
@@ -1 +1 @@
1
- {"version":3,"file":"export.d.ts","sourceRoot":"","sources":["../../../../../packages/jam/node/export.ts"],"names":[],"mappings":"AAUA,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,iBA2F9G"}
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 = LmdbWorkerConfig.new({
33
- nodeName: jamNodeConfig.nodeName,
34
- chainSpec,
35
- blake2b,
36
- dbPath,
37
- workerParams: undefined,
38
- });
39
- const rootDb = config.openDatabase();
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,5 +1,6 @@
1
1
  import { type FuzzVersion } from "#@typeberry/ext-ipc";
2
2
  import { v1 as fuzzV1 } from "#@typeberry/fuzz-proto";
3
+ import { type Closer } from "#@typeberry/utils";
3
4
  import type { JamConfig } from "./jam-config.js";
4
5
  export type FuzzConfig = {
5
6
  version: FuzzVersion;
@@ -24,5 +25,7 @@ export declare function getFuzzDetails(): {
24
25
  nodeVersion: fuzzV1.Version;
25
26
  gpVersion: fuzzV1.Version;
26
27
  };
27
- export declare function mainFuzz(fuzzConfig: FuzzConfig, withRelPath: (v: string) => string): Promise<() => void>;
28
+ export declare function mainFuzz(fuzzConfig: FuzzConfig, withRelPath: (v: string) => string): Promise<{
29
+ close: Closer;
30
+ }>;
28
31
  //# sourceMappingURL=main-fuzz.d.ts.map
@@ -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,uBA2GxF"}
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"}
@@ -8,12 +8,23 @@ import { v1 as fuzzV1 } from "#@typeberry/fuzz-proto";
8
8
  import { HASH_SIZE } from "#@typeberry/hash";
9
9
  import { Logger } from "#@typeberry/logger";
10
10
  import { CURRENT_VERSION, Result, version } from "#@typeberry/utils";
11
- import { logHostEnvironment } from "#@typeberry/workers-api-node";
11
+ import { FjallValuesSession, logHostEnvironment } from "#@typeberry/workers-api-node";
12
12
  import { getChainSpec } from "./common.js";
13
13
  import { mainImporter } from "./main-importer.js";
14
14
  const logger = Logger.new(import.meta.filename, "fuzztarget");
15
15
  /** Dedicated subdirectory under the configured base path that the fuzzer owns and wipes. */
16
16
  const FUZZ_DB_SUBDIR = "typeberry-fuzz-db";
17
+ const FUZZ_DB_FJALL = "fjall-hybrid";
18
+ const FUZZ_DB_LMDB = "lmdb-hybrid";
19
+ const FUZZ_DB_OPTIONS = [FUZZ_DB_FJALL, FUZZ_DB_LMDB];
20
+ /** Subdirectory (under the fuzzer's db dir) holding the reused fjall values keyspace. */
21
+ const FUZZ_FJALL_VALUES_SUBDIR = "values-session";
22
+ /**
23
+ * Size of the fjall block-cache for the fuzz session. Values pile up across
24
+ * resets (for fjall we do not wipe between them), so this cache is what keeps
25
+ * the resident memory bounded.
26
+ */
27
+ const FUZZ_FJALL_CACHE_BYTES = 128 * 1024 * 1024;
17
28
  /**
18
29
  * Resolve the directory the fuzzer should use for its on-disk database, or
19
30
  * `undefined` for an in-memory database. The dedicated `FUZZ_DB_SUBDIR` is
@@ -50,7 +61,24 @@ export async function mainFuzz(fuzzConfig, withRelPath) {
50
61
  logHostEnvironment(logger);
51
62
  const { jamNodeConfig: config } = fuzzConfig;
52
63
  const fuzzDbBase = resolveFuzzDbBase(config.node.databaseBasePath);
64
+ const rawFuzzDb = process.env.JAM_FUZZ_DB?.trim() ?? "";
65
+ // Using experimental fjall-hybrid by default, with an option to test lmdb as well.
66
+ const hybridStateBackend = rawFuzzDb === "" ? FUZZ_DB_FJALL : rawFuzzDb;
67
+ if (!isValidStateBackend(hybridStateBackend)) {
68
+ throw new Error(`JAM_FUZZ_DB must be one of: ${FUZZ_DB_OPTIONS} (got: "${rawFuzzDb}").`);
69
+ }
70
+ if (fuzzDbBase !== undefined) {
71
+ logger.info `🗄️ Fuzz persistent backend: ${hybridStateBackend}.`;
72
+ }
53
73
  let runningNode = null;
74
+ // The fjall values keyspace is opened once per fuzz session and reused on
75
+ // every reset, because opening it is the slow part. Only the in-memory blocks
76
+ // and leaf sets are rebuilt for each vector. fjall-hybrid only.
77
+ let fjallSession = null;
78
+ // Set when close() starts. Guards resetState so a fuzz command arriving
79
+ // mid-shutdown can't build a fresh node that close() then orphans.
80
+ let isClosing = false;
81
+ let activeReset = null;
54
82
  const chainSpec = getChainSpec(config.node.flavor);
55
83
  const closeFuzzTarget = startFuzzTarget(fuzzConfig.version, fuzzConfig.socket, {
56
84
  ...getFuzzDetails(),
@@ -74,65 +102,124 @@ export async function mainFuzz(fuzzConfig, withRelPath) {
74
102
  }
75
103
  return runningNode.getStateEntries(hash);
76
104
  },
77
- resetState: async (header, state, ancestry) => {
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
+ },
127
+ },
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.
171
+ await wipeFuzzDb(fuzzDbBase);
172
+ }
173
+ runningNode = await buildNode(fuzzDbBase);
174
+ return await runningNode.getBestStateRootHash();
175
+ }
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;
180
+ }
181
+ }
182
+ runningNode = await buildNode(undefined);
183
+ return await runningNode.getBestStateRootHash();
184
+ })();
185
+ activeReset = reset;
186
+ const clearActiveReset = () => {
187
+ if (activeReset === reset) {
188
+ activeReset = null;
189
+ }
190
+ };
191
+ reset.then(clearActiveReset, clearActiveReset);
192
+ return reset;
193
+ },
194
+ });
195
+ return {
196
+ close: async () => {
197
+ isClosing = true;
198
+ // Stop accepting connections + unlink the socket.
199
+ closeFuzzTarget();
200
+ // Drain the active session (flush + close DB). Swallow errors so a
201
+ // failing close still lets the process exit 0; the db is wiped next.
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
+ }
78
206
  if (runningNode !== null) {
79
- const finish = runningNode.close();
207
+ const node = runningNode;
80
208
  runningNode = null;
81
- await finish;
209
+ await node.close().catch((e) => logger.error `Error closing fuzz node: ${e}`);
210
+ }
211
+ // Release the reused fjall values keyspace before wiping its files.
212
+ if (fjallSession !== null) {
213
+ const session = fjallSession;
214
+ fjallSession = null;
215
+ await session.close().catch((e) => logger.error `Error closing fjall session: ${e}`);
82
216
  }
83
- const buildNode = (databaseBasePath) => {
84
- const isPersistent = databaseBasePath !== undefined;
85
- return mainImporter({
86
- ...config,
87
- node: {
88
- ...config.node,
89
- databaseBasePath,
90
- chainSpec: {
91
- ...config.node.chainSpec,
92
- genesisHeader: Encoder.encodeObject(Header.Codec, header, chainSpec),
93
- genesisState: new Map(state),
94
- },
95
- },
96
- ancestry,
97
- network: null,
98
- }, withRelPath, {
99
- initGenesisFromAncestry: fuzzConfig.initGenesisFromAncestry,
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: 20,
103
- pruneBlocks: true,
104
- // Long full-spec sessions accumulate a large, never-pruned values db.
105
- // Syncing lets the OS reclaim dirty mmap pages, and compression (full
106
- // spec only, where values are big) bounds its on-disk/page-cache size.
107
- // Tiny stays uncompressed since its db is small and speed matters more.
108
- ephemeral: isPersistent,
109
- stateBackend: isPersistent ? "hybrid" : "lmdb",
110
- });
111
- };
112
217
  if (fuzzDbBase !== undefined) {
113
- // Each reset starts a fresh session from the genesis the fuzzer just sent,
114
- // so the on-disk db must be empty: otherwise initializeDatabase sees an
115
- // already-initialized db and silently resumes the previous run's state.
116
- await wipeFuzzDb(fuzzDbBase);
117
- try {
118
- runningNode = await buildNode(fuzzDbBase);
119
- return await runningNode.getBestStateRootHash();
120
- }
121
- catch (e) {
122
- // A partially-opened db may leak on failure; acceptable for this degraded fallback (proper cleanup belongs in mainImporter).
123
- logger.warn `Failed to open persistent fuzz db at ${fuzzDbBase}, falling back to in-memory: ${e}`;
124
- runningNode = null;
125
- }
218
+ await wipeFuzzDb(fuzzDbBase).catch(() => { });
126
219
  }
127
- runningNode = await buildNode(undefined);
128
- return await runningNode.getBestStateRootHash();
129
220
  },
130
- });
131
- return () => {
132
- closeFuzzTarget();
133
- if (fuzzDbBase !== undefined) {
134
- // best-effort cleanup on shutdown; ignore failures (dir may already be gone).
135
- wipeFuzzDb(fuzzDbBase).catch(() => { });
136
- }
137
221
  };
138
222
  }
223
+ function isValidStateBackend(val) {
224
+ return FUZZ_DB_OPTIONS.indexOf(val) !== -1;
225
+ }
@@ -1,13 +1,25 @@
1
+ import { type FjallValuesSession } from "#@typeberry/workers-api-node";
1
2
  import type { JamConfig } from "./jam-config.js";
2
3
  import type { NodeApi } from "./main.js";
4
+ export type StateBackend = "lmdb" | "fjall" | "lmdb-hybrid" | "fjall-hybrid";
3
5
  export type ImporterOptions = {
4
6
  initGenesisFromAncestry?: boolean;
5
7
  dummyFinalityDepth?: number;
6
8
  pruneBlocks?: boolean;
7
- /** Open the LMDB database without fsync/compression. Only safe for throwaway dbs (e.g. fuzzing). */
9
+ /** Open the database without fsync/compression. Only safe for throwaway dbs (e.g. fuzzing). */
8
10
  ephemeral?: boolean;
9
- /** Persistent backend to use when `databaseBasePath` is set. Defaults to full LMDB. */
10
- stateBackend?: "lmdb" | "hybrid";
11
+ /**
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.
15
+ */
16
+ stateBackend?: StateBackend;
17
+ /**
18
+ * Reuse an already-open fjall values session instead of opening a fresh
19
+ * keyspace. Only used when `stateBackend === "fjall-hybrid"`. The fuzz target
20
+ * opens one per run and reuses it across resets.
21
+ */
22
+ sharedFjallSession?: FjallValuesSession;
11
23
  };
12
24
  export declare function mainImporter(config: JamConfig, withRelPath: (v: string) => string, options?: ImporterOptions): Promise<NodeApi>;
13
25
  //# 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":"AAWA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAIzC,MAAM,MAAM,eAAe,GAAG;IAC5B,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAClC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,oGAAoG;IACpG,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,uFAAuF;IACvF,YAAY,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAC;CAClC,CAAC;AAEF,wBAAsB,YAAY,CAChC,MAAM,EAAE,SAAS,EACjB,WAAW,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,EAClC,OAAO,GAAE,eAAoB,GAC5B,OAAO,CAAC,OAAO,CAAC,CAoGlB"}
1
+ {"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 ? "in-memory" : (options.stateBackend ?? "lmdb");
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;
@@ -39,8 +44,8 @@ export async function mainImporter(config, withRelPath, options = {}) {
39
44
  blake2b,
40
45
  workerParams,
41
46
  })
42
- : dbBackend === "hybrid"
43
- ? HybridWorkerConfig.new({
47
+ : dbBackend === "lmdb-hybrid" || dbBackend === "fjall-hybrid"
48
+ ? await HybridWorkerConfig.new({
44
49
  nodeName,
45
50
  chainSpec,
46
51
  blake2b,
@@ -48,18 +53,29 @@ export async function mainImporter(config, withRelPath, options = {}) {
48
53
  workerParams,
49
54
  ephemeral,
50
55
  compression,
56
+ backend: dbBackend === "lmdb-hybrid" ? "lmdb" : "fjall",
57
+ sharedFjallSession: options.sharedFjallSession,
51
58
  })
52
- : LmdbWorkerConfig.new({
53
- nodeName,
54
- chainSpec,
55
- blake2b,
56
- dbPath,
57
- workerParams,
58
- ephemeral,
59
- });
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
+ });
60
76
  // Initialize the database with genesis state and block if there isn't one.
61
77
  logger.info `🛢️ Opening database at ${dbPath}`;
62
- const rootDb = workerConfig.openDatabase({ readonly: false });
78
+ const rootDb = await workerConfig.openDatabase({ readonly: false });
63
79
  await initializeDatabase(chainSpec, blake2b, genesisHeaderHash, rootDb, config.node.chainSpec, config.ancestry, {
64
80
  initGenesisFromAncestry: options.initGenesisFromAncestry,
65
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;AAe/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;AAKnF,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,CA2KlB"}
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" : "lmdb"}.`;
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: LmdbWorkerConfig.new(importerParams) };
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
- await initializeDatabase(chainSpec, blake2b, genesisHeaderHash, rootDb, config.node.chainSpec, config.ancestry);
50
- // NOTE [ToDr] even though, we should be closing the database here,
51
- // it seems that opening it in the main thread for writing, and later
52
- // in the importer thread, causes issues. Everything works fine though,
53
- // if we DO NOT close the database (I guess it's process-shared?)
54
- // await rootDb.close();
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
- logger.log `[main] 🛢️ Closing the database`;
142
- await rootDb.close();
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(LmdbWorkerConfig.new({
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(LmdbWorkerConfig.new({
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
+ }