@typeberry/lib 0.8.4 → 0.9.0

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 (129) 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/utils/debug.d.ts +4 -2
  10. package/packages/core/utils/debug.d.ts.map +1 -1
  11. package/packages/core/utils/debug.js +18 -13
  12. package/packages/core/utils/debug.test.js +12 -6
  13. package/packages/jam/config-node/node-config.d.ts +2 -1
  14. package/packages/jam/config-node/node-config.d.ts.map +1 -1
  15. package/packages/jam/config-node/node-config.js +8 -3
  16. package/packages/jam/config-node/node-config.test.js +3 -3
  17. package/packages/jam/database-fjall/hybrid-states.d.ts +45 -0
  18. package/packages/jam/database-fjall/hybrid-states.d.ts.map +1 -0
  19. package/packages/jam/database-fjall/hybrid-states.js +113 -0
  20. package/packages/jam/database-fjall/hybrid-states.test.d.ts +2 -0
  21. package/packages/jam/database-fjall/hybrid-states.test.d.ts.map +1 -0
  22. package/packages/jam/database-fjall/hybrid-states.test.js +83 -0
  23. package/packages/jam/database-fjall/index.d.ts +3 -0
  24. package/packages/jam/database-fjall/index.d.ts.map +1 -0
  25. package/packages/jam/database-fjall/index.js +2 -0
  26. package/packages/jam/database-fjall/root.d.ts +52 -0
  27. package/packages/jam/database-fjall/root.d.ts.map +1 -0
  28. package/packages/jam/database-fjall/root.js +85 -0
  29. package/packages/jam/jamnp-s/tasks/ticket-distribution.d.ts +18 -10
  30. package/packages/jam/jamnp-s/tasks/ticket-distribution.d.ts.map +1 -1
  31. package/packages/jam/jamnp-s/tasks/ticket-distribution.js +44 -68
  32. package/packages/jam/jamnp-s/tasks/ticket-distribution.test.js +30 -8
  33. package/packages/jam/node/main-fuzz.d.ts.map +1 -1
  34. package/packages/jam/node/main-fuzz.js +16 -1
  35. package/packages/jam/node/main-importer.d.ts +6 -3
  36. package/packages/jam/node/main-importer.d.ts.map +1 -1
  37. package/packages/jam/node/main-importer.js +3 -2
  38. package/packages/jam/safrole/bandersnatch-vrf.d.ts +24 -4
  39. package/packages/jam/safrole/bandersnatch-vrf.d.ts.map +1 -1
  40. package/packages/jam/safrole/bandersnatch-vrf.js +63 -26
  41. package/packages/jam/safrole/bandersnatch-vrf.test.js +12 -9
  42. package/packages/jam/safrole/bandersnatch-wasm.d.ts +10 -0
  43. package/packages/jam/safrole/bandersnatch-wasm.d.ts.map +1 -1
  44. package/packages/jam/safrole/bandersnatch-wasm.js +12 -0
  45. package/packages/jam/safrole/safrole.js +5 -5
  46. package/packages/jam/safrole/safrole.test.js +13 -13
  47. package/packages/jam/ticket-pool/index.d.ts +4 -0
  48. package/packages/jam/ticket-pool/index.d.ts.map +1 -0
  49. package/packages/jam/ticket-pool/index.js +3 -0
  50. package/packages/jam/ticket-pool/pending-ticket-pool.d.ts +30 -0
  51. package/packages/jam/ticket-pool/pending-ticket-pool.d.ts.map +1 -0
  52. package/packages/jam/ticket-pool/pending-ticket-pool.js +56 -0
  53. package/packages/jam/ticket-pool/pending-ticket-pool.test.d.ts +2 -0
  54. package/packages/jam/ticket-pool/pending-ticket-pool.test.d.ts.map +1 -0
  55. package/packages/jam/ticket-pool/pending-ticket-pool.test.js +67 -0
  56. package/packages/jam/ticket-pool/ticket-validator.d.ts +47 -0
  57. package/packages/jam/ticket-pool/ticket-validator.d.ts.map +1 -0
  58. package/packages/jam/ticket-pool/ticket-validator.js +34 -0
  59. package/packages/jam/ticket-pool/ticket-validator.test.d.ts +2 -0
  60. package/packages/jam/ticket-pool/ticket-validator.test.d.ts.map +1 -0
  61. package/packages/jam/ticket-pool/ticket-validator.test.js +35 -0
  62. package/packages/jam/ticket-pool/verified-ticket-pool.d.ts +26 -0
  63. package/packages/jam/ticket-pool/verified-ticket-pool.d.ts.map +1 -0
  64. package/packages/jam/ticket-pool/verified-ticket-pool.js +41 -0
  65. package/packages/jam/ticket-pool/verified-ticket-pool.test.d.ts +2 -0
  66. package/packages/jam/ticket-pool/verified-ticket-pool.test.d.ts.map +1 -0
  67. package/packages/jam/ticket-pool/verified-ticket-pool.test.js +54 -0
  68. package/packages/workers/api-node/config.d.ts +12 -5
  69. package/packages/workers/api-node/config.d.ts.map +1 -1
  70. package/packages/workers/api-node/config.js +20 -17
  71. package/packages/workers/api-node/config.test.js +38 -1
  72. package/packages/workers/block-authorship/{generator.d.ts → block-generator.d.ts} +5 -5
  73. package/packages/workers/block-authorship/block-generator.d.ts.map +1 -0
  74. package/packages/workers/block-authorship/{generator.js → block-generator.js} +3 -3
  75. package/packages/workers/block-authorship/block-generator.test.d.ts +2 -0
  76. package/packages/workers/block-authorship/block-generator.test.d.ts.map +1 -0
  77. package/packages/workers/block-authorship/{generator.test.js → block-generator.test.js} +8 -8
  78. package/packages/workers/block-authorship/epoch-authoring-slots.d.ts +35 -0
  79. package/packages/workers/block-authorship/epoch-authoring-slots.d.ts.map +1 -0
  80. package/packages/workers/block-authorship/epoch-authoring-slots.js +86 -0
  81. package/packages/workers/block-authorship/epoch-tracker.d.ts +29 -0
  82. package/packages/workers/block-authorship/epoch-tracker.d.ts.map +1 -0
  83. package/packages/workers/block-authorship/epoch-tracker.js +80 -0
  84. package/packages/workers/block-authorship/index.d.ts.map +1 -1
  85. package/packages/workers/block-authorship/index.js +1 -1
  86. package/packages/workers/block-authorship/main.d.ts +3 -0
  87. package/packages/workers/block-authorship/main.d.ts.map +1 -1
  88. package/packages/workers/block-authorship/main.js +197 -315
  89. package/packages/workers/block-authorship/ticket-generator/bootstrap-main.d.ts +2 -0
  90. package/packages/workers/block-authorship/ticket-generator/bootstrap-main.d.ts.map +1 -0
  91. package/packages/workers/block-authorship/ticket-generator/bootstrap-main.js +23 -0
  92. package/packages/workers/block-authorship/ticket-generator/index.d.ts +16 -0
  93. package/packages/workers/block-authorship/ticket-generator/index.d.ts.map +1 -0
  94. package/packages/workers/block-authorship/ticket-generator/index.js +62 -0
  95. package/packages/workers/block-authorship/ticket-generator/protocol.d.ts +50 -0
  96. package/packages/workers/block-authorship/ticket-generator/protocol.d.ts.map +1 -0
  97. package/packages/workers/block-authorship/ticket-generator/protocol.js +54 -0
  98. package/packages/workers/block-authorship/{ticket-generator.d.ts → ticket-generator/ticket-generator.d.ts} +4 -0
  99. package/packages/workers/block-authorship/ticket-generator/ticket-generator.d.ts.map +1 -0
  100. package/packages/workers/block-authorship/{ticket-generator.js → ticket-generator/ticket-generator.js} +19 -9
  101. package/packages/workers/block-authorship/ticket-generator/ticket-generator.test.d.ts.map +1 -0
  102. package/packages/workers/block-authorship/{ticket-generator.test.js → ticket-generator/ticket-generator.test.js} +13 -9
  103. package/packages/workers/block-authorship/ticket-generator/worker-pool.d.ts +36 -0
  104. package/packages/workers/block-authorship/ticket-generator/worker-pool.d.ts.map +1 -0
  105. package/packages/workers/block-authorship/ticket-generator/worker-pool.js +111 -0
  106. package/packages/workers/block-authorship/ticket-validator.d.ts +31 -0
  107. package/packages/workers/block-authorship/ticket-validator.d.ts.map +1 -0
  108. package/packages/workers/block-authorship/ticket-validator.js +59 -0
  109. package/packages/workers/comms-authorship-network/protocol.d.ts +14 -4
  110. package/packages/workers/comms-authorship-network/protocol.d.ts.map +1 -1
  111. package/packages/workers/comms-authorship-network/protocol.js +12 -6
  112. package/packages/workers/comms-authorship-network/tickets-message.d.ts +0 -14
  113. package/packages/workers/comms-authorship-network/tickets-message.d.ts.map +1 -1
  114. package/packages/workers/comms-authorship-network/tickets-message.js +0 -17
  115. package/packages/workers/importer/importer.d.ts +2 -2
  116. package/packages/workers/importer/importer.d.ts.map +1 -1
  117. package/packages/workers/importer/importer.js +5 -5
  118. package/packages/workers/importer/stats.d.ts +1 -3
  119. package/packages/workers/importer/stats.d.ts.map +1 -1
  120. package/packages/workers/importer/stats.js +12 -12
  121. package/packages/workers/jam-network/main.d.ts.map +1 -1
  122. package/packages/workers/jam-network/main.js +25 -4
  123. package/packages/workers/block-authorship/generator.d.ts.map +0 -1
  124. package/packages/workers/block-authorship/generator.test.d.ts +0 -2
  125. package/packages/workers/block-authorship/generator.test.d.ts.map +0 -1
  126. package/packages/workers/block-authorship/ticket-generator.d.ts.map +0 -1
  127. package/packages/workers/block-authorship/ticket-generator.test.d.ts.map +0 -1
  128. /package/packages/configs/{typeberry-dev.json → typeberry-dev-tiny.json} +0 -0
  129. /package/packages/workers/block-authorship/{ticket-generator.test.d.ts → ticket-generator/ticket-generator.test.d.ts} +0 -0
@@ -71,13 +71,19 @@ export declare class InMemWorkerConfig<T = undefined> implements WorkerConfig<T,
71
71
  readonly: boolean;
72
72
  }): RootDb<BlocksDb, SerializedStatesDb>;
73
73
  }
74
+ /** Persistent values store backing the hybrid config. */
75
+ export type HybridBackend = "lmdb" | "fjall";
74
76
  /**
75
77
  * Hybrid worker config for the fuzz target: in-memory blocks and leaf sets,
76
- * but large values persisted to LMDB.
78
+ * but large values persisted to disk (LMDB or fjall, selected by `backend`).
79
+ *
80
+ * The fjall backend is opt-in so its performance can be compared against LMDB
81
+ * before committing to it. fjall opens its keyspace asynchronously, hence the
82
+ * async `new`.
77
83
  *
78
84
  * Like `InMemWorkerConfig`, the blocks and leaf sets are shared across the
79
85
  * 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
86
+ * returns the same instances and a no-op close. The values store is opened once
81
87
  * here and closed by `HybridSerializedStates.close()` at importer teardown.
82
88
  *
83
89
  * In-process only: it holds shared mutable state (the in-memory leaf
@@ -92,7 +98,8 @@ export declare class HybridWorkerConfig<T = undefined> implements WorkerConfig<T
92
98
  readonly dbPath: string;
93
99
  readonly ephemeral: boolean;
94
100
  readonly compression: boolean;
95
- static new<T>({ nodeName, chainSpec, workerParams, blake2b, dbPath, ephemeral, compression, }: {
101
+ private readonly states;
102
+ static new<T>({ nodeName, chainSpec, workerParams, blake2b, dbPath, ephemeral, compression, backend, }: {
96
103
  nodeName: string;
97
104
  chainSpec: ChainSpec;
98
105
  workerParams: T;
@@ -100,9 +107,9 @@ export declare class HybridWorkerConfig<T = undefined> implements WorkerConfig<T
100
107
  dbPath: string;
101
108
  ephemeral?: boolean;
102
109
  compression?: boolean;
103
- }): HybridWorkerConfig<T>;
110
+ backend?: HybridBackend;
111
+ }): Promise<HybridWorkerConfig<T>>;
104
112
  private readonly blocks;
105
- private readonly states;
106
113
  private constructor();
107
114
  openDatabase(_options?: {
108
115
  readonly: boolean;
@@ -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,8 @@
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 { HybridSerializedStates, LmdbBlocks, LmdbRoot, LmdbStates } from "#@typeberry/database-lmdb";
4
+ import { HybridSerializedStates as FjallHybridSerializedStates } from "#@typeberry/database-fjall";
5
+ import { LmdbBlocks, HybridSerializedStates as LmdbHybridSerializedStates, LmdbRoot, LmdbStates, } from "#@typeberry/database-lmdb";
5
6
  import { Blake2b } from "#@typeberry/hash";
6
7
  import { ThreadPort } from "./port.js";
7
8
  /** A worker config that's usable in node.js and uses LMDB database backend. */
@@ -110,11 +111,15 @@ export class InMemWorkerConfig {
110
111
  }
111
112
  /**
112
113
  * Hybrid worker config for the fuzz target: in-memory blocks and leaf sets,
113
- * but large values persisted to LMDB.
114
+ * but large values persisted to disk (LMDB or fjall, selected by `backend`).
115
+ *
116
+ * The fjall backend is opt-in so its performance can be compared against LMDB
117
+ * before committing to it. fjall opens its keyspace asynchronously, hence the
118
+ * async `new`.
114
119
  *
115
120
  * Like `InMemWorkerConfig`, the blocks and leaf sets are shared across the
116
121
  * open/close/reopen dance that genesis init performs, so `openDatabase`
117
- * returns the same instances and a no-op close. The LMDB root is opened once
122
+ * returns the same instances and a no-op close. The values store is opened once
118
123
  * here and closed by `HybridSerializedStates.close()` at importer teardown.
119
124
  *
120
125
  * In-process only: it holds shared mutable state (the in-memory leaf
@@ -129,12 +134,17 @@ export class HybridWorkerConfig {
129
134
  dbPath;
130
135
  ephemeral;
131
136
  compression;
132
- static new({ nodeName, chainSpec, workerParams, blake2b, dbPath, ephemeral = false, compression = true, }) {
133
- return new HybridWorkerConfig(nodeName, chainSpec, workerParams, blake2b, dbPath, ephemeral, compression);
137
+ states;
138
+ static async new({ nodeName, chainSpec, workerParams, blake2b, dbPath, ephemeral = false, compression = true, backend = "lmdb", }) {
139
+ // fjall opens its keyspace asynchronously; LMDB is synchronous. Either way
140
+ // the values store is created once here and shared across reopen.
141
+ const states = backend === "fjall"
142
+ ? await FjallHybridSerializedStates.new({ spec: chainSpec, blake2b, dbPath, ephemeral })
143
+ : LmdbHybridSerializedStates.new({ spec: chainSpec, blake2b, dbPath, ephemeral, compression, readOnly: false });
144
+ return new HybridWorkerConfig(nodeName, chainSpec, workerParams, blake2b, dbPath, ephemeral, compression, states);
134
145
  }
135
146
  blocks;
136
- states;
137
- constructor(nodeName, chainSpec, workerParams, blake2b, dbPath, ephemeral, compression = true) {
147
+ constructor(nodeName, chainSpec, workerParams, blake2b, dbPath, ephemeral, compression, states) {
138
148
  this.nodeName = nodeName;
139
149
  this.chainSpec = chainSpec;
140
150
  this.workerParams = workerParams;
@@ -142,22 +152,15 @@ export class HybridWorkerConfig {
142
152
  this.dbPath = dbPath;
143
153
  this.ephemeral = ephemeral;
144
154
  this.compression = compression;
155
+ this.states = states;
145
156
  this.blocks = InMemoryBlocks.new();
146
- this.states = HybridSerializedStates.new({
147
- spec: this.chainSpec,
148
- blake2b: this.blake2b,
149
- dbPath: this.dbPath,
150
- ephemeral: this.ephemeral,
151
- compression: this.compression,
152
- readOnly: false,
153
- });
154
157
  }
155
158
  openDatabase(_options = { readonly: true }) {
156
159
  return {
157
160
  getBlocksDb: () => this.blocks,
158
161
  getStatesDb: () => this.states,
159
- // Leaf sets and blocks live in memory; the LMDB values store is closed
160
- // via states.close() at importer teardown, so this is a no-op.
162
+ // Leaf sets and blocks live in memory; the values store is closed via
163
+ // states.close() at importer teardown, so this is a no-op.
161
164
  close: async () => { },
162
165
  };
163
166
  }
@@ -1,11 +1,12 @@
1
1
  import assert from "node:assert";
2
+ import * as fs from "node:fs";
2
3
  import { describe, it } from "node:test";
3
4
  import { MessageChannel } from "node:worker_threads";
4
5
  import { codec } from "#@typeberry/codec";
5
6
  import { tinyChainSpec } from "#@typeberry/config";
6
7
  import { Blake2b } from "#@typeberry/hash";
7
8
  import { tryAsU32 } from "#@typeberry/numbers";
8
- import { configTransferList, LmdbWorkerConfig } from "./config.js";
9
+ import { configTransferList, HybridWorkerConfig, LmdbWorkerConfig } from "./config.js";
9
10
  import { ThreadPort } from "./port.js";
10
11
  const spec = tinyChainSpec;
11
12
  describe("LmdbWorkerConfig transfer list", () => {
@@ -39,3 +40,39 @@ describe("LmdbWorkerConfig transfer list", () => {
39
40
  }
40
41
  });
41
42
  });
43
+ describe("HybridWorkerConfig", () => {
44
+ // Both persistent backends must construct asynchronously and hand out a
45
+ // working db. fjall is the experimental backend we want to benchmark.
46
+ for (const backend of ["lmdb", "fjall"]) {
47
+ it(`constructs and opens a ${backend}-backed hybrid db`, async () => {
48
+ const blake2b = await Blake2b.createHasher();
49
+ const dbPath = fs.mkdtempSync(`typeberry-hybrid-${backend}-`);
50
+ try {
51
+ const config = await HybridWorkerConfig.new({
52
+ nodeName: "node",
53
+ chainSpec: spec,
54
+ workerParams: undefined,
55
+ blake2b,
56
+ dbPath,
57
+ ephemeral: true,
58
+ backend,
59
+ });
60
+ const db = config.openDatabase({ readonly: false });
61
+ const states = db.getStatesDb();
62
+ try {
63
+ assert.notStrictEqual(db.getBlocksDb(), undefined);
64
+ assert.notStrictEqual(states, undefined);
65
+ }
66
+ finally {
67
+ // The values store owns the on-disk resources (the no-op db.close()
68
+ // does not), so close it explicitly to release the fjall keyspace.
69
+ await states.close();
70
+ await db.close();
71
+ }
72
+ }
73
+ finally {
74
+ fs.rmSync(dbPath, { recursive: true, force: true });
75
+ }
76
+ });
77
+ }
78
+ });
@@ -18,7 +18,7 @@ import { type Opaque } from "#@typeberry/utils";
18
18
  *
19
19
  */
20
20
  export type BlockSealInput = Opaque<BytesBlob, "Seal">;
21
- /** Construction arguments for {@link Generator}. */
21
+ /** Construction arguments for `BlockGenerator`. */
22
22
  export type GeneratorArgs = {
23
23
  chainSpec: ChainSpec;
24
24
  bandersnatch: BandernsatchWasm;
@@ -27,7 +27,7 @@ export type GeneratorArgs = {
27
27
  blocks: BlocksDb;
28
28
  states: StatesDb;
29
29
  };
30
- export declare class Generator {
30
+ export declare class BlockGenerator {
31
31
  private readonly metrics;
32
32
  readonly chainSpec: ChainSpec;
33
33
  readonly bandersnatch: BandernsatchWasm;
@@ -35,8 +35,8 @@ export declare class Generator {
35
35
  readonly blake2b: Blake2b;
36
36
  private readonly blocks;
37
37
  private readonly states;
38
- /** Build a {@link Generator} from its collaborators. */
39
- static new(args: GeneratorArgs): Generator;
38
+ /** Build a block generator from its collaborators. */
39
+ static new(args: GeneratorArgs): BlockGenerator;
40
40
  private constructor();
41
41
  private getLastHeaderAndState;
42
42
  nextBlockView(validatorIndex: ValidatorIndex, bandersnatchSecret: BandersnatchSecretSeed, sealPayload: BlockSealInput, timeSlot: TimeSlot, pendingTickets?: {
@@ -71,4 +71,4 @@ export declare class Generator {
71
71
  id: EntropyHash;
72
72
  }[]): Promise<Block>;
73
73
  }
74
- //# sourceMappingURL=generator.d.ts.map
74
+ //# sourceMappingURL=block-generator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"block-generator.d.ts","sourceRoot":"","sources":["../../../../../packages/workers/block-authorship/block-generator.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,EACL,KAAK,WAAW,EAIhB,KAAK,QAAQ,EACb,KAAK,cAAc,EACpB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,KAAK,SAAS,EAAa,MAAM,2BAA2B,CAAC;AAEtE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAChE,OAAO,EAAS,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAEpD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,EAAoC,KAAK,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AAClG,OAAO,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAC9D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAIvD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yCAAyC,CAAC;AAGhF,OAAO,EAAqB,KAAK,MAAM,EAAU,MAAM,kBAAkB,CAAC;AAM1E;;;;;;;;GAQG;AACH,MAAM,MAAM,cAAc,GAAG,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;AAEvD,mDAAmD;AACnD,MAAM,MAAM,aAAa,GAAG;IAC1B,SAAS,EAAE,SAAS,CAAC;IACrB,YAAY,EAAE,gBAAgB,CAAC;IAC/B,YAAY,EAAE,MAAM,CAAC,YAAY,CAAC;IAClC,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,QAAQ,CAAC;IACjB,MAAM,EAAE,QAAQ,CAAC;CAClB,CAAC;AAEF,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA2C;IAEnE,SAAgB,SAAS,EAAE,SAAS,CAAC;IACrC,SAAgB,YAAY,EAAE,gBAAgB,CAAC;IAC/C,SAAgB,YAAY,EAAE,MAAM,CAAC,YAAY,CAAC;IAClD,SAAgB,OAAO,EAAE,OAAO,CAAC;IACjC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAW;IAClC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAW;IAElC,sDAAsD;IACtD,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,aAAa;IAI9B,OAAO;IAUP,OAAO,CAAC,qBAAqB;IAYvB,aAAa,CACjB,cAAc,EAAE,cAAc,EAC9B,kBAAkB,EAAE,sBAAsB,EAC1C,WAAW,EAAE,cAAc,EAC3B,QAAQ,EAAE,QAAQ,EAClB,cAAc,GAAE;QAAE,MAAM,EAAE,YAAY,CAAC;QAAC,EAAE,EAAE,WAAW,CAAA;KAAE,EAAO,GAC/D,OAAO,CAAC,SAAS,CAAC;IAKrB;;;;;;;;;OASG;YACW,cAAc;IAiB5B;;;;;;;;;;OAUG;IACH,OAAO,CAAC,uBAAuB;IA4BzB,SAAS,CACb,cAAc,EAAE,cAAc,EAC9B,kBAAkB,EAAE,sBAAsB,EAC1C,WAAW,EAAE,cAAc,EAC3B,QAAQ,EAAE,QAAQ,EAClB,cAAc,GAAE;QAAE,MAAM,EAAE,YAAY,CAAC;QAAC,EAAE,EAAE,WAAW,CAAA;KAAE,EAAO;CAqGnE"}
@@ -13,7 +13,7 @@ import { asOpaqueType, now, Result } from "#@typeberry/utils";
13
13
  import * as metrics from "./metrics.js";
14
14
  const EMPTY_AUX_DATA = BytesBlob.empty();
15
15
  const logger = Logger.new(import.meta.filename, "author");
16
- export class Generator {
16
+ export class BlockGenerator {
17
17
  metrics;
18
18
  chainSpec;
19
19
  bandersnatch;
@@ -21,9 +21,9 @@ export class Generator {
21
21
  blake2b;
22
22
  blocks;
23
23
  states;
24
- /** Build a {@link Generator} from its collaborators. */
24
+ /** Build a block generator from its collaborators. */
25
25
  static new(args) {
26
- return new Generator(args);
26
+ return new BlockGenerator(args);
27
27
  }
28
28
  constructor(args) {
29
29
  this.chainSpec = args.chainSpec;
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=block-generator.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"block-generator.test.d.ts","sourceRoot":"","sources":["../../../../../packages/workers/block-authorship/block-generator.test.ts"],"names":[],"mappings":""}
@@ -13,7 +13,7 @@ import { JAM_FALLBACK_SEAL } from "#@typeberry/safrole/constants.js";
13
13
  import { VALIDATOR_META_BYTES, ValidatorData } from "#@typeberry/state";
14
14
  import { SafroleSealingKeysKind } from "#@typeberry/state/safrole-data.js";
15
15
  import { asOpaqueType, deepEqual, Result } from "#@typeberry/utils";
16
- import { Generator } from "./generator.js";
16
+ import { BlockGenerator } from "./block-generator.js";
17
17
  // Test validator data - need 6 validators to match tinyChainSpec.validatorsCount
18
18
  const validatorDataArray = [
19
19
  {
@@ -157,7 +157,7 @@ describe("Generator", () => {
157
157
  const state = createMockState(0);
158
158
  const blocksDb = createMockBlocksDb(MOCK_PARENT_HASH);
159
159
  const statesDb = createMockStatesDb(state);
160
- const generator = Generator.new({
160
+ const generator = BlockGenerator.new({
161
161
  chainSpec: tinyChainSpec,
162
162
  bandersnatch,
163
163
  keccakHasher,
@@ -180,7 +180,7 @@ describe("Generator", () => {
180
180
  const state = createMockState(0);
181
181
  const blocksDb = createMockBlocksDb(MOCK_PARENT_HASH);
182
182
  const statesDb = createMockStatesDb(state);
183
- const generator = Generator.new({
183
+ const generator = BlockGenerator.new({
184
184
  chainSpec: tinyChainSpec,
185
185
  bandersnatch,
186
186
  keccakHasher,
@@ -225,7 +225,7 @@ describe("Generator", () => {
225
225
  const state = createMockState(9);
226
226
  const blocksDb = createMockBlocksDb(MOCK_PARENT_HASH);
227
227
  const statesDb = createMockStatesDb(state);
228
- const generator = Generator.new({
228
+ const generator = BlockGenerator.new({
229
229
  chainSpec: tinyChainSpec,
230
230
  bandersnatch,
231
231
  keccakHasher,
@@ -262,7 +262,7 @@ describe("Generator", () => {
262
262
  };
263
263
  const blocksDb = createMockBlocksDb(MOCK_PARENT_HASH);
264
264
  const statesDb = createMockStatesDb(state);
265
- const generator = Generator.new({
265
+ const generator = BlockGenerator.new({
266
266
  chainSpec: tinyChainSpec,
267
267
  bandersnatch,
268
268
  keccakHasher,
@@ -300,7 +300,7 @@ describe("Generator", () => {
300
300
  const state = createMockState(0);
301
301
  const blocksDb = createMockBlocksDb(MOCK_PARENT_HASH);
302
302
  const statesDb = createMockStatesDb(state);
303
- const generator = Generator.new({
303
+ const generator = BlockGenerator.new({
304
304
  chainSpec: tinyChainSpec,
305
305
  bandersnatch,
306
306
  keccakHasher,
@@ -338,7 +338,7 @@ describe("Generator", () => {
338
338
  const state = createMockState(0);
339
339
  const blocksDb = createMockBlocksDb(MOCK_PARENT_HASH);
340
340
  const statesDb = createMockStatesDb(state);
341
- const generator = Generator.new({
341
+ const generator = BlockGenerator.new({
342
342
  chainSpec: tinyChainSpec,
343
343
  bandersnatch,
344
344
  keccakHasher,
@@ -384,7 +384,7 @@ describe("Generator", () => {
384
384
  const state = createMockState(lastSlotOfEpoch0);
385
385
  const blocksDb = createMockBlocksDb(MOCK_PARENT_HASH);
386
386
  const statesDb = createMockStatesDb(state);
387
- const generator = Generator.new({
387
+ const generator = BlockGenerator.new({
388
388
  chainSpec: tinyChainSpec,
389
389
  bandersnatch,
390
390
  keccakHasher,
@@ -0,0 +1,35 @@
1
+ import type { EntropyHash } from "#@typeberry/block";
2
+ import type { ChainSpec } from "#@typeberry/config";
3
+ import type { BandersnatchKey, BandersnatchSecretSeed, Ed25519Key, Ed25519SecretSeed } from "#@typeberry/crypto";
4
+ import type { BandernsatchWasm } from "#@typeberry/safrole/bandersnatch-wasm.js";
5
+ import { type SafroleSealingKeys } from "#@typeberry/state";
6
+ import type { BlockSealInput } from "./block-generator.js";
7
+ import type { ValidatorSecrets } from "./protocol.js";
8
+ type ValidatorPrivateKeys = {
9
+ bandersnatchSecret: BandersnatchSecretSeed;
10
+ ed25519Secret: Ed25519SecretSeed;
11
+ };
12
+ type ValidatorPublicKeys = {
13
+ bandersnatchPublic: BandersnatchKey;
14
+ ed25519Public: Ed25519Key;
15
+ };
16
+ type ValidatorKeys = ValidatorPrivateKeys & ValidatorPublicKeys;
17
+ export type SlotSealData = {
18
+ key: ValidatorKeys;
19
+ sealPayload: BlockSealInput;
20
+ logId: string;
21
+ };
22
+ /** A helper class to figure out in which slots in the next epoch we are authoring blocks. */
23
+ export declare class EpochAuthoringSlots {
24
+ private readonly ticketsPerValidator;
25
+ private readonly bandersnatch;
26
+ private readonly keys;
27
+ static new(chainSpec: ChainSpec, bandersnatch: BandernsatchWasm, ownedSecrets: readonly ValidatorSecrets[]): Promise<EpochAuthoringSlots>;
28
+ private constructor();
29
+ getValidatorKeys(): ValidatorKeys[];
30
+ getBandersnatchPublicKeys(): (import("@typeberry/bytes").Bytes<32> & import("@typeberry/utils").WithOpaque<"BandersnatchKey">)[];
31
+ getOurSlotsInKeySeries(sealingKeySeries: SafroleSealingKeys, entropy: EntropyHash): Promise<Array<SlotSealData | null>>;
32
+ private getOwnTicketIds;
33
+ }
34
+ export {};
35
+ //# sourceMappingURL=epoch-authoring-slots.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"epoch-authoring-slots.d.ts","sourceRoot":"","sources":["../../../../../packages/workers/block-authorship/epoch-authoring-slots.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAGpD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,KAAK,EAAE,eAAe,EAAE,sBAAsB,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAGhH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yCAAyC,CAAC;AAEhF,OAAO,EAAE,KAAK,kBAAkB,EAA0B,MAAM,kBAAkB,CAAC;AAEnF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAEtD,KAAK,oBAAoB,GAAG;IAC1B,kBAAkB,EAAE,sBAAsB,CAAC;IAC3C,aAAa,EAAE,iBAAiB,CAAC;CAClC,CAAC;AAEF,KAAK,mBAAmB,GAAG;IACzB,kBAAkB,EAAE,eAAe,CAAC;IACpC,aAAa,EAAE,UAAU,CAAC;CAC3B,CAAC;AAEF,KAAK,aAAa,GAAG,oBAAoB,GAAG,mBAAmB,CAAC;AAEhE,MAAM,MAAM,YAAY,GAAG;IACzB,GAAG,EAAE,aAAa,CAAC;IACnB,WAAW,EAAE,cAAc,CAAC;IAC5B,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,6FAA6F;AAC7F,qBAAa,mBAAmB;IAY5B,OAAO,CAAC,QAAQ,CAAC,mBAAmB;IACpC,OAAO,CAAC,QAAQ,CAAC,YAAY;IAC7B,OAAO,CAAC,QAAQ,CAAC,IAAI;WAbV,GAAG,CAAC,SAAS,EAAE,SAAS,EAAE,YAAY,EAAE,gBAAgB,EAAE,YAAY,EAAE,SAAS,gBAAgB,EAAE;IAUhH,OAAO;IAMP,gBAAgB;IAIhB,yBAAyB;IAInB,sBAAsB,CAC1B,gBAAgB,EAAE,kBAAkB,EACpC,OAAO,EAAE,WAAW,GACnB,OAAO,CAAC,KAAK,CAAC,YAAY,GAAG,IAAI,CAAC,CAAC;YAuBxB,eAAe;CAmB9B"}
@@ -0,0 +1,86 @@
1
+ import { BytesBlob } from "#@typeberry/bytes";
2
+ import { HashDictionary } from "#@typeberry/collections";
3
+ import { deriveBandersnatchPublicKey, deriveEd25519PublicKey } from "#@typeberry/crypto/key-derivation.js";
4
+ import bandersnatchVrf from "#@typeberry/safrole/bandersnatch-vrf.js";
5
+ import { JAM_FALLBACK_SEAL, JAM_TICKET_SEAL } from "#@typeberry/safrole/constants.js";
6
+ import { SafroleSealingKeysKind } from "#@typeberry/state";
7
+ import { asOpaqueType } from "#@typeberry/utils";
8
+ /** A helper class to figure out in which slots in the next epoch we are authoring blocks. */
9
+ export class EpochAuthoringSlots {
10
+ ticketsPerValidator;
11
+ bandersnatch;
12
+ keys;
13
+ static async new(chainSpec, bandersnatch, ownedSecrets) {
14
+ const keys = await derivePublicKeys(ownedSecrets);
15
+ const keysDictionary = new HashDictionary();
16
+ for (const key of keys) {
17
+ keysDictionary.set(key.bandersnatchPublic, key);
18
+ }
19
+ return new EpochAuthoringSlots(chainSpec.ticketsPerValidator, bandersnatch, keysDictionary);
20
+ }
21
+ constructor(ticketsPerValidator, bandersnatch, keys) {
22
+ this.ticketsPerValidator = ticketsPerValidator;
23
+ this.bandersnatch = bandersnatch;
24
+ this.keys = keys;
25
+ }
26
+ getValidatorKeys() {
27
+ return Array.from(this.keys.values());
28
+ }
29
+ getBandersnatchPublicKeys() {
30
+ return Array.from(this.keys.keys());
31
+ }
32
+ async getOurSlotsInKeySeries(sealingKeySeries, entropy) {
33
+ // Fallback (keys) mode. Just find keys that match ours.
34
+ if (sealingKeySeries.kind === SafroleSealingKeysKind.Keys) {
35
+ const sealPayload = getFallbackSealPayload(entropy);
36
+ return sealingKeySeries.keys.map((author) => {
37
+ const key = this.keys.get(author);
38
+ if (key === undefined) {
39
+ return null;
40
+ }
41
+ return {
42
+ key,
43
+ sealPayload,
44
+ logId: `key ${key.bandersnatchPublic.toStringTruncated()}`,
45
+ };
46
+ });
47
+ }
48
+ // Tickets mode. Generate own VrfOutputHash for our tickets (cheap) and check if it matches.
49
+ const ownTickets = await this.getOwnTicketIds(entropy);
50
+ const slots = sealingKeySeries.tickets.map((ticket) => ownTickets.get(ticket.id.asOpaque()) ?? null);
51
+ return slots;
52
+ }
53
+ async getOwnTicketIds(entropy) {
54
+ // generate our own tickets first
55
+ const ownTickets = new HashDictionary();
56
+ for (let attempt = 0; attempt < this.ticketsPerValidator; attempt++) {
57
+ const sealPayload = getTicketSealPayload(entropy, attempt);
58
+ for (const key of this.keys.values()) {
59
+ const result = await bandersnatchVrf.getVrfOutputHash(this.bandersnatch, key.bandersnatchSecret, sealPayload);
60
+ if (result.isOk) {
61
+ const ticketId = result.ok.asOpaque();
62
+ ownTickets.set(ticketId, {
63
+ key,
64
+ sealPayload,
65
+ logId: `ticket ${ticketId} (attempt ${attempt})`,
66
+ });
67
+ }
68
+ }
69
+ }
70
+ return ownTickets;
71
+ }
72
+ }
73
+ function derivePublicKeys(keys) {
74
+ return Promise.all(keys.map(async (secrets) => ({
75
+ bandersnatchSecret: secrets.bandersnatch,
76
+ bandersnatchPublic: deriveBandersnatchPublicKey(secrets.bandersnatch),
77
+ ed25519Secret: secrets.ed25519,
78
+ ed25519Public: await deriveEd25519PublicKey(secrets.ed25519),
79
+ })));
80
+ }
81
+ function getTicketSealPayload(entropy, attempt) {
82
+ return asOpaqueType(BytesBlob.blobFromParts(JAM_TICKET_SEAL, entropy.raw, new Uint8Array([attempt])));
83
+ }
84
+ function getFallbackSealPayload(entropy) {
85
+ return asOpaqueType(BytesBlob.blobFromParts(JAM_FALLBACK_SEAL, entropy.raw));
86
+ }
@@ -0,0 +1,29 @@
1
+ import { type EntropyHash, type Epoch, type TimeSlot } from "#@typeberry/block";
2
+ import type { ChainSpec } from "#@typeberry/config";
3
+ import type { Blake2b } from "#@typeberry/hash";
4
+ import type { Logger } from "#@typeberry/logger";
5
+ import type { BandernsatchWasm } from "#@typeberry/safrole/bandersnatch-wasm.js";
6
+ import { type SafroleSealingKeys, type State } from "#@typeberry/state";
7
+ import { Result } from "#@typeberry/utils";
8
+ import { EpochAuthoringSlots, type SlotSealData } from "./epoch-authoring-slots.js";
9
+ import type { ValidatorSecrets } from "./protocol.js";
10
+ /** Per-epoch data computed once when entering (or resuming into) an epoch. */
11
+ export type EpochData = {
12
+ epoch: Epoch;
13
+ epochLength: number;
14
+ sealingKeySeries: SafroleSealingKeys;
15
+ entropy: EntropyHash;
16
+ slots: Array<SlotSealData | null>;
17
+ };
18
+ export declare class EpochTracker {
19
+ private readonly chainSpec;
20
+ private readonly blake2bHasher;
21
+ readonly authoring: EpochAuthoringSlots;
22
+ static new(chainSpec: ChainSpec, bandersnatch: BandernsatchWasm, blake2bHasher: Blake2b, keys: readonly ValidatorSecrets[]): Promise<EpochTracker>;
23
+ private constructor();
24
+ isEpochChanged(stateTimeSlot: TimeSlot, newTimeSlot: TimeSlot): boolean;
25
+ getEpochData(logger: Logger, state: State, newTimeSlot: TimeSlot): Promise<Result<EpochData, string>>;
26
+ private getSealingKeySeries;
27
+ private logEpochAuthorshipInfo;
28
+ }
29
+ //# sourceMappingURL=epoch-tracker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"epoch-tracker.d.ts","sourceRoot":"","sources":["../../../../../packages/workers/block-authorship/epoch-tracker.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,WAAW,EAAE,KAAK,KAAK,EAAE,KAAK,QAAQ,EAAc,MAAM,kBAAkB,CAAC;AAC3F,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAEhD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yCAAyC,CAAC;AAChF,OAAO,EAAE,KAAK,kBAAkB,EAA0B,KAAK,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAC/F,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC1C,OAAO,EAAE,mBAAmB,EAAE,KAAK,YAAY,EAAE,MAAM,4BAA4B,CAAC;AACpF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAEtD,8EAA8E;AAC9E,MAAM,MAAM,SAAS,GAAG;IACtB,KAAK,EAAE,KAAK,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,gBAAgB,EAAE,kBAAkB,CAAC;IACrC,OAAO,EAAE,WAAW,CAAC;IACrB,KAAK,EAAE,KAAK,CAAC,YAAY,GAAG,IAAI,CAAC,CAAC;CACnC,CAAC;AAEF,qBAAa,YAAY;IAYrB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,aAAa;aACd,SAAS,EAAE,mBAAmB;WAbnC,GAAG,CACd,SAAS,EAAE,SAAS,EACpB,YAAY,EAAE,gBAAgB,EAC9B,aAAa,EAAE,OAAO,EACtB,IAAI,EAAE,SAAS,gBAAgB,EAAE;IAMnC,OAAO;IAMP,cAAc,CAAC,aAAa,EAAE,QAAQ,EAAE,WAAW,EAAE,QAAQ,GAAG,OAAO;IAOjE,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,QAAQ,GAAG,OAAO,CAAC,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;YA+B7F,mBAAmB;IAejC,OAAO,CAAC,sBAAsB;CAe/B"}
@@ -0,0 +1,80 @@
1
+ import { tryAsEpoch } from "#@typeberry/block";
2
+ import { Safrole } from "#@typeberry/safrole";
3
+ import { SafroleSealingKeysKind } from "#@typeberry/state";
4
+ import { Result } from "#@typeberry/utils";
5
+ import { EpochAuthoringSlots } from "./epoch-authoring-slots.js";
6
+ export class EpochTracker {
7
+ chainSpec;
8
+ blake2bHasher;
9
+ authoring;
10
+ static async new(chainSpec, bandersnatch, blake2bHasher, keys) {
11
+ const epochSlots = await EpochAuthoringSlots.new(chainSpec, bandersnatch, keys);
12
+ return new EpochTracker(chainSpec, blake2bHasher, epochSlots);
13
+ }
14
+ constructor(chainSpec, blake2bHasher, authoring) {
15
+ this.chainSpec = chainSpec;
16
+ this.blake2bHasher = blake2bHasher;
17
+ this.authoring = authoring;
18
+ }
19
+ isEpochChanged(stateTimeSlot, newTimeSlot) {
20
+ const epochLength = this.chainSpec.epochLength;
21
+ const stateEpoch = Math.floor(stateTimeSlot / epochLength);
22
+ const newEpoch = Math.floor(newTimeSlot / epochLength);
23
+ return newEpoch > stateEpoch;
24
+ }
25
+ async getEpochData(logger, state, newTimeSlot) {
26
+ const sealingKeySeriesResult = await this.getSealingKeySeries(state, newTimeSlot);
27
+ // Propagate the typed failure instead of crashing — `main` decides whether to
28
+ // retry, skip or terminate, and keeps the real error details.
29
+ if (sealingKeySeriesResult.isError) {
30
+ return Result.error(`${sealingKeySeriesResult.error}`, sealingKeySeriesResult.details);
31
+ }
32
+ const epochLength = this.chainSpec.epochLength;
33
+ const sealingKeySeries = sealingKeySeriesResult.ok;
34
+ // On a new epoch, `state.entropy[2]` is the epoch-E entropy (pre-transition);
35
+ // mid-epoch, it has already shifted to `entropy[3]`. Use the same predicate
36
+ // as `getSealingKeySeries` so the entropy and the key series stay consistent
37
+ // even when the first authored block of an epoch isn't exactly at slot E·L.
38
+ const isNewEpoch = this.isEpochChanged(state.timeslot, newTimeSlot);
39
+ const entropy = isNewEpoch ? state.entropy[2] : state.entropy[3];
40
+ const epoch = tryAsEpoch(Math.floor(newTimeSlot / epochLength));
41
+ logger.log `[E${epoch}] is using ${SafroleSealingKeysKind[sealingKeySeries.kind]}`;
42
+ logger.trace `[E${epoch}] ${sealingKeySeries}`;
43
+ const slots = await this.authoring.getOurSlotsInKeySeries(sealingKeySeries, entropy);
44
+ this.logEpochAuthorshipInfo(logger, epoch, slots);
45
+ return Result.ok({
46
+ epoch,
47
+ epochLength,
48
+ sealingKeySeries,
49
+ entropy,
50
+ slots,
51
+ });
52
+ }
53
+ async getSealingKeySeries(state, newTimeSlot) {
54
+ // in case we are not changing epoch, just use the state data
55
+ if (!this.isEpochChanged(state.timeslot, newTimeSlot)) {
56
+ return Result.ok(state.sealingKeySeries);
57
+ }
58
+ // otherwise, pick the new sealing key series already
59
+ const safrole = new Safrole(this.chainSpec, this.blake2bHasher, state);
60
+ return await safrole.getSealingKeySeries({
61
+ entropy: state.entropy[1],
62
+ slot: newTimeSlot,
63
+ punishSet: state.disputesRecords.punishSet,
64
+ });
65
+ }
66
+ logEpochAuthorshipInfo(logger, epoch, slots) {
67
+ let isCreating = false;
68
+ let slot = epoch * this.chainSpec.epochLength;
69
+ for (const sealData of slots) {
70
+ if (sealData !== null) {
71
+ isCreating = true;
72
+ logger.info `[E${epoch}#${slot}] Validator ${sealData.key.bandersnatchPublic.toStringTruncated()} will author using ${sealData.logId}`;
73
+ }
74
+ slot += 1;
75
+ }
76
+ if (isCreating === false) {
77
+ logger.info `[E${epoch}] No blocks to author for this epoch.`;
78
+ }
79
+ }
80
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../packages/workers/block-authorship/index.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAC;AAC1B,cAAc,cAAc,CAAC;AAC7B,cAAc,eAAe,CAAC;AAC9B,eAAO,MAAM,MAAM,mBAAwD,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../packages/workers/block-authorship/index.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAC;AAC1B,cAAc,cAAc,CAAC;AAC7B,cAAc,eAAe,CAAC;AAC9B,eAAO,MAAM,MAAM,mBAAyD,CAAC"}
@@ -1,4 +1,4 @@
1
1
  export * from "./main.js";
2
2
  export * from "./metrics.js";
3
3
  export * from "./protocol.js";
4
- export const WORKER = new URL("./bootstrap-generator.mjs", import.meta.url);
4
+ export const WORKER = new URL("./bootstrap-authorship.mjs", import.meta.url);
@@ -2,6 +2,9 @@ import type { NetworkingComms } from "#@typeberry/comms-authorship-network";
2
2
  import type { WorkerConfig } from "#@typeberry/workers-api";
3
3
  import type { BlockAuthorshipConfig, GeneratorInternal } from "./protocol.js";
4
4
  type Config = WorkerConfig<BlockAuthorshipConfig>;
5
+ /**
6
+ * The `BlockAuthorship` should create new blocks and send them as signals to the main thread.
7
+ */
5
8
  export declare function main(config: Config, comms: GeneratorInternal, networkingComms: NetworkingComms): Promise<void>;
6
9
  export {};
7
10
  //# sourceMappingURL=main.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../../../../packages/workers/block-authorship/main.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qCAAqC,CAAC;AAiB3E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAE3D,OAAO,KAAK,EAAE,qBAAqB,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAK9E,KAAK,MAAM,GAAG,YAAY,CAAC,qBAAqB,CAAC,CAAC;AAwBlD,wBAAsB,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,iBAAiB,EAAE,eAAe,EAAE,eAAe,iBAiapG"}
1
+ {"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../../../../packages/workers/block-authorship/main.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qCAAqC,CAAC;AAS3E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAG3D,OAAO,KAAK,EAAE,qBAAqB,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAM9E,KAAK,MAAM,GAAG,YAAY,CAAC,qBAAqB,CAAC,CAAC;AAElD;;GAEG;AAEH,wBAAsB,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,iBAAiB,EAAE,eAAe,EAAE,eAAe,iBAkKpG"}