@typeberry/lib 0.8.4-2e4ce67 → 0.8.4-79f2119

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 (127) hide show
  1. package/package.json +5 -3
  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 +22 -2
  39. package/packages/jam/safrole/bandersnatch-vrf.d.ts.map +1 -1
  40. package/packages/jam/safrole/bandersnatch-vrf.js +54 -20
  41. package/packages/jam/safrole/bandersnatch-vrf.test.js +3 -2
  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/ticket-pool/index.d.ts +4 -0
  46. package/packages/jam/ticket-pool/index.d.ts.map +1 -0
  47. package/packages/jam/ticket-pool/index.js +3 -0
  48. package/packages/jam/ticket-pool/pending-ticket-pool.d.ts +30 -0
  49. package/packages/jam/ticket-pool/pending-ticket-pool.d.ts.map +1 -0
  50. package/packages/jam/ticket-pool/pending-ticket-pool.js +56 -0
  51. package/packages/jam/ticket-pool/pending-ticket-pool.test.d.ts +2 -0
  52. package/packages/jam/ticket-pool/pending-ticket-pool.test.d.ts.map +1 -0
  53. package/packages/jam/ticket-pool/pending-ticket-pool.test.js +67 -0
  54. package/packages/jam/ticket-pool/ticket-validator.d.ts +47 -0
  55. package/packages/jam/ticket-pool/ticket-validator.d.ts.map +1 -0
  56. package/packages/jam/ticket-pool/ticket-validator.js +34 -0
  57. package/packages/jam/ticket-pool/ticket-validator.test.d.ts +2 -0
  58. package/packages/jam/ticket-pool/ticket-validator.test.d.ts.map +1 -0
  59. package/packages/jam/ticket-pool/ticket-validator.test.js +35 -0
  60. package/packages/jam/ticket-pool/verified-ticket-pool.d.ts +26 -0
  61. package/packages/jam/ticket-pool/verified-ticket-pool.d.ts.map +1 -0
  62. package/packages/jam/ticket-pool/verified-ticket-pool.js +41 -0
  63. package/packages/jam/ticket-pool/verified-ticket-pool.test.d.ts +2 -0
  64. package/packages/jam/ticket-pool/verified-ticket-pool.test.d.ts.map +1 -0
  65. package/packages/jam/ticket-pool/verified-ticket-pool.test.js +54 -0
  66. package/packages/workers/api-node/config.d.ts +12 -5
  67. package/packages/workers/api-node/config.d.ts.map +1 -1
  68. package/packages/workers/api-node/config.js +20 -17
  69. package/packages/workers/api-node/config.test.js +38 -1
  70. package/packages/workers/block-authorship/{generator.d.ts → block-generator.d.ts} +5 -5
  71. package/packages/workers/block-authorship/block-generator.d.ts.map +1 -0
  72. package/packages/workers/block-authorship/{generator.js → block-generator.js} +3 -3
  73. package/packages/workers/block-authorship/block-generator.test.d.ts +2 -0
  74. package/packages/workers/block-authorship/block-generator.test.d.ts.map +1 -0
  75. package/packages/workers/block-authorship/{generator.test.js → block-generator.test.js} +8 -8
  76. package/packages/workers/block-authorship/epoch-authoring-slots.d.ts +35 -0
  77. package/packages/workers/block-authorship/epoch-authoring-slots.d.ts.map +1 -0
  78. package/packages/workers/block-authorship/epoch-authoring-slots.js +86 -0
  79. package/packages/workers/block-authorship/epoch-tracker.d.ts +29 -0
  80. package/packages/workers/block-authorship/epoch-tracker.d.ts.map +1 -0
  81. package/packages/workers/block-authorship/epoch-tracker.js +80 -0
  82. package/packages/workers/block-authorship/index.d.ts.map +1 -1
  83. package/packages/workers/block-authorship/index.js +1 -1
  84. package/packages/workers/block-authorship/main.d.ts +3 -0
  85. package/packages/workers/block-authorship/main.d.ts.map +1 -1
  86. package/packages/workers/block-authorship/main.js +197 -317
  87. package/packages/workers/block-authorship/ticket-generator/bootstrap-main.d.ts +2 -0
  88. package/packages/workers/block-authorship/ticket-generator/bootstrap-main.d.ts.map +1 -0
  89. package/packages/workers/block-authorship/ticket-generator/bootstrap-main.js +23 -0
  90. package/packages/workers/block-authorship/ticket-generator/index.d.ts +16 -0
  91. package/packages/workers/block-authorship/ticket-generator/index.d.ts.map +1 -0
  92. package/packages/workers/block-authorship/ticket-generator/index.js +62 -0
  93. package/packages/workers/block-authorship/ticket-generator/protocol.d.ts +50 -0
  94. package/packages/workers/block-authorship/ticket-generator/protocol.d.ts.map +1 -0
  95. package/packages/workers/block-authorship/ticket-generator/protocol.js +54 -0
  96. package/packages/workers/block-authorship/{ticket-generator.d.ts → ticket-generator/ticket-generator.d.ts} +4 -0
  97. package/packages/workers/block-authorship/ticket-generator/ticket-generator.d.ts.map +1 -0
  98. package/packages/workers/block-authorship/{ticket-generator.js → ticket-generator/ticket-generator.js} +19 -9
  99. package/packages/workers/block-authorship/ticket-generator/ticket-generator.test.d.ts.map +1 -0
  100. package/packages/workers/block-authorship/{ticket-generator.test.js → ticket-generator/ticket-generator.test.js} +13 -9
  101. package/packages/workers/block-authorship/ticket-generator/worker-pool.d.ts +36 -0
  102. package/packages/workers/block-authorship/ticket-generator/worker-pool.d.ts.map +1 -0
  103. package/packages/workers/block-authorship/ticket-generator/worker-pool.js +111 -0
  104. package/packages/workers/block-authorship/ticket-validator.d.ts +31 -0
  105. package/packages/workers/block-authorship/ticket-validator.d.ts.map +1 -0
  106. package/packages/workers/block-authorship/ticket-validator.js +59 -0
  107. package/packages/workers/comms-authorship-network/protocol.d.ts +14 -4
  108. package/packages/workers/comms-authorship-network/protocol.d.ts.map +1 -1
  109. package/packages/workers/comms-authorship-network/protocol.js +12 -6
  110. package/packages/workers/comms-authorship-network/tickets-message.d.ts +0 -14
  111. package/packages/workers/comms-authorship-network/tickets-message.d.ts.map +1 -1
  112. package/packages/workers/comms-authorship-network/tickets-message.js +0 -17
  113. package/packages/workers/importer/importer.d.ts +2 -2
  114. package/packages/workers/importer/importer.d.ts.map +1 -1
  115. package/packages/workers/importer/importer.js +5 -5
  116. package/packages/workers/importer/stats.d.ts +1 -3
  117. package/packages/workers/importer/stats.d.ts.map +1 -1
  118. package/packages/workers/importer/stats.js +12 -12
  119. package/packages/workers/jam-network/main.d.ts.map +1 -1
  120. package/packages/workers/jam-network/main.js +25 -4
  121. package/packages/workers/block-authorship/generator.d.ts.map +0 -1
  122. package/packages/workers/block-authorship/generator.test.d.ts +0 -2
  123. package/packages/workers/block-authorship/generator.test.d.ts.map +0 -1
  124. package/packages/workers/block-authorship/ticket-generator.d.ts.map +0 -1
  125. package/packages/workers/block-authorship/ticket-generator.test.d.ts.map +0 -1
  126. /package/packages/configs/{typeberry-dev.json → typeberry-dev-tiny.json} +0 -0
  127. /package/packages/workers/block-authorship/{ticket-generator.test.d.ts → ticket-generator/ticket-generator.test.d.ts} +0 -0
@@ -0,0 +1,62 @@
1
+ import os from "node:os";
2
+ import { tryAsEpoch } from "#@typeberry/block";
3
+ import { HashSet } from "#@typeberry/collections";
4
+ import { Logger } from "#@typeberry/logger";
5
+ import { measure } from "#@typeberry/utils";
6
+ import { TicketGeneratorPool } from "./worker-pool.js";
7
+ /**
8
+ * Extra validators to generate tickets for, beyond the minimum needed to fill the
9
+ * accumulator. Filling requires `epochLength` distinct valid tickets; each validator
10
+ * yields `ticketsPerValidator`. The margin guards against a few tickets failing to
11
+ * land (extra tickets are simply dropped by the accumulator).
12
+ */
13
+ const TICKET_GENERATION_VALIDATOR_MARGIN = 8;
14
+ /** Leave this many cores for the main thread, importer, network and the OS. */
15
+ const TICKET_POOL_RESERVED_CORES = 4;
16
+ /** Hard cap on ticket-generation worker threads. */
17
+ const TICKET_POOL_MAX_WORKERS = 8;
18
+ const logger = Logger.new(import.meta.filename, "tickets");
19
+ const measureTicketGen = measure("ticket:gen");
20
+ /** Number of worker threads to use for parallel ticket generation. */
21
+ function ticketPoolWorkerCount(validators) {
22
+ const cores = os.availableParallelism?.() ?? os.cpus().length;
23
+ const availableCores = Math.min(cores - TICKET_POOL_RESERVED_CORES, TICKET_POOL_MAX_WORKERS);
24
+ // never reserve more cores than we have validators (makes no sense)
25
+ const requiredCores = Math.min(validators, availableCores);
26
+ return Math.max(1, requiredCores);
27
+ }
28
+ export class TicketGenerator {
29
+ chainSpec;
30
+ keys;
31
+ pool;
32
+ static async new(chainSpec, keys) {
33
+ const pool = await TicketGeneratorPool.create(ticketPoolWorkerCount(keys.length));
34
+ return new TicketGenerator(chainSpec, keys, pool);
35
+ }
36
+ constructor(chainSpec, keys, pool) {
37
+ this.chainSpec = chainSpec;
38
+ this.keys = keys;
39
+ this.pool = pool;
40
+ }
41
+ async generateTickets(state, isEpochStart, onTickets) {
42
+ // Pick the right entropy and validator set
43
+ const validators = isEpochStart ? state.designatedValidatorData : state.nextValidatorData;
44
+ const entropy = isEpochStart ? state.entropy[1] : state.entropy[2];
45
+ const epoch = tryAsEpoch(Math.floor(state.timeslot / this.chainSpec.epochLength));
46
+ const ringKeys = validators.map((d) => d.bandersnatch);
47
+ const nextKeySet = HashSet.from(ringKeys);
48
+ const validatorKeys = this.keys.filter((k) => nextKeySet.has(k.public));
49
+ // Generate just enough validators to fill the accumulator, plus a margin.
50
+ const needed = Math.ceil(this.chainSpec.epochLength / this.chainSpec.ticketsPerValidator) + TICKET_GENERATION_VALIDATOR_MARGIN;
51
+ const selected = validatorKeys.slice(0, Math.min(validatorKeys.length, needed));
52
+ const ticketGen = measureTicketGen();
53
+ logger.info `🎫 [E${epoch}] generating tickets for ${selected.length} validators across ${this.pool.workerCount} worker threads…`;
54
+ try {
55
+ await this.pool.generate(ringKeys, selected, entropy, this.chainSpec.ticketsPerValidator, onTickets);
56
+ logger.info `🎫 [E${epoch}] ${ticketGen}`;
57
+ }
58
+ catch (e) {
59
+ logger.warn `🎫 [E${epoch}] ticket generation failed: ${e}`;
60
+ }
61
+ }
62
+ }
@@ -0,0 +1,50 @@
1
+ import type { Transferable } from "node:worker_threads";
2
+ /**
3
+ * Parameters for a single ticket-generation shard sent to a worker thread.
4
+ */
5
+ export declare class TicketGenShardParams {
6
+ /** Concatenated ring public keys (`ringSize * 32` bytes). */
7
+ readonly ringKeysData: Uint8Array;
8
+ /** Index within the ring for each validator in this shard. */
9
+ readonly proverKeyIndices: Uint32Array;
10
+ /** Concatenated validator secret seeds (`count * secretSeedDataLen` bytes). */
11
+ readonly secretSeedsData: Uint8Array;
12
+ /** Length of each secret seed in `secretSeedsData`. */
13
+ readonly secretSeedDataLen: number;
14
+ /** Concatenated VRF inputs, one per attempt. */
15
+ readonly inputsData: Uint8Array;
16
+ /** Length of each VRF input in `inputsData`. */
17
+ readonly vrfInputDataLen: number;
18
+ constructor(
19
+ /** Concatenated ring public keys (`ringSize * 32` bytes). */
20
+ ringKeysData: Uint8Array,
21
+ /** Index within the ring for each validator in this shard. */
22
+ proverKeyIndices: Uint32Array,
23
+ /** Concatenated validator secret seeds (`count * secretSeedDataLen` bytes). */
24
+ secretSeedsData: Uint8Array,
25
+ /** Length of each secret seed in `secretSeedsData`. */
26
+ secretSeedDataLen: number,
27
+ /** Concatenated VRF inputs, one per attempt. */
28
+ inputsData: Uint8Array,
29
+ /** Length of each VRF input in `inputsData`. */
30
+ vrfInputDataLen: number);
31
+ /**
32
+ * No transfers: `ringKeysData` and `inputsData` are shared across all shards,
33
+ * so transferring would detach them for the other shards.
34
+ */
35
+ getTransferList(): Transferable[];
36
+ }
37
+ /** Result of a ticket-generation shard: the raw `batchGenerateRingVrfForValidators` output. */
38
+ export declare class TicketGenShardResult {
39
+ /** Raw output: validator-major, attempt-major records of `status || signature`. */
40
+ readonly signatures: Uint8Array;
41
+ constructor(
42
+ /** Raw output: validator-major, attempt-major records of `status || signature`. */
43
+ signatures: Uint8Array);
44
+ /**
45
+ * No transfers: the native binding returns a view backed by external/WASM
46
+ * memory that cannot be detached, so transferring it throws inside the worker.
47
+ */
48
+ getTransferList(): Transferable[];
49
+ }
50
+ //# sourceMappingURL=protocol.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../../../../../../packages/workers/block-authorship/ticket-generator/protocol.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAExD;;GAEG;AACH,qBAAa,oBAAoB;IAE7B,6DAA6D;IAC7D,QAAQ,CAAC,YAAY,EAAE,UAAU;IACjC,8DAA8D;IAC9D,QAAQ,CAAC,gBAAgB,EAAE,WAAW;IACtC,+EAA+E;IAC/E,QAAQ,CAAC,eAAe,EAAE,UAAU;IACpC,uDAAuD;IACvD,QAAQ,CAAC,iBAAiB,EAAE,MAAM;IAClC,gDAAgD;IAChD,QAAQ,CAAC,UAAU,EAAE,UAAU;IAC/B,gDAAgD;IAChD,QAAQ,CAAC,eAAe,EAAE,MAAM;;IAXhC,6DAA6D;IACpD,YAAY,EAAE,UAAU;IACjC,8DAA8D;IACrD,gBAAgB,EAAE,WAAW;IACtC,+EAA+E;IACtE,eAAe,EAAE,UAAU;IACpC,uDAAuD;IAC9C,iBAAiB,EAAE,MAAM;IAClC,gDAAgD;IACvC,UAAU,EAAE,UAAU;IAC/B,gDAAgD;IACvC,eAAe,EAAE,MAAM;IAGlC;;;OAGG;IACH,eAAe,IAAI,YAAY,EAAE;CAGlC;AAED,+FAA+F;AAC/F,qBAAa,oBAAoB;IAE7B,mFAAmF;IACnF,QAAQ,CAAC,UAAU,EAAE,UAAU;;IAD/B,mFAAmF;IAC1E,UAAU,EAAE,UAAU;IAGjC;;;OAGG;IACH,eAAe,IAAI,YAAY,EAAE;CAGlC"}
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Parameters for a single ticket-generation shard sent to a worker thread.
3
+ */
4
+ export class TicketGenShardParams {
5
+ ringKeysData;
6
+ proverKeyIndices;
7
+ secretSeedsData;
8
+ secretSeedDataLen;
9
+ inputsData;
10
+ vrfInputDataLen;
11
+ constructor(
12
+ /** Concatenated ring public keys (`ringSize * 32` bytes). */
13
+ ringKeysData,
14
+ /** Index within the ring for each validator in this shard. */
15
+ proverKeyIndices,
16
+ /** Concatenated validator secret seeds (`count * secretSeedDataLen` bytes). */
17
+ secretSeedsData,
18
+ /** Length of each secret seed in `secretSeedsData`. */
19
+ secretSeedDataLen,
20
+ /** Concatenated VRF inputs, one per attempt. */
21
+ inputsData,
22
+ /** Length of each VRF input in `inputsData`. */
23
+ vrfInputDataLen) {
24
+ this.ringKeysData = ringKeysData;
25
+ this.proverKeyIndices = proverKeyIndices;
26
+ this.secretSeedsData = secretSeedsData;
27
+ this.secretSeedDataLen = secretSeedDataLen;
28
+ this.inputsData = inputsData;
29
+ this.vrfInputDataLen = vrfInputDataLen;
30
+ }
31
+ /**
32
+ * No transfers: `ringKeysData` and `inputsData` are shared across all shards,
33
+ * so transferring would detach them for the other shards.
34
+ */
35
+ getTransferList() {
36
+ return [];
37
+ }
38
+ }
39
+ /** Result of a ticket-generation shard: the raw `batchGenerateRingVrfForValidators` output. */
40
+ export class TicketGenShardResult {
41
+ signatures;
42
+ constructor(
43
+ /** Raw output: validator-major, attempt-major records of `status || signature`. */
44
+ signatures) {
45
+ this.signatures = signatures;
46
+ }
47
+ /**
48
+ * No transfers: the native binding returns a view backed by external/WASM
49
+ * memory that cannot be detached, so transferring it throws inside the worker.
50
+ */
51
+ getTransferList() {
52
+ return [];
53
+ }
54
+ }
@@ -16,6 +16,10 @@ export type ValidatorKey = {
16
16
  *
17
17
  * Each validator key produces `ticketsPerValidator` tickets using ring VRF proofs.
18
18
  * The ring keys define the anonymous set - only members can produce valid proofs.
19
+ *
20
+ * All resolved validators are generated in a single batched native call
21
+ * ({@link bandersnatchVrf.generateTickets}) which reuses the ring
22
+ * prover setup across the batch.
19
23
  */
20
24
  export declare function generateTickets(bandersnatch: BandernsatchWasm, ringKeys: BandersnatchKey[], validatorKeys: ValidatorKey[], entropy: EntropyHash, ticketsPerValidator: number): Promise<Result<SignedTicket[], TicketGeneratorError>>;
21
25
  //# sourceMappingURL=ticket-generator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ticket-generator.d.ts","sourceRoot":"","sources":["../../../../../../packages/workers/block-authorship/ticket-generator/ticket-generator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAChE,OAAO,KAAK,EAAE,eAAe,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AAGjF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yCAAyC,CAAC;AAChF,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAI1C,oBAAY,oBAAoB;IAC9B,sBAAsB,2BAA2B;IACjD,kBAAkB,uBAAuB;CAC1C;AAED,MAAM,MAAM,YAAY,GAAG;IACzB,MAAM,EAAE,sBAAsB,CAAC;IAC/B,MAAM,EAAE,eAAe,CAAC;CACzB,CAAC;AAEF;;;;;;;;;GASG;AACH,wBAAsB,eAAe,CACnC,YAAY,EAAE,gBAAgB,EAC9B,QAAQ,EAAE,eAAe,EAAE,EAC3B,aAAa,EAAE,YAAY,EAAE,EAC7B,OAAO,EAAE,WAAW,EACpB,mBAAmB,EAAE,MAAM,GAC1B,OAAO,CAAC,MAAM,CAAC,YAAY,EAAE,EAAE,oBAAoB,CAAC,CAAC,CA2CvD"}
@@ -12,25 +12,35 @@ export var TicketGeneratorError;
12
12
  *
13
13
  * Each validator key produces `ticketsPerValidator` tickets using ring VRF proofs.
14
14
  * The ring keys define the anonymous set - only members can produce valid proofs.
15
+ *
16
+ * All resolved validators are generated in a single batched native call
17
+ * ({@link bandersnatchVrf.generateTickets}) which reuses the ring
18
+ * prover setup across the batch.
15
19
  */
16
20
  export async function generateTickets(bandersnatch, ringKeys, validatorKeys, entropy, ticketsPerValidator) {
17
- const allTickets = [];
21
+ // Resolve each validator's index within the ring, skipping any that are not
22
+ // members (only ring members can produce valid proofs).
23
+ const proverKeyIndices = [];
24
+ const secrets = [];
18
25
  for (const validatorKey of validatorKeys) {
19
26
  const proverIndex = ringKeys.findIndex((k) => k.isEqualTo(validatorKey.public));
20
27
  if (proverIndex < 0) {
21
28
  logger.warn `Validator public key not found in the ring, skipping ticket generation for this key`;
22
29
  continue;
23
30
  }
24
- const ticketResult = await bandersnatchVrf.generateTickets(bandersnatch, ringKeys, proverIndex, validatorKey.secret, entropy, ticketsPerValidator);
25
- if (ticketResult.isOk) {
26
- allTickets.push(...ticketResult.ok);
27
- }
28
- else {
29
- logger.warn `Failed to generate tickets for validator, skipping`;
31
+ proverKeyIndices.push(proverIndex);
32
+ secrets.push(validatorKey.secret);
33
+ }
34
+ if (proverKeyIndices.length === 0) {
35
+ // No resolvable validators: an error if some were requested, else just empty.
36
+ if (validatorKeys.length > 0) {
37
+ return Result.error(TicketGeneratorError.TicketGenerationFailed, () => "Failed to generate tickets for all validators");
30
38
  }
39
+ return Result.ok([]);
31
40
  }
32
- if (validatorKeys.length > 0 && allTickets.length === 0) {
41
+ const result = await bandersnatchVrf.generateTickets(bandersnatch, ringKeys, proverKeyIndices, secrets, entropy, ticketsPerValidator);
42
+ if (result.isError) {
33
43
  return Result.error(TicketGeneratorError.TicketGenerationFailed, () => "Failed to generate tickets for all validators");
34
44
  }
35
- return Result.ok(allTickets);
45
+ return Result.ok(result.ok.flat());
36
46
  }
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ticket-generator.test.d.ts","sourceRoot":"","sources":["../../../../../../packages/workers/block-authorship/ticket-generator/ticket-generator.test.ts"],"names":[],"mappings":""}
@@ -21,15 +21,19 @@ function createMockValidatorKeys(count) {
21
21
  describe("Ticket Generator", () => {
22
22
  beforeEach(async () => {
23
23
  await initWasm();
24
- mock.method(bandersnatchVrf, "generateTickets", async (_bandersnatch, _ringKeys, _proverIndex, _key, _entropy, ticketsPerValidator) => {
25
- const tickets = [];
26
- for (let attempt = 0; attempt < ticketsPerValidator; attempt++) {
27
- tickets.push({
28
- attempt: tryAsTicketAttempt(attempt),
29
- signature: Bytes.zero(784).asOpaque(),
30
- });
31
- }
32
- return Result.ok(tickets);
24
+ mock.method(bandersnatchVrf, "generateTickets", async (_bandersnatch, _ringKeys, proverKeyIndices, _secrets, _entropy, ticketsPerValidator) => {
25
+ // One ticket list per resolved validator, in validator-major order.
26
+ const perValidator = proverKeyIndices.map(() => {
27
+ const tickets = [];
28
+ for (let attempt = 0; attempt < ticketsPerValidator; attempt++) {
29
+ tickets.push({
30
+ attempt: tryAsTicketAttempt(attempt),
31
+ signature: Bytes.zero(784).asOpaque(),
32
+ });
33
+ }
34
+ return tickets;
35
+ });
36
+ return Result.ok(perValidator);
33
37
  });
34
38
  });
35
39
  afterEach(() => {
@@ -0,0 +1,36 @@
1
+ import type { EntropyHash } from "#@typeberry/block";
2
+ import type { SignedTicket } from "#@typeberry/block/tickets.js";
3
+ import { type BandersnatchKey } from "#@typeberry/crypto";
4
+ import type { ValidatorKey } from "./ticket-generator.js";
5
+ /**
6
+ * A pool of worker threads that generate ring-VRF tickets in parallel.
7
+ *
8
+ * Ring-VRF proof generation is a heavy, synchronous, CPU-bound native call
9
+ * (~0.7s per validator with a large ring). Running it on the authoring thread
10
+ * blocks block production; this pool shards the work across worker threads so
11
+ * the main thread stays free and wall-clock time drops ~linearly with cores.
12
+ *
13
+ * Uses `Executor` from `@typeberry/concurrent`.
14
+ */
15
+ export declare class TicketGeneratorPool {
16
+ private readonly executor;
17
+ /** Number of worker threads in the pool. */
18
+ readonly workerCount: number;
19
+ private constructor();
20
+ /** Spawn `workerCount` worker threads (each initialises the native binding). */
21
+ static create(workerCount: number): Promise<TicketGeneratorPool>;
22
+ /** Terminate all worker threads. */
23
+ destroy(): Promise<void>;
24
+ /**
25
+ * Generate tickets for `validatorKeys`, sharded evenly across the pool.
26
+ *
27
+ * `onShardTickets` is invoked on the calling (main) thread as each shard's
28
+ * results arrive, so tickets can be pooled/distributed incrementally rather
29
+ * than waiting for the whole batch. Returns once every shard has settled.
30
+ *
31
+ * Validators whose public key is not in `ringKeys` are skipped (they cannot
32
+ * produce valid proofs).
33
+ */
34
+ generate(ringKeys: BandersnatchKey[], validatorKeys: ValidatorKey[], entropy: EntropyHash, ticketsPerValidator: number, onShardTickets: (tickets: SignedTicket[]) => Promise<void>): Promise<void>;
35
+ }
36
+ //# sourceMappingURL=worker-pool.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"worker-pool.d.ts","sourceRoot":"","sources":["../../../../../../packages/workers/block-authorship/ticket-generator/worker-pool.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAGhE,OAAO,EAAE,KAAK,eAAe,EAA0C,MAAM,mBAAmB,CAAC;AAIjG,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAiB1D;;;;;;;;;GASG;AACH,qBAAa,mBAAmB;IAE5B,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,4CAA4C;aAC5B,WAAW,EAAE,MAAM;IAHrC,OAAO;IAMP,gFAAgF;WACnE,MAAM,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAQtE,oCAAoC;IAC9B,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAI9B;;;;;;;;;OASG;IACG,QAAQ,CACZ,QAAQ,EAAE,eAAe,EAAE,EAC3B,aAAa,EAAE,YAAY,EAAE,EAC7B,OAAO,EAAE,WAAW,EACpB,mBAAmB,EAAE,MAAM,EAC3B,cAAc,EAAE,CAAC,OAAO,EAAE,YAAY,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,GACzD,OAAO,CAAC,IAAI,CAAC;CA6DjB"}
@@ -0,0 +1,111 @@
1
+ import { BytesBlob } from "#@typeberry/bytes";
2
+ import { Executor } from "#@typeberry/concurrent";
3
+ import { SEED_SIZE } from "#@typeberry/crypto";
4
+ import { Logger } from "#@typeberry/logger";
5
+ import { buildTicketVrfInputs, parseTicketsBatchOutput } from "#@typeberry/safrole/bandersnatch-vrf.js";
6
+ import { TicketGenShardParams } from "./protocol.js";
7
+ const logger = Logger.new(import.meta.filename, "tickets-pool");
8
+ /** `.mjs` bootstrap that `tsImport`s the worker entry (worker threads need it under tsx). */
9
+ const WORKER_BOOTSTRAP = new URL("./bootstrap-ticket-generator.mjs", import.meta.url);
10
+ /**
11
+ * Validators per shard.
12
+ *
13
+ * Kept small so tickets stream into the pool incrementally (and get included in
14
+ * blocks throughout the contest period) rather than arriving in one lump when
15
+ * generation finishes. The prover setup is re-done per shard, but that cost is
16
+ * tiny next to the per-proof time, so finer shards add only ~1-2% overhead.
17
+ */
18
+ const TICKET_SHARD_SIZE = 16;
19
+ /**
20
+ * A pool of worker threads that generate ring-VRF tickets in parallel.
21
+ *
22
+ * Ring-VRF proof generation is a heavy, synchronous, CPU-bound native call
23
+ * (~0.7s per validator with a large ring). Running it on the authoring thread
24
+ * blocks block production; this pool shards the work across worker threads so
25
+ * the main thread stays free and wall-clock time drops ~linearly with cores.
26
+ *
27
+ * Uses `Executor` from `@typeberry/concurrent`.
28
+ */
29
+ export class TicketGeneratorPool {
30
+ executor;
31
+ workerCount;
32
+ constructor(executor,
33
+ /** Number of worker threads in the pool. */
34
+ workerCount) {
35
+ this.executor = executor;
36
+ this.workerCount = workerCount;
37
+ }
38
+ /** Spawn `workerCount` worker threads (each initialises the native binding). */
39
+ static async create(workerCount) {
40
+ const executor = await Executor.initialize(WORKER_BOOTSTRAP, {
41
+ minWorkers: workerCount,
42
+ maxWorkers: workerCount,
43
+ });
44
+ return new TicketGeneratorPool(executor, workerCount);
45
+ }
46
+ /** Terminate all worker threads. */
47
+ async destroy() {
48
+ await this.executor.destroy();
49
+ }
50
+ /**
51
+ * Generate tickets for `validatorKeys`, sharded evenly across the pool.
52
+ *
53
+ * `onShardTickets` is invoked on the calling (main) thread as each shard's
54
+ * results arrive, so tickets can be pooled/distributed incrementally rather
55
+ * than waiting for the whole batch. Returns once every shard has settled.
56
+ *
57
+ * Validators whose public key is not in `ringKeys` are skipped (they cannot
58
+ * produce valid proofs).
59
+ */
60
+ async generate(ringKeys, validatorKeys, entropy, ticketsPerValidator, onShardTickets) {
61
+ // Resolve each validator's index within the ring (skip non-members).
62
+ const keyToIndex = new Map();
63
+ for (let i = 0; i < ringKeys.length; i++) {
64
+ keyToIndex.set(ringKeys[i].toString(), i);
65
+ }
66
+ const resolved = [];
67
+ for (const vk of validatorKeys) {
68
+ const idx = keyToIndex.get(vk.public.toString());
69
+ if (idx === undefined) {
70
+ continue;
71
+ }
72
+ resolved.push({ index: idx, secret: vk.secret });
73
+ }
74
+ if (resolved.length === 0) {
75
+ return;
76
+ }
77
+ const { inputsData, vrfInputDataLen } = buildTicketVrfInputs(entropy, ticketsPerValidator);
78
+ const ringKeysData = BytesBlob.blobFromParts(ringKeys.map((k) => k.raw)).raw;
79
+ const runShard = (shard) => {
80
+ const indices = Uint32Array.from(shard.map((r) => r.index));
81
+ const secretSeedsData = BytesBlob.blobFromParts(shard.map((r) => r.secret.raw)).raw;
82
+ const params = new TicketGenShardParams(ringKeysData, indices, secretSeedsData, SEED_SIZE, inputsData, vrfInputDataLen);
83
+ return this.executor
84
+ .run(params)
85
+ .then((result) => {
86
+ const parsed = parseTicketsBatchOutput(result.signatures, indices.length, ticketsPerValidator);
87
+ if (parsed.isError) {
88
+ logger.warn `A ticket-generation shard returned an invalid proof: ${parsed.error}`;
89
+ return;
90
+ }
91
+ return onShardTickets(parsed.ok.flat());
92
+ })
93
+ .catch((e) => {
94
+ logger.warn `A ticket-generation shard failed: ${e}`;
95
+ });
96
+ };
97
+ // Dispatch small shards in waves of `workerCount` so every worker stays busy
98
+ // and tickets are delivered incrementally (one wave at a time) without
99
+ // over-queuing the executor.
100
+ const shardSize = Math.min(resolved.length, TICKET_SHARD_SIZE);
101
+ const waveSize = shardSize * this.workerCount;
102
+ for (let waveStart = 0; waveStart < resolved.length; waveStart += waveSize) {
103
+ const wave = [];
104
+ const waveEnd = Math.min(waveStart + waveSize, resolved.length);
105
+ for (let start = waveStart; start < waveEnd; start += shardSize) {
106
+ wave.push(runShard(resolved.slice(start, start + shardSize)));
107
+ }
108
+ await Promise.all(wave);
109
+ }
110
+ }
111
+ }
@@ -0,0 +1,31 @@
1
+ import type { Epoch } from "#@typeberry/block";
2
+ import type { SignedTicket } from "#@typeberry/block/tickets.js";
3
+ import type { ChainSpec } from "#@typeberry/config";
4
+ import type { BandernsatchWasm } from "#@typeberry/safrole/bandersnatch-wasm.js";
5
+ import type { State } from "#@typeberry/state";
6
+ import { type TicketValidator, ValidationError, type VerifiedTicket } from "#@typeberry/ticket-pool";
7
+ import { Result } from "#@typeberry/utils";
8
+ /**
9
+ * {@link TicketValidator} implementation that verifies a ticket against the ring
10
+ * commitment and current epoch entropy using bandersnatch.
11
+ *
12
+ * `getState` is a thunk because state advances continuously while validation is in
13
+ * flight; we want the latest available state for each call.
14
+ */
15
+ export declare class BandersnatchTicketValidator implements TicketValidator {
16
+ private readonly chainSpec;
17
+ private readonly bandersnatch;
18
+ private readonly getState;
19
+ static new(chainSpec: ChainSpec, bandersnatch: BandernsatchWasm, getState: () => State): BandersnatchTicketValidator;
20
+ private constructor();
21
+ validate(epochIndex: Epoch, inTickets: SignedTicket[]): Promise<Result<VerifiedTicket[], ValidationError>>;
22
+ /**
23
+ * Returns the correct tickets entropy for verification given the current state.
24
+ *
25
+ * When `state` is from epoch E-1 (i.e. we haven't produced epoch E's first block yet),
26
+ * the ticket entropy for epoch E is at index 1 (not yet shifted). After the epoch
27
+ * transition it moves to index 2.
28
+ */
29
+ private getTicketEntropy;
30
+ }
31
+ //# sourceMappingURL=ticket-validator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ticket-validator.d.ts","sourceRoot":"","sources":["../../../../../packages/workers/block-authorship/ticket-validator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAe,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAC3D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAChE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAEnD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yCAAyC,CAAC;AAChF,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,KAAK,eAAe,EAAE,eAAe,EAAE,KAAK,cAAc,EAAE,MAAM,wBAAwB,CAAC;AACpG,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAE1C;;;;;;GAMG;AACH,qBAAa,2BAA4B,YAAW,eAAe;IAM/D,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,YAAY;IAC7B,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAP3B,MAAM,CAAC,GAAG,CAAC,SAAS,EAAE,SAAS,EAAE,YAAY,EAAE,gBAAgB,EAAE,QAAQ,EAAE,MAAM,KAAK;IAItF,OAAO;IAMD,QAAQ,CAAC,UAAU,EAAE,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,cAAc,EAAE,EAAE,eAAe,CAAC,CAAC;IAwChH;;;;;;OAMG;IACH,OAAO,CAAC,gBAAgB;CAIzB"}
@@ -0,0 +1,59 @@
1
+ import bandersnatchVrf from "#@typeberry/safrole/bandersnatch-vrf.js";
2
+ import { ValidationError } from "#@typeberry/ticket-pool";
3
+ import { Result } from "#@typeberry/utils";
4
+ /**
5
+ * {@link TicketValidator} implementation that verifies a ticket against the ring
6
+ * commitment and current epoch entropy using bandersnatch.
7
+ *
8
+ * `getState` is a thunk because state advances continuously while validation is in
9
+ * flight; we want the latest available state for each call.
10
+ */
11
+ export class BandersnatchTicketValidator {
12
+ chainSpec;
13
+ bandersnatch;
14
+ getState;
15
+ static new(chainSpec, bandersnatch, getState) {
16
+ return new BandersnatchTicketValidator(chainSpec, bandersnatch, getState);
17
+ }
18
+ constructor(chainSpec, bandersnatch, getState) {
19
+ this.chainSpec = chainSpec;
20
+ this.bandersnatch = bandersnatch;
21
+ this.getState = getState;
22
+ }
23
+ async validate(epochIndex, inTickets) {
24
+ const state = this.getState();
25
+ // Reject obviously invalid epochs up front: we only have the entropy to verify
26
+ // the current epoch (tickets being submitted now) and the next one (tickets at
27
+ // an epoch boundary). Anything else would only fail after the expensive proof
28
+ // check, so a peer could otherwise burn CPU with far-past/future batches.
29
+ const stateEpoch = Math.floor(state.timeslot / this.chainSpec.epochLength);
30
+ if (epochIndex < stateEpoch || epochIndex > stateEpoch + 1) {
31
+ return Result.error(ValidationError.InvalidProof, () => `ticket batch targets an invalid epoch ${epochIndex}`);
32
+ }
33
+ const entropy = this.getTicketEntropy(epochIndex, state);
34
+ // Batch verifier: a single `isValid` covers the whole batch
35
+ // and `tickets` holds the computed id per input ticket.
36
+ const { isValid, tickets } = await bandersnatchVrf.verifyTickets(this.bandersnatch, state.designatedValidatorData.length, state.epochRoot, inTickets, entropy);
37
+ if (tickets.length !== inTickets.length) {
38
+ return Result.error(ValidationError.ValidatorUnavailable, () => `io size mismatch got: ${tickets.length}, expected ${inTickets.length}`);
39
+ }
40
+ if (!isValid) {
41
+ return Result.error(ValidationError.InvalidProof, () => "bandersnatch proof rejected");
42
+ }
43
+ return Result.ok(inTickets.map((ticket, index) => {
44
+ const id = tickets[index];
45
+ return { ticket, id };
46
+ }));
47
+ }
48
+ /**
49
+ * Returns the correct tickets entropy for verification given the current state.
50
+ *
51
+ * When `state` is from epoch E-1 (i.e. we haven't produced epoch E's first block yet),
52
+ * the ticket entropy for epoch E is at index 1 (not yet shifted). After the epoch
53
+ * transition it moves to index 2.
54
+ */
55
+ getTicketEntropy(epochIndex, state) {
56
+ const stateEpoch = Math.floor(state.timeslot / this.chainSpec.epochLength);
57
+ return epochIndex > stateEpoch ? state.entropy[1] : state.entropy[2];
58
+ }
59
+ }
@@ -1,5 +1,5 @@
1
1
  import { type Api, type Internal } from "#@typeberry/workers-api";
2
- import { ReceivedTicketMessage, TicketsMessage } from "./tickets-message.js";
2
+ import { TicketsMessage } from "./tickets-message.js";
3
3
  /**
4
4
  * Port name for authorship-network direct communication.
5
5
  * Used when spawning jam-network worker to pass the port for receiving tickets.
@@ -21,14 +21,24 @@ export declare const protocol: import("@typeberry/workers-api").LousyProtocol<{
21
21
  }>>;
22
22
  response: import("@typeberry/codec").Descriptor<void, void>;
23
23
  };
24
+ replaceTicketPool: {
25
+ request: import("@typeberry/codec").Descriptor<TicketsMessage, import("@typeberry/codec").ViewOf<TicketsMessage, {
26
+ epochIndex: import("@typeberry/codec").Descriptor<number & import("@typeberry/numbers").WithBytesRepresentation<4> & import("@typeberry/utils").WithOpaque<"Epoch">, import("@typeberry/bytes").Bytes<4>>;
27
+ tickets: import("@typeberry/codec").Descriptor<import("@typeberry/block").SignedTicket[], import("@typeberry/codec").SequenceView<import("@typeberry/block").SignedTicket, import("@typeberry/codec").ViewOf<import("@typeberry/block").SignedTicket, {
28
+ attempt: import("@typeberry/codec").Descriptor<number & import("@typeberry/numbers").WithBytesRepresentation<1> & import("@typeberry/utils").WithOpaque<"TicketAttempt[u8]">, import("@typeberry/numbers").U32>;
29
+ signature: import("@typeberry/codec").Descriptor<import("@typeberry/bytes").Bytes<784> & import("@typeberry/utils").WithOpaque<"BandersnatchRingSignature">, import("@typeberry/bytes").Bytes<784>>;
30
+ }>>>;
31
+ }>>;
32
+ response: import("@typeberry/codec").Descriptor<void, void>;
33
+ };
24
34
  }, {
25
35
  receivedTickets: {
26
- request: import("@typeberry/codec").Descriptor<ReceivedTicketMessage, import("@typeberry/codec").ViewOf<ReceivedTicketMessage, {
36
+ request: import("@typeberry/codec").Descriptor<TicketsMessage, import("@typeberry/codec").ViewOf<TicketsMessage, {
27
37
  epochIndex: import("@typeberry/codec").Descriptor<number & import("@typeberry/numbers").WithBytesRepresentation<4> & import("@typeberry/utils").WithOpaque<"Epoch">, import("@typeberry/bytes").Bytes<4>>;
28
- ticket: import("@typeberry/codec").Descriptor<import("@typeberry/block").SignedTicket, import("@typeberry/codec").ViewOf<import("@typeberry/block").SignedTicket, {
38
+ tickets: import("@typeberry/codec").Descriptor<import("@typeberry/block").SignedTicket[], import("@typeberry/codec").SequenceView<import("@typeberry/block").SignedTicket, import("@typeberry/codec").ViewOf<import("@typeberry/block").SignedTicket, {
29
39
  attempt: import("@typeberry/codec").Descriptor<number & import("@typeberry/numbers").WithBytesRepresentation<1> & import("@typeberry/utils").WithOpaque<"TicketAttempt[u8]">, import("@typeberry/numbers").U32>;
30
40
  signature: import("@typeberry/codec").Descriptor<import("@typeberry/bytes").Bytes<784> & import("@typeberry/utils").WithOpaque<"BandersnatchRingSignature">, import("@typeberry/bytes").Bytes<784>>;
31
- }>>;
41
+ }>>>;
32
42
  }>>;
33
43
  response: import("@typeberry/codec").Descriptor<boolean, boolean>;
34
44
  };
@@ -1 +1 @@
1
- {"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../../../../../packages/workers/comms-authorship-network/protocol.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,GAAG,EAAkB,KAAK,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AACjF,OAAO,EAAE,qBAAqB,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAE7E;;;GAGG;AACH,eAAO,MAAM,uBAAuB,uBAAuB,CAAC;AAE5D;;;;GAIG;AACH,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;;;EAiBnB,CAAC;AAEH,MAAM,MAAM,eAAe,GAAG,GAAG,CAAC,OAAO,QAAQ,CAAC,CAAC;AACnD,MAAM,MAAM,eAAe,GAAG,QAAQ,CAAC,OAAO,QAAQ,CAAC,CAAC"}
1
+ {"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../../../../../packages/workers/comms-authorship-network/protocol.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,GAAG,EAAkB,KAAK,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AACjF,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEtD;;;GAGG;AACH,eAAO,MAAM,uBAAuB,uBAAuB,CAAC;AAE5D;;;;GAIG;AACH,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAuBnB,CAAC;AAEH,MAAM,MAAM,eAAe,GAAG,GAAG,CAAC,OAAO,QAAQ,CAAC,CAAC;AACnD,MAAM,MAAM,eAAe,GAAG,QAAQ,CAAC,OAAO,QAAQ,CAAC,CAAC"}
@@ -1,6 +1,6 @@
1
1
  import { codec } from "#@typeberry/codec";
2
2
  import { createProtocol } from "#@typeberry/workers-api";
3
- import { ReceivedTicketMessage, TicketsMessage } from "./tickets-message.js";
3
+ import { TicketsMessage } from "./tickets-message.js";
4
4
  /**
5
5
  * Port name for authorship-network direct communication.
6
6
  * Used when spawning jam-network worker to pass the port for receiving tickets.
@@ -12,19 +12,25 @@ export const AUTHORSHIP_NETWORK_PORT = "authorship-network";
12
12
  * This bypasses the main thread for ticket distribution, reducing latency.
13
13
  */
14
14
  export const protocol = createProtocol("authorship-network", {
15
- // Messages from block-authorship to jam-network
15
+ // Messages from block-authorship to jam-network.
16
16
  toWorker: {
17
+ // Newly generated own tickets; networking should add them to its redistribution pool.
17
18
  tickets: {
18
19
  request: TicketsMessage.Codec,
19
20
  response: codec.nothing,
20
21
  },
22
+ // Authoritative pool snapshot for the given epoch; networking replaces its local
23
+ // pool with these tickets (one-way, source of truth lives in block-authorship).
24
+ replaceTicketPool: {
25
+ request: TicketsMessage.Codec,
26
+ response: codec.nothing,
27
+ },
21
28
  },
22
- // Messages from jam-network to block-authorship (one ticket per relay).
23
- // Response indicates whether the ticket passed validation used by jam-network
24
- // to decide whether to redistribute the ticket to other peers.
29
+ // Messages from jam-network to block-authorship
30
+ // Response indicates whether all tickets in batch were valid (no per-ticket validity!)
25
31
  fromWorker: {
26
32
  receivedTickets: {
27
- request: ReceivedTicketMessage.Codec,
33
+ request: TicketsMessage.Codec,
28
34
  response: codec.bool,
29
35
  },
30
36
  },
@@ -15,18 +15,4 @@ export declare class TicketsMessage extends WithDebug {
15
15
  static create({ epochIndex, tickets }: CodecRecord<TicketsMessage>): TicketsMessage;
16
16
  private constructor();
17
17
  }
18
- /** Single-ticket message sent from jam-network to block-authorship (one ticket per peer relay). */
19
- export declare class ReceivedTicketMessage extends WithDebug {
20
- readonly epochIndex: Epoch;
21
- readonly ticket: SignedTicket;
22
- static Codec: import("@typeberry/codec").Descriptor<ReceivedTicketMessage, import("@typeberry/codec").ViewOf<ReceivedTicketMessage, {
23
- epochIndex: import("@typeberry/codec").Descriptor<number & import("@typeberry/numbers").WithBytesRepresentation<4> & import("@typeberry/utils").WithOpaque<"Epoch">, import("@typeberry/bytes").Bytes<4>>;
24
- ticket: import("@typeberry/codec").Descriptor<SignedTicket, import("@typeberry/codec").ViewOf<SignedTicket, {
25
- attempt: import("@typeberry/codec").Descriptor<number & import("@typeberry/numbers").WithBytesRepresentation<1> & import("@typeberry/utils").WithOpaque<"TicketAttempt[u8]">, import("@typeberry/numbers").U32>;
26
- signature: import("@typeberry/codec").Descriptor<import("@typeberry/bytes").Bytes<784> & import("@typeberry/utils").WithOpaque<"BandersnatchRingSignature">, import("@typeberry/bytes").Bytes<784>>;
27
- }>>;
28
- }>>;
29
- static create({ epochIndex, ticket }: CodecRecord<ReceivedTicketMessage>): ReceivedTicketMessage;
30
- private constructor();
31
- }
32
18
  //# sourceMappingURL=tickets-message.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"tickets-message.d.ts","sourceRoot":"","sources":["../../../../../packages/workers/comms-authorship-network/tickets-message.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAC3D,OAAO,EAAE,KAAK,WAAW,EAAS,MAAM,kBAAkB,CAAC;AAC3D,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAE7C,qBAAa,cAAe,SAAQ,SAAS;aAWzB,UAAU,EAAE,KAAK;aACjB,OAAO,EAAE,YAAY,EAAE;IAXzC,MAAM,CAAC,KAAK;;;;;;QAGT;IAEH,MAAM,CAAC,MAAM,CAAC,EAAE,UAAU,EAAE,OAAO,EAAE,EAAE,WAAW,CAAC,cAAc,CAAC;IAIlE,OAAO;CAMR;AAED,mGAAmG;AACnG,qBAAa,qBAAsB,SAAQ,SAAS;aAWhC,UAAU,EAAE,KAAK;aACjB,MAAM,EAAE,YAAY;IAXtC,MAAM,CAAC,KAAK;;;;;;QAGT;IAEH,MAAM,CAAC,MAAM,CAAC,EAAE,UAAU,EAAE,MAAM,EAAE,EAAE,WAAW,CAAC,qBAAqB,CAAC;IAIxE,OAAO;CAMR"}
1
+ {"version":3,"file":"tickets-message.d.ts","sourceRoot":"","sources":["../../../../../packages/workers/comms-authorship-network/tickets-message.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAC3D,OAAO,EAAE,KAAK,WAAW,EAAS,MAAM,kBAAkB,CAAC;AAC3D,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAE7C,qBAAa,cAAe,SAAQ,SAAS;aAWzB,UAAU,EAAE,KAAK;aACjB,OAAO,EAAE,YAAY,EAAE;IAXzC,MAAM,CAAC,KAAK;;;;;;QAGT;IAEH,MAAM,CAAC,MAAM,CAAC,EAAE,UAAU,EAAE,OAAO,EAAE,EAAE,WAAW,CAAC,cAAc,CAAC;IAIlE,OAAO;CAMR"}