@typeberry/lib 0.8.4-9502983 → 0.8.4-faebc7a

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 (63) hide show
  1. package/package.json +6 -4
  2. package/packages/jam/database-fjall/hybrid-states.d.ts +45 -0
  3. package/packages/jam/database-fjall/hybrid-states.d.ts.map +1 -0
  4. package/packages/jam/database-fjall/hybrid-states.js +113 -0
  5. package/packages/jam/database-fjall/hybrid-states.test.d.ts +2 -0
  6. package/packages/jam/database-fjall/hybrid-states.test.d.ts.map +1 -0
  7. package/packages/jam/database-fjall/hybrid-states.test.js +83 -0
  8. package/packages/jam/database-fjall/index.d.ts +3 -0
  9. package/packages/jam/database-fjall/index.d.ts.map +1 -0
  10. package/packages/jam/database-fjall/index.js +2 -0
  11. package/packages/jam/database-fjall/root.d.ts +52 -0
  12. package/packages/jam/database-fjall/root.d.ts.map +1 -0
  13. package/packages/jam/database-fjall/root.js +85 -0
  14. package/packages/jam/jamnp-s/tasks/ticket-distribution.d.ts +18 -10
  15. package/packages/jam/jamnp-s/tasks/ticket-distribution.d.ts.map +1 -1
  16. package/packages/jam/jamnp-s/tasks/ticket-distribution.js +44 -68
  17. package/packages/jam/jamnp-s/tasks/ticket-distribution.test.js +30 -8
  18. package/packages/jam/node/main-fuzz.d.ts.map +1 -1
  19. package/packages/jam/node/main-fuzz.js +16 -1
  20. package/packages/jam/node/main-importer.d.ts +6 -3
  21. package/packages/jam/node/main-importer.d.ts.map +1 -1
  22. package/packages/jam/node/main-importer.js +3 -2
  23. package/packages/jam/safrole/bandersnatch-vrf.d.ts +2 -2
  24. package/packages/jam/safrole/bandersnatch-vrf.d.ts.map +1 -1
  25. package/packages/jam/safrole/bandersnatch-vrf.js +9 -6
  26. package/packages/jam/safrole/bandersnatch-vrf.test.js +9 -7
  27. package/packages/jam/safrole/safrole.js +5 -5
  28. package/packages/jam/safrole/safrole.test.js +13 -13
  29. package/packages/jam/ticket-pool/index.d.ts +4 -0
  30. package/packages/jam/ticket-pool/index.d.ts.map +1 -0
  31. package/packages/jam/ticket-pool/index.js +3 -0
  32. package/packages/jam/ticket-pool/pending-ticket-pool.d.ts +30 -0
  33. package/packages/jam/ticket-pool/pending-ticket-pool.d.ts.map +1 -0
  34. package/packages/jam/ticket-pool/pending-ticket-pool.js +56 -0
  35. package/packages/jam/ticket-pool/pending-ticket-pool.test.d.ts +2 -0
  36. package/packages/jam/ticket-pool/pending-ticket-pool.test.d.ts.map +1 -0
  37. package/packages/jam/ticket-pool/pending-ticket-pool.test.js +67 -0
  38. package/packages/jam/ticket-pool/ticket-validator.d.ts +46 -0
  39. package/packages/jam/ticket-pool/ticket-validator.d.ts.map +1 -0
  40. package/packages/jam/ticket-pool/ticket-validator.js +29 -0
  41. package/packages/jam/ticket-pool/ticket-validator.test.d.ts +2 -0
  42. package/packages/jam/ticket-pool/ticket-validator.test.d.ts.map +1 -0
  43. package/packages/jam/ticket-pool/ticket-validator.test.js +34 -0
  44. package/packages/jam/ticket-pool/verified-ticket-pool.d.ts +24 -0
  45. package/packages/jam/ticket-pool/verified-ticket-pool.d.ts.map +1 -0
  46. package/packages/jam/ticket-pool/verified-ticket-pool.js +37 -0
  47. package/packages/jam/ticket-pool/verified-ticket-pool.test.d.ts +2 -0
  48. package/packages/jam/ticket-pool/verified-ticket-pool.test.d.ts.map +1 -0
  49. package/packages/jam/ticket-pool/verified-ticket-pool.test.js +54 -0
  50. package/packages/workers/api-node/config.d.ts +12 -5
  51. package/packages/workers/api-node/config.d.ts.map +1 -1
  52. package/packages/workers/api-node/config.js +20 -17
  53. package/packages/workers/api-node/config.test.js +38 -1
  54. package/packages/workers/block-authorship/main.d.ts.map +1 -1
  55. package/packages/workers/block-authorship/main.js +22 -72
  56. package/packages/workers/block-authorship/ticket-validator.d.ts +32 -0
  57. package/packages/workers/block-authorship/ticket-validator.d.ts.map +1 -0
  58. package/packages/workers/block-authorship/ticket-validator.js +56 -0
  59. package/packages/workers/comms-authorship-network/protocol.d.ts +10 -0
  60. package/packages/workers/comms-authorship-network/protocol.d.ts.map +1 -1
  61. package/packages/workers/comms-authorship-network/protocol.js +8 -1
  62. package/packages/workers/jam-network/main.d.ts.map +1 -1
  63. package/packages/workers/jam-network/main.js +20 -4
@@ -13,9 +13,11 @@ import bandersnatchVrf from "#@typeberry/safrole/bandersnatch-vrf.js";
13
13
  import { BandernsatchWasm } from "#@typeberry/safrole/bandersnatch-wasm.js";
14
14
  import { JAM_FALLBACK_SEAL, JAM_TICKET_SEAL } from "#@typeberry/safrole/constants.js";
15
15
  import { SafroleSealingKeysKind } from "#@typeberry/state";
16
+ import { VerifiedTicketPool } from "#@typeberry/ticket-pool";
16
17
  import { asOpaqueType, Result } from "#@typeberry/utils";
17
18
  import { Generator } from "./generator.js";
18
19
  import { generateTickets } from "./ticket-generator.js";
20
+ import { BandersnatchTicketValidator } from "./ticket-validator.js";
19
21
  const logger = Logger.new(import.meta.filename, "author");
20
22
  export async function main(config, comms, networkingComms) {
21
23
  await initWasm();
@@ -181,78 +183,16 @@ export async function main(config, comms, networkingComms) {
181
183
  }
182
184
  return Result.ok(state.sealingKeySeries);
183
185
  }
184
- // Ticket pool: epochIndex -> {ticket, id}[]
185
- // IDs (entropyHash) are computed at receipt time via verifyTickets(), enabling O(1) dedup by ID.
186
- const ticketPool = new Map();
187
- const ticketIdSets = new Map();
188
- /**
189
- * Adds pre-verified tickets to the in-memory ticket pool for the given epoch.
190
- *
191
- * Clears the pool when the epoch changes (we only ever need tickets for one epoch at a time).
192
- * Deduplicates by ticket ID using a HashSet for O(1) lookup — prevents double-counting
193
- * tickets received from multiple peers or via both CE-131 and CE-132 paths.
194
- */
195
- function addToPool(epochIndex, verifiedTickets) {
196
- if (ticketPool.size > 0 && !ticketPool.has(epochIndex)) {
197
- ticketPool.clear();
198
- ticketIdSets.clear();
199
- }
200
- const existing = ticketPool.get(epochIndex) ?? [];
201
- let idSet = ticketIdSets.get(epochIndex) ?? null;
202
- if (idSet === null) {
203
- idSet = HashSet.new();
204
- ticketIdSets.set(epochIndex, idSet);
205
- }
206
- for (const entry of verifiedTickets) {
207
- if (!idSet.has(entry.id)) {
208
- existing.push(entry);
209
- idSet.insert(entry.id);
210
- }
211
- }
212
- ticketPool.set(epochIndex, existing);
213
- }
214
- /**
215
- * Returns the correct tickets entropy for verification given the current state.
216
- *
217
- * When `state` is from epoch E-1 (i.e. we haven't produced epoch E's first block yet),
218
- * the ticket entropy for epoch E is at index 1 (not yet shifted).
219
- * After the epoch transition it moves to index 2.
220
- */
221
- function getTicketEntropy(epochIndex, state) {
222
- const stateEpoch = Math.floor(state.timeslot / chainSpec.epochLength);
223
- return epochIndex > stateEpoch ? state.entropy[1] : state.entropy[2];
224
- }
225
- /**
226
- * Verifies tickets against the ring commitment and current epoch entropy, then adds valid
227
- * ones to the pool with their computed IDs.
228
- *
229
- * Called both for own generated tickets and for tickets relayed from peers.
230
- * Verification computes the ticket ID (entropyHash) which is then used for
231
- * deduplication in the pool and later when building the extrinsic.
232
- */
233
- async function verifyAndAddToPool(epochIndex, tickets, state) {
234
- const results = await bandersnatchVrf.verifyTickets(bandersnatch, state.designatedValidatorData.length, state.epochRoot, tickets, getTicketEntropy(epochIndex, state));
235
- if (results.length !== tickets.length) {
236
- logger.error `verifyTickets returned ${results.length} results for ${tickets.length} tickets`;
237
- return false;
238
- }
239
- const verified = tickets
240
- .map((ticket, i) => ({ ticket, id: results[i].entropyHash }))
241
- .filter((_, i) => results[i].isValid);
242
- addToPool(epochIndex, verified);
243
- return verified.length > 0;
244
- }
186
+ // Verified tickets for the current epoch, keyed by entropy hash (ticket id).
187
+ // Tickets enter via `validator.validate(...)` which both verifies and inserts.
188
+ const verifiedPool = new VerifiedTicketPool();
189
+ const ticketValidator = new BandersnatchTicketValidator(bandersnatch, chainSpec, verifiedPool, () => states.getState(blocks.getBestHeaderHash()));
245
190
  // Receive a single ticket from peers (via jam-network worker).
246
191
  // Returns true if the ticket passed validation so jam-network can decide whether to redistribute it.
247
192
  networkingComms.setOnReceivedTickets(async ({ epochIndex, ticket }) => {
248
193
  logger.log `Received ticket from peer for epoch ${epochIndex}`;
249
- const hash = blocks.getBestHeaderHash();
250
- const state = states.getState(hash);
251
- if (state === null) {
252
- logger.warn `Cannot verify received ticket: no state available`;
253
- return false;
254
- }
255
- return await verifyAndAddToPool(epochIndex, [ticket], state);
194
+ const result = await ticketValidator.validate(epochIndex, ticket);
195
+ return result.isOk;
256
196
  });
257
197
  const isFastForward = config.workerParams.isFastForward;
258
198
  let lastGeneratedSlot = startTimeSlot;
@@ -301,8 +241,10 @@ export async function main(config, comms, networkingComms) {
301
241
  }
302
242
  else {
303
243
  logger.log `Generated ${ticketsResult.ok.length} tickets for epoch ${epoch}. Distributing...`;
304
- // Verify own tickets to get IDs, then add to pool
305
- await verifyAndAddToPool(epoch, ticketsResult.ok, state);
244
+ // Verify own tickets (validator stores them in the pool with computed ids).
245
+ for (const ticket of ticketsResult.ok) {
246
+ await ticketValidator.validate(epoch, ticket);
247
+ }
306
248
  // Send directly to network worker (bypasses main thread)
307
249
  await networkingComms.sendTickets({ epochIndex: epoch, tickets: ticketsResult.ok });
308
250
  }
@@ -328,6 +270,12 @@ export async function main(config, comms, networkingComms) {
328
270
  }
329
271
  await buildTicketAuthorshipCache(selingKeySeriesResult.ok, entropy);
330
272
  }
273
+ // On every epoch boundary, push the authoritative ticket pool to networking so it
274
+ // can replace its redistribution set; this keeps the two sides from drifting.
275
+ if (isNewEpoch) {
276
+ const dumpTickets = verifiedPool.getForEpoch(epoch).map((entry) => entry.ticket);
277
+ await networkingComms.sendReplaceTicketPool({ epochIndex: epoch, tickets: dumpTickets });
278
+ }
331
279
  const sealData = getSealData(selingKeySeriesResult.ok, keys, timeSlot, entropy);
332
280
  if (sealData !== null && currentValidatorData !== null) {
333
281
  const { key, sealPayload } = sealData;
@@ -336,8 +284,10 @@ export async function main(config, comms, networkingComms) {
336
284
  continue;
337
285
  }
338
286
  logger.log `Attempting to create a block using ${sealData.logId} located at validator index ${validatorIndex}.`;
339
- const currentEpochTickets = ticketPool.get(epoch) ?? [];
340
- const newBlock = await generator.nextBlockView(validatorIndex, key.bandersnatchSecret, sealPayload, timeSlot, currentEpochTickets);
287
+ const currentEpochTickets = verifiedPool.getForEpoch(epoch);
288
+ const newBlock = await generator.nextBlockView(validatorIndex, key.bandersnatchSecret, sealPayload, timeSlot,
289
+ // VerifiedTicket has the same `{ ticket, id }` shape the generator expects.
290
+ [...currentEpochTickets]);
341
291
  counter += 1;
342
292
  lastGeneratedSlot = timeSlot;
343
293
  logger.trace `Sending block ${counter}`;
@@ -0,0 +1,32 @@
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, type ValidatedTicket, ValidationError, type VerifiedTicketPool } from "#@typeberry/ticket-pool";
7
+ import { Result } from "#@typeberry/utils";
8
+ /**
9
+ * Real {@link TicketValidator} implementation that verifies a ticket against the ring
10
+ * commitment and current epoch entropy using bandersnatch, then stores the verified
11
+ * ticket (with its computed id) into the supplied {@link VerifiedTicketPool}.
12
+ *
13
+ * `getState` is a thunk because state advances continuously while validation is in
14
+ * flight; we want the latest available state for each call.
15
+ */
16
+ export declare class BandersnatchTicketValidator implements TicketValidator {
17
+ private readonly bandersnatch;
18
+ private readonly chainSpec;
19
+ private readonly pool;
20
+ private readonly getState;
21
+ constructor(bandersnatch: BandernsatchWasm, chainSpec: ChainSpec, pool: VerifiedTicketPool, getState: () => State | null);
22
+ validate(epochIndex: Epoch, ticket: SignedTicket): Promise<Result<ValidatedTicket, ValidationError>>;
23
+ /**
24
+ * Returns the correct tickets entropy for verification given the current state.
25
+ *
26
+ * When `state` is from epoch E-1 (i.e. we haven't produced epoch E's first block yet),
27
+ * the ticket entropy for epoch E is at index 1 (not yet shifted). After the epoch
28
+ * transition it moves to index 2.
29
+ */
30
+ private getTicketEntropy;
31
+ }
32
+ //# 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;AAGnD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yCAAyC,CAAC;AAChF,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EACL,KAAK,eAAe,EACpB,KAAK,eAAe,EACpB,eAAe,EAEf,KAAK,kBAAkB,EACxB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAI1C;;;;;;;GAOG;AACH,qBAAa,2BAA4B,YAAW,eAAe;IAE/D,OAAO,CAAC,QAAQ,CAAC,YAAY;IAC7B,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,QAAQ;gBAHR,YAAY,EAAE,gBAAgB,EAC9B,SAAS,EAAE,SAAS,EACpB,IAAI,EAAE,kBAAkB,EACxB,QAAQ,EAAE,MAAM,KAAK,GAAG,IAAI;IAGzC,QAAQ,CAAC,UAAU,EAAE,KAAK,EAAE,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,eAAe,EAAE,eAAe,CAAC,CAAC;IA+B1G;;;;;;OAMG;IACH,OAAO,CAAC,gBAAgB;CAIzB"}
@@ -0,0 +1,56 @@
1
+ import { Logger } from "#@typeberry/logger";
2
+ import bandersnatchVrf from "#@typeberry/safrole/bandersnatch-vrf.js";
3
+ import { ValidationError, } from "#@typeberry/ticket-pool";
4
+ import { Result } from "#@typeberry/utils";
5
+ const logger = Logger.new(import.meta.filename, "ticket-validator");
6
+ /**
7
+ * Real {@link TicketValidator} implementation that verifies a ticket against the ring
8
+ * commitment and current epoch entropy using bandersnatch, then stores the verified
9
+ * ticket (with its computed id) into the supplied {@link VerifiedTicketPool}.
10
+ *
11
+ * `getState` is a thunk because state advances continuously while validation is in
12
+ * flight; we want the latest available state for each call.
13
+ */
14
+ export class BandersnatchTicketValidator {
15
+ bandersnatch;
16
+ chainSpec;
17
+ pool;
18
+ getState;
19
+ constructor(bandersnatch, chainSpec, pool, getState) {
20
+ this.bandersnatch = bandersnatch;
21
+ this.chainSpec = chainSpec;
22
+ this.pool = pool;
23
+ this.getState = getState;
24
+ }
25
+ async validate(epochIndex, ticket) {
26
+ const state = this.getState();
27
+ if (state === null) {
28
+ return Result.error(ValidationError.ValidatorUnavailable, () => "no state available");
29
+ }
30
+ const entropy = this.getTicketEntropy(epochIndex, state);
31
+ // Batch verifier: a single `isValid` covers the whole batch and `tickets` holds the
32
+ // computed id per input ticket. We only ever pass one ticket here.
33
+ const { isValid, tickets } = await bandersnatchVrf.verifyTickets(this.bandersnatch, state.designatedValidatorData.length, state.epochRoot, [ticket], entropy);
34
+ if (tickets.length !== 1) {
35
+ logger.error `verifyTickets returned ${tickets.length} results for 1 ticket`;
36
+ return Result.error(ValidationError.ValidatorUnavailable, () => "verifier returned unexpected result count");
37
+ }
38
+ if (!isValid) {
39
+ return Result.error(ValidationError.InvalidProof, () => "bandersnatch proof rejected");
40
+ }
41
+ const verified = { ticket, id: tickets[0] };
42
+ this.pool.add(epochIndex, [verified]);
43
+ return Result.ok({ id: tickets[0] });
44
+ }
45
+ /**
46
+ * Returns the correct tickets entropy for verification given the current state.
47
+ *
48
+ * When `state` is from epoch E-1 (i.e. we haven't produced epoch E's first block yet),
49
+ * the ticket entropy for epoch E is at index 1 (not yet shifted). After the epoch
50
+ * transition it moves to index 2.
51
+ */
52
+ getTicketEntropy(epochIndex, state) {
53
+ const stateEpoch = Math.floor(state.timeslot / this.chainSpec.epochLength);
54
+ return epochIndex > stateEpoch ? state.entropy[1] : state.entropy[2];
55
+ }
56
+ }
@@ -21,6 +21,16 @@ 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
36
  request: import("@typeberry/codec").Descriptor<ReceivedTicketMessage, import("@typeberry/codec").ViewOf<ReceivedTicketMessage, {
@@ -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,qBAAqB,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAE7E;;;GAGG;AACH,eAAO,MAAM,uBAAuB,uBAAuB,CAAC;AAE5D;;;;GAIG;AACH,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAwBnB,CAAC;AAEH,MAAM,MAAM,eAAe,GAAG,GAAG,CAAC,OAAO,QAAQ,CAAC,CAAC;AACnD,MAAM,MAAM,eAAe,GAAG,QAAQ,CAAC,OAAO,QAAQ,CAAC,CAAC"}
@@ -12,12 +12,19 @@ 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
29
  // Messages from jam-network to block-authorship (one ticket per relay).
23
30
  // Response indicates whether the ticket passed validation — used by jam-network
@@ -1 +1 @@
1
- {"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../../../../packages/workers/jam-network/main.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qCAAqC,CAAC;AAK3E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,KAAK,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAI1E;;;;;;GAMG;AACH,wBAAsB,IAAI,CACxB,MAAM,EAAE,YAAY,CAAC,gBAAgB,CAAC,EACtC,KAAK,EAAE,kBAAkB,EACzB,eAAe,EAAE,eAAe,iBAyDjC"}
1
+ {"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../../../../packages/workers/jam-network/main.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qCAAqC,CAAC;AAO3E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,KAAK,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAI1E;;;;;;GAMG;AACH,wBAAsB,IAAI,CACxB,MAAM,EAAE,YAAY,CAAC,gBAAgB,CAAC,EACtC,KAAK,EAAE,kBAAkB,EACzB,eAAe,EAAE,eAAe,iBAwEjC"}
@@ -2,6 +2,8 @@ import { parseBootnode } from "#@typeberry/config-node";
2
2
  import { ed25519, initWasm } from "#@typeberry/crypto";
3
3
  import { setup } from "#@typeberry/jamnp-s";
4
4
  import { Logger } from "#@typeberry/logger";
5
+ import { ValidationError } from "#@typeberry/ticket-pool";
6
+ import { Result } from "#@typeberry/utils";
5
7
  const logger = Logger.new(import.meta.filename, "net");
6
8
  /**
7
9
  * JAM networking worker.
@@ -38,11 +40,25 @@ export async function main(config, comms, authorshipComms) {
38
40
  network.ticketTask.addTicket(epochIndex, ticket);
39
41
  }
40
42
  });
41
- // Relay tickets received from peers back to block-authorship (one ticket at a time).
42
- // Returns the validation result so ticket-distribution knows whether to redistribute.
43
- network.ticketTask.setOnTicketReceived(async (epochIndex, ticket) => {
44
- return await authorshipComms.sendReceivedTickets({ epochIndex, ticket });
43
+ // Authorship pushes the authoritative ticket pool on epoch boundaries; networking
44
+ // replaces its redistribution pool wholesale so the two sides cannot drift.
45
+ authorshipComms.setOnReplaceTicketPool(async ({ epochIndex, tickets }) => {
46
+ logger.log `Replacing redistribution pool from block-authorship for epoch ${epochIndex} (${tickets.length} tickets)`;
47
+ network.ticketTask.replacePool(epochIndex, tickets);
45
48
  });
49
+ // Validator that hands a received ticket to block-authorship over IPC and waits
50
+ // for an accept/reject decision. The wire protocol stays a simple bool; the
51
+ // computed id stays inside authorship (it owns the verified pool).
52
+ const ipcValidator = {
53
+ validate: async (epochIndex, ticket) => {
54
+ const ok = await authorshipComms.sendReceivedTickets({ epochIndex, ticket });
55
+ if (!ok) {
56
+ return Result.error(ValidationError.InvalidProof, () => "authorship rejected the ticket");
57
+ }
58
+ return Result.ok({ id: null });
59
+ },
60
+ };
61
+ network.ticketTask.setTicketValidator(ipcValidator);
46
62
  await network.network.start();
47
63
  // stop the network when the worker is finishing.
48
64
  await waitForFinish;