@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.
- package/package.json +6 -4
- package/packages/jam/database-fjall/hybrid-states.d.ts +45 -0
- package/packages/jam/database-fjall/hybrid-states.d.ts.map +1 -0
- package/packages/jam/database-fjall/hybrid-states.js +113 -0
- package/packages/jam/database-fjall/hybrid-states.test.d.ts +2 -0
- package/packages/jam/database-fjall/hybrid-states.test.d.ts.map +1 -0
- package/packages/jam/database-fjall/hybrid-states.test.js +83 -0
- package/packages/jam/database-fjall/index.d.ts +3 -0
- package/packages/jam/database-fjall/index.d.ts.map +1 -0
- package/packages/jam/database-fjall/index.js +2 -0
- package/packages/jam/database-fjall/root.d.ts +52 -0
- package/packages/jam/database-fjall/root.d.ts.map +1 -0
- package/packages/jam/database-fjall/root.js +85 -0
- package/packages/jam/jamnp-s/tasks/ticket-distribution.d.ts +18 -10
- package/packages/jam/jamnp-s/tasks/ticket-distribution.d.ts.map +1 -1
- package/packages/jam/jamnp-s/tasks/ticket-distribution.js +44 -68
- package/packages/jam/jamnp-s/tasks/ticket-distribution.test.js +30 -8
- package/packages/jam/node/main-fuzz.d.ts.map +1 -1
- package/packages/jam/node/main-fuzz.js +16 -1
- package/packages/jam/node/main-importer.d.ts +6 -3
- package/packages/jam/node/main-importer.d.ts.map +1 -1
- package/packages/jam/node/main-importer.js +3 -2
- package/packages/jam/safrole/bandersnatch-vrf.d.ts +2 -2
- package/packages/jam/safrole/bandersnatch-vrf.d.ts.map +1 -1
- package/packages/jam/safrole/bandersnatch-vrf.js +9 -6
- package/packages/jam/safrole/bandersnatch-vrf.test.js +9 -7
- package/packages/jam/safrole/safrole.js +5 -5
- package/packages/jam/safrole/safrole.test.js +13 -13
- package/packages/jam/ticket-pool/index.d.ts +4 -0
- package/packages/jam/ticket-pool/index.d.ts.map +1 -0
- package/packages/jam/ticket-pool/index.js +3 -0
- package/packages/jam/ticket-pool/pending-ticket-pool.d.ts +30 -0
- package/packages/jam/ticket-pool/pending-ticket-pool.d.ts.map +1 -0
- package/packages/jam/ticket-pool/pending-ticket-pool.js +56 -0
- package/packages/jam/ticket-pool/pending-ticket-pool.test.d.ts +2 -0
- package/packages/jam/ticket-pool/pending-ticket-pool.test.d.ts.map +1 -0
- package/packages/jam/ticket-pool/pending-ticket-pool.test.js +67 -0
- package/packages/jam/ticket-pool/ticket-validator.d.ts +46 -0
- package/packages/jam/ticket-pool/ticket-validator.d.ts.map +1 -0
- package/packages/jam/ticket-pool/ticket-validator.js +29 -0
- package/packages/jam/ticket-pool/ticket-validator.test.d.ts +2 -0
- package/packages/jam/ticket-pool/ticket-validator.test.d.ts.map +1 -0
- package/packages/jam/ticket-pool/ticket-validator.test.js +34 -0
- package/packages/jam/ticket-pool/verified-ticket-pool.d.ts +24 -0
- package/packages/jam/ticket-pool/verified-ticket-pool.d.ts.map +1 -0
- package/packages/jam/ticket-pool/verified-ticket-pool.js +37 -0
- package/packages/jam/ticket-pool/verified-ticket-pool.test.d.ts +2 -0
- package/packages/jam/ticket-pool/verified-ticket-pool.test.d.ts.map +1 -0
- package/packages/jam/ticket-pool/verified-ticket-pool.test.js +54 -0
- package/packages/workers/api-node/config.d.ts +12 -5
- package/packages/workers/api-node/config.d.ts.map +1 -1
- package/packages/workers/api-node/config.js +20 -17
- package/packages/workers/api-node/config.test.js +38 -1
- package/packages/workers/block-authorship/main.d.ts.map +1 -1
- package/packages/workers/block-authorship/main.js +22 -72
- package/packages/workers/block-authorship/ticket-validator.d.ts +32 -0
- package/packages/workers/block-authorship/ticket-validator.d.ts.map +1 -0
- package/packages/workers/block-authorship/ticket-validator.js +56 -0
- package/packages/workers/comms-authorship-network/protocol.d.ts +10 -0
- package/packages/workers/comms-authorship-network/protocol.d.ts.map +1 -1
- package/packages/workers/comms-authorship-network/protocol.js +8 -1
- package/packages/workers/jam-network/main.d.ts.map +1 -1
- 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
|
-
//
|
|
185
|
-
//
|
|
186
|
-
const
|
|
187
|
-
const
|
|
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
|
|
250
|
-
|
|
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
|
|
305
|
-
|
|
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 =
|
|
340
|
-
const newBlock = await generator.nextBlockView(validatorIndex, key.bandersnatchSecret, sealPayload, timeSlot,
|
|
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
|
|
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":"
|
|
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
|
-
//
|
|
42
|
-
//
|
|
43
|
-
|
|
44
|
-
|
|
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;
|