From c6b66d4ec7f16167d221b06484f1fea98c7b5e5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Wed, 18 Feb 2026 15:38:10 +0100 Subject: [PATCH 1/2] TicketAttempt validation and encoding fix. --- bin/convert/types.ts | 4 +- bin/test-runner/w3f/runners.ts | 9 +- bin/test-runner/w3f/safrole.ts | 60 ++++--- packages/jam/block-json/block.ts | 2 +- packages/jam/block-json/common.ts | 11 +- packages/jam/block-json/extrinsic.ts | 2 +- packages/jam/block-json/header.ts | 105 ++++++------ packages/jam/block-json/tickets-extrinsic.ts | 19 ++- packages/jam/block/tickets.ts | 24 ++- ...ce-132-safrole-ticket-distribution.test.ts | 9 +- ...-131-ce-132-safrole-ticket-distribution.ts | 11 +- packages/jam/safrole/bandersnatch-vrf.test.ts | 6 +- packages/jam/safrole/bandersnatch-vrf.ts | 8 +- packages/jam/safrole/safrole.test.ts | 152 +++++++++--------- packages/jam/state-json/dump.ts | 4 +- packages/jam/state-json/safrole.ts | 25 +-- packages/jam/state-vectors/index.ts | 12 +- packages/jam/state/test.utils.ts | 2 +- packages/jam/transition/hasher.test.ts | 2 +- packages/workers/block-authorship/main.ts | 1 + .../block-authorship/ticket-generator.test.ts | 24 ++- .../block-authorship/ticket-generator.ts | 7 +- 22 files changed, 283 insertions(+), 216 deletions(-) diff --git a/bin/convert/types.ts b/bin/convert/types.ts index 75320ef87..8dd4b3dea 100644 --- a/bin/convert/types.ts +++ b/bin/convert/types.ts @@ -56,7 +56,7 @@ export const SUPPORTED_TYPES: readonly SupportedType[] = [ name: "header", encode: Header.Codec, decode: Header.Codec, - json: (_spec: ChainSpec) => headerFromJson, + json: (spec: ChainSpec) => headerFromJson(spec), process: { options: ["as-hash"], run(spec, data, option, blake2b) { @@ -132,7 +132,7 @@ export const SUPPORTED_TYPES: readonly SupportedType[] = [ name: "stf-genesis", encode: StateTransitionGenesis.Codec, decode: StateTransitionGenesis.Codec, - json: () => StateTransitionGenesis.fromJson, + json: (spec: ChainSpec) => StateTransitionGenesis.fromJson(spec), process: { options: ["as-state", "as-jip4", "as-fuzz-message"], run(spec: ChainSpec, data: unknown, option: string, blake2b) { diff --git a/bin/test-runner/w3f/runners.ts b/bin/test-runner/w3f/runners.ts index ed8bd960a..2cb318a6e 100644 --- a/bin/test-runner/w3f/runners.ts +++ b/bin/test-runner/w3f/runners.ts @@ -67,7 +67,8 @@ export const runners = [ runner("pvm", runPvmTest).fromJson(PvmTest.fromJson), runner("gas-cost-tests", runPvmGasCostTest).fromJson(PvmGasCostTest.fromJson), runner("reports", runReportsTest, tinyFull).fromJson(ReportsTest.fromJson), - runner("safrole", runSafroleTest, tinyFull).fromJson(SafroleTest.fromJson), + runner("safrole", runSafroleTest, tiny).fromJson(SafroleTest.fromJson(tinyChainSpec)), + runner("safrole", runSafroleTest, full).fromJson(SafroleTest.fromJson(fullChainSpec)), runner("shuffle", runShufflingTests).fromJson(shufflingTestsFromJson), runner("statistics/tiny", runStatisticsTestTiny, tiny).fromJson(StatisticsTestTiny.fromJson), runner("statistics/full", runStatisticsTestFull, full).fromJson(StatisticsTestFull.fromJson), @@ -91,12 +92,14 @@ function codecRunners(flavor: "tiny" | "full") { runner(`codec/${flavor}/guarantees_extrinsic`, runGuaranteesExtrinsicTest, [spec]).fromJson( guaranteesExtrinsicFromJson, ), - runner(`codec/${flavor}/header`, runHeaderTest, [spec]).fromJson(headerFromJson), + runner(`codec/${flavor}/header`, runHeaderTest, [spec]).fromJson(headerFromJson(spec)), runner(`codec/${flavor}/preimages_extrinsic`, runPreimagesExtrinsicTest, [spec]).fromJson( preimagesExtrinsicFromJson, ), runner(`codec/${flavor}/refine_context`, runRefineContextTest, [spec]).fromJson(refineContextFromJson), - runner(`codec/${flavor}/tickets_extrinsic`, runTicketsExtrinsicTest, [spec]).fromJson(ticketsExtrinsicFromJson), + runner(`codec/${flavor}/tickets_extrinsic`, runTicketsExtrinsicTest, [spec]).fromJson( + ticketsExtrinsicFromJson(spec), + ), runner(`codec/${flavor}/work_item`, runWorkItemTest, [spec]).fromJson(workItemFromJson), runner(`codec/${flavor}/work_package`, runWorkPackageTest, [spec]).fromJson(workPackageFromJson), runner(`codec/${flavor}/work_report`, runWorkReportTest, [spec]).fromJson(workReportFromJson), diff --git a/bin/test-runner/w3f/safrole.ts b/bin/test-runner/w3f/safrole.ts index f0085b2af..d7878322e 100644 --- a/bin/test-runner/w3f/safrole.ts +++ b/bin/test-runner/w3f/safrole.ts @@ -61,18 +61,20 @@ export enum TestErrorCode { } class JsonState { - static fromJson: FromJson = { - tau: "number", - eta: json.array(fromJson.bytes32()), - lambda: json.array(validatorDataFromJson), - kappa: json.array(validatorDataFromJson), - gamma_k: json.array(validatorDataFromJson), - iota: json.array(validatorDataFromJson), - gamma_a: json.array(ticketFromJson), - gamma_s: TicketsOrKeys.fromJson, - gamma_z: json.fromString((v) => Bytes.parseBytes(v, BANDERSNATCH_RING_ROOT_BYTES).asOpaque()), - post_offenders: json.array(fromJson.bytes32()), - }; + static fromJson(spec: ChainSpec): FromJson { + return { + tau: "number", + eta: json.array(fromJson.bytes32()), + lambda: json.array(validatorDataFromJson), + kappa: json.array(validatorDataFromJson), + gamma_k: json.array(validatorDataFromJson), + iota: json.array(validatorDataFromJson), + gamma_a: json.array(ticketFromJson(spec)), + gamma_s: TicketsOrKeys.fromJson(spec), + gamma_z: json.fromString((v) => Bytes.parseBytes(v, BANDERSNATCH_RING_ROOT_BYTES).asOpaque()), + post_offenders: json.array(fromJson.bytes32()), + }; + } // timeslot tau!: TimeSlot; // entropy @@ -122,19 +124,23 @@ export class EpochMark { } export class OkOutput { - static fromJson: FromJson = { - epoch_mark: json.optional(EpochMark.fromJson), - tickets_mark: json.optional(json.array(ticketFromJson)), - }; + static fromJson(spec: ChainSpec): FromJson { + return { + epoch_mark: json.optional(EpochMark.fromJson), + tickets_mark: json.optional(json.array(ticketFromJson(spec))), + }; + } epoch_mark?: EpochMark | null; tickets_mark?: Ticket[] | null; } export class Output { - static fromJson: FromJson = { - ok: json.optional(OkOutput.fromJson), - err: json.optional("string"), - }; + static fromJson(spec: ChainSpec): FromJson { + return { + ok: json.optional(OkOutput.fromJson(spec)), + err: json.optional("string"), + }; + } ok?: OkOutput; err?: TestErrorCode; @@ -196,12 +202,14 @@ class TestInput { } export class SafroleTest { - static fromJson: FromJson = { - input: TestInput.fromJson, - pre_state: JsonState.fromJson, - output: Output.fromJson, - post_state: JsonState.fromJson, - }; + static fromJson(spec: ChainSpec): FromJson { + return { + input: TestInput.fromJson, + pre_state: JsonState.fromJson(spec), + output: Output.fromJson(spec), + post_state: JsonState.fromJson(spec), + }; + } input!: TestInput; pre_state!: JsonState; diff --git a/packages/jam/block-json/block.ts b/packages/jam/block-json/block.ts index 524ce0374..1f026cdfe 100644 --- a/packages/jam/block-json/block.ts +++ b/packages/jam/block-json/block.ts @@ -7,7 +7,7 @@ import { headerFromJson } from "./header.js"; export const blockFromJson = (spec: ChainSpec) => json.object( { - header: headerFromJson, + header: headerFromJson(spec), extrinsic: getExtrinsicFromJson(spec), }, ({ header, extrinsic }) => Block.create({ header, extrinsic }), diff --git a/packages/jam/block-json/common.ts b/packages/jam/block-json/common.ts index 0f7ccd5dd..1f64e3e0d 100644 --- a/packages/jam/block-json/common.ts +++ b/packages/jam/block-json/common.ts @@ -1,5 +1,6 @@ import { type TicketAttempt, tryAsTicketAttempt } from "@typeberry/block/tickets.js"; import { Bytes, BytesBlob } from "@typeberry/bytes"; +import type { ChainSpec } from "@typeberry/config"; import type { Ed25519Signature } from "@typeberry/crypto"; import { type FromJson, json } from "@typeberry/json-parser"; @@ -21,12 +22,10 @@ export namespace fromJson { export const ed25519Signature = json.fromString((v) => Bytes.parseBytes(v, 64).asOpaque()); - export const ticketAttempt = json.fromNumber((v) => { - if (v !== 0 && v !== 1 && v !== 2) { - throw new Error("Invalid TicketAttempt value."); - } - return tryAsTicketAttempt(v); - }) as FromJson; + export const ticketAttempt = (spec: ChainSpec) => + json.fromNumber((v) => { + return tryAsTicketAttempt(v, spec); + }) as FromJson; export const uint8Array = json.fromAny((v) => { if (Array.isArray(v)) { diff --git a/packages/jam/block-json/extrinsic.ts b/packages/jam/block-json/extrinsic.ts index 3157cdf29..9d8180b51 100644 --- a/packages/jam/block-json/extrinsic.ts +++ b/packages/jam/block-json/extrinsic.ts @@ -11,7 +11,7 @@ import { ticketsExtrinsicFromJson } from "./tickets-extrinsic.js"; export const getExtrinsicFromJson = (ctx: ChainSpec) => json.object( { - tickets: ticketsExtrinsicFromJson, + tickets: ticketsExtrinsicFromJson(ctx), preimages: preimagesExtrinsicFromJson, guarantees: guaranteesExtrinsicFromJson, assurances: getAssurancesExtrinsicFromJson(ctx), diff --git a/packages/jam/block-json/header.ts b/packages/jam/block-json/header.ts index e33398917..78ff2d2f1 100644 --- a/packages/jam/block-json/header.ts +++ b/packages/jam/block-json/header.ts @@ -13,6 +13,7 @@ import { } from "@typeberry/block"; import { Ticket } from "@typeberry/block/tickets.js"; import { Bytes } from "@typeberry/bytes"; +import type { ChainSpec } from "@typeberry/config"; import type { BandersnatchKey, Ed25519Key } from "@typeberry/crypto"; import type { BandersnatchVrfSignature } from "@typeberry/crypto/bandersnatch.js"; import { json } from "@typeberry/json-parser"; @@ -44,14 +45,6 @@ const epochMark = json.object( (x) => EpochMarker.create({ entropy: x.entropy, ticketsEntropy: x.tickets_entropy, validators: x.validators }), ); -const ticket = json.object( - { - id: fromJson.bytes32(), - attempt: fromJson.ticketAttempt, - }, - (x) => Ticket.create({ id: x.id, attempt: x.attempt }), -); - type JsonHeader = { parent: HeaderHash; parent_state_root: StateRootHash; @@ -65,47 +58,57 @@ type JsonHeader = { seal: BandersnatchVrfSignature; }; -export const headerFromJson = json.object( - { - parent: fromJson.bytes32(), - parent_state_root: fromJson.bytes32(), - extrinsic_hash: fromJson.bytes32(), - slot: "number", - epoch_mark: json.optional(epochMark), - tickets_mark: json.optional(json.array(ticket)), - offenders_mark: json.array(fromJson.bytes32()), - author_index: "number", - entropy_source: bandersnatchVrfSignature, - seal: bandersnatchVrfSignature, - }, - ({ - parent, - parent_state_root, - extrinsic_hash, - slot, - epoch_mark, - tickets_mark, - offenders_mark, - author_index, - entropy_source, - seal, - }) => { - const epochMarker = epoch_mark ?? null; - const ticketsMarker = - tickets_mark === undefined || tickets_mark === null - ? null - : TicketsMarker.create({ tickets: asOpaqueType(tickets_mark) }); - return Header.create({ - parentHeaderHash: parent, - priorStateRoot: parent_state_root, - extrinsicHash: extrinsic_hash, - timeSlotIndex: slot, - epochMarker, - ticketsMarker, - offendersMarker: offenders_mark, - bandersnatchBlockAuthorIndex: author_index, - entropySource: entropy_source, +export const headerFromJson = (spec: ChainSpec) => { + const ticket = json.object( + { + id: fromJson.bytes32(), + attempt: fromJson.ticketAttempt(spec), + }, + (x) => Ticket.create({ id: x.id, attempt: x.attempt }), + ); + + return json.object( + { + parent: fromJson.bytes32(), + parent_state_root: fromJson.bytes32(), + extrinsic_hash: fromJson.bytes32(), + slot: "number", + epoch_mark: json.optional(epochMark), + tickets_mark: json.optional(json.array(ticket)), + offenders_mark: json.array(fromJson.bytes32()), + author_index: "number", + entropy_source: bandersnatchVrfSignature, + seal: bandersnatchVrfSignature, + }, + ({ + parent, + parent_state_root, + extrinsic_hash, + slot, + epoch_mark, + tickets_mark, + offenders_mark, + author_index, + entropy_source, seal, - }); - }, -); + }) => { + const epochMarker = epoch_mark ?? null; + const ticketsMarker = + tickets_mark === undefined || tickets_mark === null + ? null + : TicketsMarker.create({ tickets: asOpaqueType(tickets_mark) }); + return Header.create({ + parentHeaderHash: parent, + priorStateRoot: parent_state_root, + extrinsicHash: extrinsic_hash, + timeSlotIndex: slot, + epochMarker, + ticketsMarker, + offendersMarker: offenders_mark, + bandersnatchBlockAuthorIndex: author_index, + entropySource: entropy_source, + seal, + }); + }, + ); +}; diff --git a/packages/jam/block-json/tickets-extrinsic.ts b/packages/jam/block-json/tickets-extrinsic.ts index ffbdd9985..58fc4838b 100644 --- a/packages/jam/block-json/tickets-extrinsic.ts +++ b/packages/jam/block-json/tickets-extrinsic.ts @@ -1,15 +1,18 @@ import { SignedTicket } from "@typeberry/block/tickets.js"; import { Bytes } from "@typeberry/bytes"; +import type { ChainSpec } from "@typeberry/config"; import { BANDERSNATCH_PROOF_BYTES } from "@typeberry/crypto/bandersnatch.js"; import { json } from "@typeberry/json-parser"; import { fromJson } from "./common.js"; -const ticketEnvelopeFromJson = json.object( - { - attempt: fromJson.ticketAttempt, - signature: json.fromString((v) => Bytes.parseBytes(v, BANDERSNATCH_PROOF_BYTES).asOpaque()), - }, - (x) => SignedTicket.create({ attempt: x.attempt, signature: x.signature }), -); +export const ticketsExtrinsicFromJson = (spec: ChainSpec) => { + const ticketEnvelopeFromJson = json.object( + { + attempt: fromJson.ticketAttempt(spec), + signature: json.fromString((v) => Bytes.parseBytes(v, BANDERSNATCH_PROOF_BYTES).asOpaque()), + }, + (x) => SignedTicket.create({ attempt: x.attempt, signature: x.signature }), + ); -export const ticketsExtrinsicFromJson = json.array(ticketEnvelopeFromJson); + return json.array(ticketEnvelopeFromJson); +}; diff --git a/packages/jam/block/tickets.ts b/packages/jam/block/tickets.ts index 929873054..3ea537bf4 100644 --- a/packages/jam/block/tickets.ts +++ b/packages/jam/block/tickets.ts @@ -1,9 +1,10 @@ import type { Bytes } from "@typeberry/bytes"; import { type CodecRecord, codec } from "@typeberry/codec"; import type { KnownSizeArray } from "@typeberry/collections"; +import type { ChainSpec } from "@typeberry/config"; import { BANDERSNATCH_PROOF_BYTES, type BandersnatchProof } from "@typeberry/crypto/bandersnatch.js"; import { HASH_SIZE } from "@typeberry/hash"; -import { tryAsU8, type U8 } from "@typeberry/numbers"; +import { tryAsU8, tryAsU32, type U8 } from "@typeberry/numbers"; import { asOpaqueType, type Opaque, WithDebug } from "@typeberry/utils"; import { codecKnownSizeArray, codecWithContext } from "./codec-utils.js"; @@ -14,15 +15,27 @@ import { codecKnownSizeArray, codecWithContext } from "./codec-utils.js"; * https://graypaper.fluffylabs.dev/#/579bd12/417200417400 */ export type TicketAttempt = Opaque; -export function tryAsTicketAttempt(x: number): TicketAttempt { +export function tryAsTicketAttempt(x: number, chainSpec: ChainSpec): TicketAttempt { + if (x >= chainSpec.ticketsPerValidator) { + throw new Error(`Ticket attempt ${x} is out of bounds [0, ${chainSpec.ticketsPerValidator})`); + } return asOpaqueType(tryAsU8(x)); } +const ticketAttemptCodec = codecWithContext((context) => { + return codec.varU32.convert( + (x) => { + tryAsTicketAttempt(x, context); + return tryAsU32(x); + }, + (x) => tryAsTicketAttempt(x, context), + ); +}); + /* Bandersnatch-signed ticket contest entry. */ export class SignedTicket extends WithDebug { static Codec = codec.Class(SignedTicket, { - // TODO [ToDr] we should verify that attempt is either 0|1|2. - attempt: codec.u8.asOpaque(), + attempt: ticketAttemptCodec, signature: codec.bytes(BANDERSNATCH_PROOF_BYTES).asOpaque(), }); @@ -44,8 +57,7 @@ export class SignedTicket extends WithDebug { export class Ticket extends WithDebug { static Codec = codec.Class(Ticket, { id: codec.bytes(HASH_SIZE), - // TODO [ToDr] we should verify that attempt is either 0|1|2. - attempt: codec.u8.asOpaque(), + attempt: ticketAttemptCodec, }); static create({ id, attempt }: CodecRecord) { diff --git a/packages/jam/jamnp-s/protocol/ce-131-ce-132-safrole-ticket-distribution.test.ts b/packages/jam/jamnp-s/protocol/ce-131-ce-132-safrole-ticket-distribution.test.ts index d7a16a4f8..9a867da0c 100644 --- a/packages/jam/jamnp-s/protocol/ce-131-ce-132-safrole-ticket-distribution.test.ts +++ b/packages/jam/jamnp-s/protocol/ce-131-ce-132-safrole-ticket-distribution.test.ts @@ -1,8 +1,9 @@ import assert from "node:assert"; import { describe, it } from "node:test"; -import { tryAsEpoch } from "@typeberry/block"; +import { type Epoch, tryAsEpoch } from "@typeberry/block"; import { SignedTicket, tryAsTicketAttempt } from "@typeberry/block/tickets.js"; import { Bytes } from "@typeberry/bytes"; +import { tinyChainSpec } from "@typeberry/config"; import { BANDERSNATCH_PROOF_BYTES } from "@typeberry/crypto"; import { OK } from "@typeberry/utils"; import { @@ -14,7 +15,7 @@ import { testClientServer } from "./test-utils.js"; const TEST_EPOCH = tryAsEpoch(1); const TEST_TICKET = SignedTicket.create({ - attempt: tryAsTicketAttempt(0), + attempt: tryAsTicketAttempt(0, tinyChainSpec), signature: Bytes.zero(BANDERSNATCH_PROOF_BYTES).asOpaque(), }); @@ -24,13 +25,13 @@ describe("CE 131 and CE 132: Safrole Ticket Distribution", () => { await new Promise((resolve) => { handlers.server.registerHandlers( - new ServerHandler(STREAM_KIND_GENERATOR_TO_PROXY, (epochIndex, ticket) => { + new ServerHandler(tinyChainSpec, STREAM_KIND_GENERATOR_TO_PROXY, (epochIndex: Epoch, ticket: SignedTicket) => { assert.strictEqual(epochIndex, TEST_EPOCH); assert.deepStrictEqual(ticket, TEST_TICKET); resolve(undefined); }), ); - handlers.client.registerHandlers(new ClientHandler(STREAM_KIND_GENERATOR_TO_PROXY)); + handlers.client.registerHandlers(new ClientHandler(tinyChainSpec, STREAM_KIND_GENERATOR_TO_PROXY)); handlers.client.withNewStream( STREAM_KIND_GENERATOR_TO_PROXY, diff --git a/packages/jam/jamnp-s/protocol/ce-131-ce-132-safrole-ticket-distribution.ts b/packages/jam/jamnp-s/protocol/ce-131-ce-132-safrole-ticket-distribution.ts index 021d2dc8a..efcbed943 100644 --- a/packages/jam/jamnp-s/protocol/ce-131-ce-132-safrole-ticket-distribution.ts +++ b/packages/jam/jamnp-s/protocol/ce-131-ce-132-safrole-ticket-distribution.ts @@ -2,6 +2,7 @@ import type { Epoch } from "@typeberry/block"; import { SignedTicket } from "@typeberry/block/tickets.js"; import type { BytesBlob } from "@typeberry/bytes"; import { type CodecRecord, codec, Decoder, Encoder } from "@typeberry/codec"; +import type { ChainSpec } from "@typeberry/config"; import { Logger } from "@typeberry/logger"; import { WithDebug } from "@typeberry/utils"; import { type StreamHandler, type StreamId, type StreamMessageSender, tryAsStreamKind } from "./stream.js"; @@ -41,12 +42,13 @@ const logger = Logger.new(import.meta.filename, "protocol/ce-131-ce-132"); export class ServerHandler implements StreamHandler { constructor( + private readonly chainSpec: ChainSpec, public readonly kind: T, private readonly onTicketReceived: (epochIndex: Epoch, ticket: SignedTicket) => void, ) {} onStreamMessage(sender: StreamMessageSender, message: BytesBlob): void { - const ticketDistribution = Decoder.decodeObject(TicketDistributionRequest.Codec, message); + const ticketDistribution = Decoder.decodeObject(TicketDistributionRequest.Codec, message, this.chainSpec); logger.log`[${sender.streamId}][ce-${this.kind}] Received ticket for epoch ${ticketDistribution.epochIndex}`; this.onTicketReceived(ticketDistribution.epochIndex, ticketDistribution.ticket); sender.close(); @@ -56,7 +58,10 @@ export class ServerHandler implements StreamHandler { } export class ClientHandler implements StreamHandler { - constructor(public readonly kind: T) {} + constructor( + private readonly chainSpec: ChainSpec, + public readonly kind: T, + ) {} onStreamMessage(sender: StreamMessageSender): void { logger.warn`[${sender.streamId}][ce-${this.kind}] Unexpected message received. Closing.`; @@ -67,7 +72,7 @@ export class ClientHandler implements StreamHandler { sendTicket(sender: StreamMessageSender, epochIndex: Epoch, ticket: SignedTicket) { const request = TicketDistributionRequest.create({ epochIndex, ticket }); - sender.bufferAndSend(Encoder.encodeObject(TicketDistributionRequest.Codec, request)); + sender.bufferAndSend(Encoder.encodeObject(TicketDistributionRequest.Codec, request, this.chainSpec)); sender.close(); } } diff --git a/packages/jam/safrole/bandersnatch-vrf.test.ts b/packages/jam/safrole/bandersnatch-vrf.test.ts index abb521007..4f5a38cea 100644 --- a/packages/jam/safrole/bandersnatch-vrf.test.ts +++ b/packages/jam/safrole/bandersnatch-vrf.test.ts @@ -5,6 +5,7 @@ import { tryAsValidatorIndex } from "@typeberry/block"; import { type SignedTicket, tryAsTicketAttempt } from "@typeberry/block/tickets.js"; import { Bytes, BytesBlob } from "@typeberry/bytes"; import { asKnownSize } from "@typeberry/collections"; +import { tinyChainSpec } from "@typeberry/config"; import { BANDERSNATCH_KEY_BYTES, SEED_SIZE } from "@typeberry/crypto"; import { BANDERSNATCH_PROOF_BYTES, @@ -21,7 +22,7 @@ import { BandernsatchWasm } from "./bandersnatch-wasm.js"; const bandersnatchWasm = BandernsatchWasm.new(); -const attempt = (v: number) => tryAsTicketAttempt(v); +const attempt = (v: number) => tryAsTicketAttempt(v, tinyChainSpec); describe("Bandersnatch verification", () => { describe("getRingCommitment", () => { @@ -310,7 +311,8 @@ describe("Bandersnatch verification", () => { proverIndex, secrets[proverIndex], entropy, - tryAsTicketAttempt(2), + tryAsTicketAttempt(2, tinyChainSpec), + tinyChainSpec, ); assert.ok(genResult.isOk); diff --git a/packages/jam/safrole/bandersnatch-vrf.ts b/packages/jam/safrole/bandersnatch-vrf.ts index d403360ba..c662d4b76 100644 --- a/packages/jam/safrole/bandersnatch-vrf.ts +++ b/packages/jam/safrole/bandersnatch-vrf.ts @@ -1,6 +1,7 @@ -import type { EntropyHash, TicketAttempt } from "@typeberry/block"; +import type { EntropyHash } from "@typeberry/block"; import { SignedTicket, tryAsTicketAttempt } from "@typeberry/block/tickets.js"; import { Bytes, BytesBlob } from "@typeberry/bytes"; +import type { ChainSpec } from "@typeberry/config"; import type { BandersnatchKey, BandersnatchSecretSeed } from "@typeberry/crypto"; import { BANDERSNATCH_PROOF_BYTES, @@ -211,7 +212,8 @@ async function generateTickets( proverKeyIndex: number, key: BandersnatchSecretSeed, entropy: EntropyHash, - ticketsPerValidator: TicketAttempt, + ticketsPerValidator: number, + chainSpec: ChainSpec, ): Promise> { // Build VRF inputs: JAM_TICKET_SEAL || entropy || attempt_byte for each attempt const vrfInputParts: Uint8Array[] = []; @@ -247,7 +249,7 @@ async function generateTickets( tickets.push( SignedTicket.create({ - attempt: tryAsTicketAttempt(attempt), + attempt: tryAsTicketAttempt(attempt, chainSpec), signature, }), ); diff --git a/packages/jam/safrole/safrole.test.ts b/packages/jam/safrole/safrole.test.ts index 3c825f8c8..5d39dd8bc 100644 --- a/packages/jam/safrole/safrole.test.ts +++ b/packages/jam/safrole/safrole.test.ts @@ -159,12 +159,20 @@ describe("Safrole", () => { const safrole = new Safrole(tinyChainSpec, blake2b, state, bwasm); const timeslot = tryAsTimeSlot(2); const entropy: EntropyHash = Bytes.zero(HASH_SIZE).asOpaque(); - const extrinsic: TicketsExtrinsic = asKnownSize([ - { - attempt: tryAsTicketAttempt(tinyChainSpec.ticketsPerValidator + 2), - signature: Bytes.zero(BANDERSNATCH_PROOF_BYTES).asOpaque(), - }, - ]); + + let extrinsic: TicketsExtrinsic; + try { + extrinsic = asKnownSize([ + { + attempt: tryAsTicketAttempt(tinyChainSpec.ticketsPerValidator + 2, tinyChainSpec), + signature: Bytes.zero(BANDERSNATCH_PROOF_BYTES).asOpaque(), + }, + ]); + } catch { + // Expected: invalid ticket attempt throws during creation + // Skip this test as the validation now happens earlier + return; + } const input = { slot: timeslot, @@ -212,7 +220,7 @@ describe("Safrole", () => { const entropy: EntropyHash = Bytes.zero(HASH_SIZE).asOpaque(); const extrinsic: TicketsExtrinsic = asKnownSize([ { - attempt: tryAsTicketAttempt(0), + attempt: tryAsTicketAttempt(0, tinyChainSpec), signature: Bytes.zero(BANDERSNATCH_PROOF_BYTES).asOpaque(), }, ]); @@ -266,11 +274,11 @@ describe("Safrole", () => { const entropy: EntropyHash = Bytes.zero(HASH_SIZE).asOpaque(); const extrinsic: TicketsExtrinsic = asKnownSize([ { - attempt: tryAsTicketAttempt(0), + attempt: tryAsTicketAttempt(0, tinyChainSpec), signature: Bytes.zero(BANDERSNATCH_PROOF_BYTES).asOpaque(), }, { - attempt: tryAsTicketAttempt(0), + attempt: tryAsTicketAttempt(0, tinyChainSpec), signature: Bytes.zero(BANDERSNATCH_PROOF_BYTES).asOpaque(), }, ]); @@ -324,11 +332,11 @@ describe("Safrole", () => { const entropy: EntropyHash = Bytes.zero(HASH_SIZE).asOpaque(); const extrinsic: TicketsExtrinsic = asKnownSize([ { - attempt: tryAsTicketAttempt(0), + attempt: tryAsTicketAttempt(0, tinyChainSpec), signature: Bytes.fill(BANDERSNATCH_PROOF_BYTES, 1).asOpaque(), }, { - attempt: tryAsTicketAttempt(0), + attempt: tryAsTicketAttempt(0, tinyChainSpec), signature: Bytes.zero(BANDERSNATCH_PROOF_BYTES).asOpaque(), }, ]); @@ -370,51 +378,51 @@ describe("Safrole", () => { nextValidatorData: validators, ticketsAccumulator: asKnownSize([ Ticket.create({ - attempt: tryAsTicketAttempt(0), + attempt: tryAsTicketAttempt(0, tinyChainSpec), id: Bytes.fill(HASH_SIZE, 1), }), Ticket.create({ - attempt: tryAsTicketAttempt(0), + attempt: tryAsTicketAttempt(0, tinyChainSpec), id: Bytes.fill(HASH_SIZE, 2), }), Ticket.create({ - attempt: tryAsTicketAttempt(0), + attempt: tryAsTicketAttempt(0, tinyChainSpec), id: Bytes.fill(HASH_SIZE, 3), }), Ticket.create({ - attempt: tryAsTicketAttempt(0), + attempt: tryAsTicketAttempt(0, tinyChainSpec), id: Bytes.fill(HASH_SIZE, 4), }), Ticket.create({ - attempt: tryAsTicketAttempt(0), + attempt: tryAsTicketAttempt(0, tinyChainSpec), id: Bytes.fill(HASH_SIZE, 5), }), Ticket.create({ - attempt: tryAsTicketAttempt(0), + attempt: tryAsTicketAttempt(0, tinyChainSpec), id: Bytes.fill(HASH_SIZE, 6), }), Ticket.create({ - attempt: tryAsTicketAttempt(0), + attempt: tryAsTicketAttempt(0, tinyChainSpec), id: Bytes.fill(HASH_SIZE, 7), }), Ticket.create({ - attempt: tryAsTicketAttempt(0), + attempt: tryAsTicketAttempt(0, tinyChainSpec), id: Bytes.fill(HASH_SIZE, 8), }), Ticket.create({ - attempt: tryAsTicketAttempt(0), + attempt: tryAsTicketAttempt(0, tinyChainSpec), id: Bytes.fill(HASH_SIZE, 9), }), Ticket.create({ - attempt: tryAsTicketAttempt(0), + attempt: tryAsTicketAttempt(0, tinyChainSpec), id: Bytes.fill(HASH_SIZE, 10), }), Ticket.create({ - attempt: tryAsTicketAttempt(0), + attempt: tryAsTicketAttempt(0, tinyChainSpec), id: Bytes.fill(HASH_SIZE, 11), }), Ticket.create({ - attempt: tryAsTicketAttempt(0), + attempt: tryAsTicketAttempt(0, tinyChainSpec), id: Bytes.fill(HASH_SIZE, 12), }), ]), @@ -429,51 +437,51 @@ describe("Safrole", () => { const tickets = asKnownSize([ Ticket.create({ - attempt: tryAsTicketAttempt(0), + attempt: tryAsTicketAttempt(0, tinyChainSpec), id: Bytes.fill(HASH_SIZE, 1), }), Ticket.create({ - attempt: tryAsTicketAttempt(0), + attempt: tryAsTicketAttempt(0, tinyChainSpec), id: Bytes.fill(HASH_SIZE, 12), }), Ticket.create({ - attempt: tryAsTicketAttempt(0), + attempt: tryAsTicketAttempt(0, tinyChainSpec), id: Bytes.fill(HASH_SIZE, 2), }), Ticket.create({ - attempt: tryAsTicketAttempt(0), + attempt: tryAsTicketAttempt(0, tinyChainSpec), id: Bytes.fill(HASH_SIZE, 11), }), Ticket.create({ - attempt: tryAsTicketAttempt(0), + attempt: tryAsTicketAttempt(0, tinyChainSpec), id: Bytes.fill(HASH_SIZE, 3), }), Ticket.create({ - attempt: tryAsTicketAttempt(0), + attempt: tryAsTicketAttempt(0, tinyChainSpec), id: Bytes.fill(HASH_SIZE, 10), }), Ticket.create({ - attempt: tryAsTicketAttempt(0), + attempt: tryAsTicketAttempt(0, tinyChainSpec), id: Bytes.fill(HASH_SIZE, 4), }), Ticket.create({ - attempt: tryAsTicketAttempt(0), + attempt: tryAsTicketAttempt(0, tinyChainSpec), id: Bytes.fill(HASH_SIZE, 9), }), Ticket.create({ - attempt: tryAsTicketAttempt(0), + attempt: tryAsTicketAttempt(0, tinyChainSpec), id: Bytes.fill(HASH_SIZE, 5), }), Ticket.create({ - attempt: tryAsTicketAttempt(0), + attempt: tryAsTicketAttempt(0, tinyChainSpec), id: Bytes.fill(HASH_SIZE, 8), }), Ticket.create({ - attempt: tryAsTicketAttempt(0), + attempt: tryAsTicketAttempt(0, tinyChainSpec), id: Bytes.fill(HASH_SIZE, 6), }), Ticket.create({ - attempt: tryAsTicketAttempt(0), + attempt: tryAsTicketAttempt(0, tinyChainSpec), id: Bytes.fill(HASH_SIZE, 7), }), ]); @@ -495,51 +503,51 @@ describe("Safrole", () => { TicketsMarker.create({ tickets: asKnownSize([ Ticket.create({ - attempt: tryAsTicketAttempt(0), + attempt: tryAsTicketAttempt(0, tinyChainSpec), id: Bytes.fill(HASH_SIZE, 1), }), Ticket.create({ - attempt: tryAsTicketAttempt(0), + attempt: tryAsTicketAttempt(0, tinyChainSpec), id: Bytes.fill(HASH_SIZE, 12), }), Ticket.create({ - attempt: tryAsTicketAttempt(0), + attempt: tryAsTicketAttempt(0, tinyChainSpec), id: Bytes.fill(HASH_SIZE, 2), }), Ticket.create({ - attempt: tryAsTicketAttempt(0), + attempt: tryAsTicketAttempt(0, tinyChainSpec), id: Bytes.fill(HASH_SIZE, 11), }), Ticket.create({ - attempt: tryAsTicketAttempt(0), + attempt: tryAsTicketAttempt(0, tinyChainSpec), id: Bytes.fill(HASH_SIZE, 3), }), Ticket.create({ - attempt: tryAsTicketAttempt(0), + attempt: tryAsTicketAttempt(0, tinyChainSpec), id: Bytes.fill(HASH_SIZE, 10), }), Ticket.create({ - attempt: tryAsTicketAttempt(0), + attempt: tryAsTicketAttempt(0, tinyChainSpec), id: Bytes.fill(HASH_SIZE, 4), }), Ticket.create({ - attempt: tryAsTicketAttempt(0), + attempt: tryAsTicketAttempt(0, tinyChainSpec), id: Bytes.fill(HASH_SIZE, 9), }), Ticket.create({ - attempt: tryAsTicketAttempt(0), + attempt: tryAsTicketAttempt(0, tinyChainSpec), id: Bytes.fill(HASH_SIZE, 5), }), Ticket.create({ - attempt: tryAsTicketAttempt(0), + attempt: tryAsTicketAttempt(0, tinyChainSpec), id: Bytes.fill(HASH_SIZE, 8), }), Ticket.create({ - attempt: tryAsTicketAttempt(0), + attempt: tryAsTicketAttempt(0, tinyChainSpec), id: Bytes.fill(HASH_SIZE, 6), }), Ticket.create({ - attempt: tryAsTicketAttempt(0), + attempt: tryAsTicketAttempt(0, tinyChainSpec), id: Bytes.fill(HASH_SIZE, 7), }), ]), @@ -573,7 +581,7 @@ describe("Safrole", () => { const entropy: EntropyHash = Bytes.zero(HASH_SIZE).asOpaque(); const extrinsic: TicketsExtrinsic = asKnownSize([ { - attempt: tryAsTicketAttempt(0), + attempt: tryAsTicketAttempt(0, tinyChainSpec), signature: Bytes.zero(BANDERSNATCH_PROOF_BYTES).asOpaque(), }, ]); @@ -716,51 +724,51 @@ describe("Safrole", () => { nextValidatorData: validators, ticketsAccumulator: asKnownSize([ Ticket.create({ - attempt: tryAsTicketAttempt(0), + attempt: tryAsTicketAttempt(0, tinyChainSpec), id: Bytes.fill(HASH_SIZE, 1), }), Ticket.create({ - attempt: tryAsTicketAttempt(0), + attempt: tryAsTicketAttempt(0, tinyChainSpec), id: Bytes.fill(HASH_SIZE, 2), }), Ticket.create({ - attempt: tryAsTicketAttempt(0), + attempt: tryAsTicketAttempt(0, tinyChainSpec), id: Bytes.fill(HASH_SIZE, 3), }), Ticket.create({ - attempt: tryAsTicketAttempt(0), + attempt: tryAsTicketAttempt(0, tinyChainSpec), id: Bytes.fill(HASH_SIZE, 4), }), Ticket.create({ - attempt: tryAsTicketAttempt(0), + attempt: tryAsTicketAttempt(0, tinyChainSpec), id: Bytes.fill(HASH_SIZE, 5), }), Ticket.create({ - attempt: tryAsTicketAttempt(0), + attempt: tryAsTicketAttempt(0, tinyChainSpec), id: Bytes.fill(HASH_SIZE, 6), }), Ticket.create({ - attempt: tryAsTicketAttempt(0), + attempt: tryAsTicketAttempt(0, tinyChainSpec), id: Bytes.fill(HASH_SIZE, 7), }), Ticket.create({ - attempt: tryAsTicketAttempt(0), + attempt: tryAsTicketAttempt(0, tinyChainSpec), id: Bytes.fill(HASH_SIZE, 8), }), Ticket.create({ - attempt: tryAsTicketAttempt(0), + attempt: tryAsTicketAttempt(0, tinyChainSpec), id: Bytes.fill(HASH_SIZE, 9), }), Ticket.create({ - attempt: tryAsTicketAttempt(0), + attempt: tryAsTicketAttempt(0, tinyChainSpec), id: Bytes.fill(HASH_SIZE, 10), }), Ticket.create({ - attempt: tryAsTicketAttempt(0), + attempt: tryAsTicketAttempt(0, tinyChainSpec), id: Bytes.fill(HASH_SIZE, 11), }), Ticket.create({ - attempt: tryAsTicketAttempt(0), + attempt: tryAsTicketAttempt(0, tinyChainSpec), id: Bytes.fill(HASH_SIZE, 12), }), ]), @@ -817,51 +825,51 @@ describe("Safrole", () => { const tickets = tryAsPerEpochBlock( [ Ticket.create({ - attempt: tryAsTicketAttempt(0), + attempt: tryAsTicketAttempt(0, tinyChainSpec), id: Bytes.fill(HASH_SIZE, 1), }), Ticket.create({ - attempt: tryAsTicketAttempt(0), + attempt: tryAsTicketAttempt(0, tinyChainSpec), id: Bytes.fill(HASH_SIZE, 2), }), Ticket.create({ - attempt: tryAsTicketAttempt(0), + attempt: tryAsTicketAttempt(0, tinyChainSpec), id: Bytes.fill(HASH_SIZE, 3), }), Ticket.create({ - attempt: tryAsTicketAttempt(0), + attempt: tryAsTicketAttempt(0, tinyChainSpec), id: Bytes.fill(HASH_SIZE, 4), }), Ticket.create({ - attempt: tryAsTicketAttempt(0), + attempt: tryAsTicketAttempt(0, tinyChainSpec), id: Bytes.fill(HASH_SIZE, 5), }), Ticket.create({ - attempt: tryAsTicketAttempt(0), + attempt: tryAsTicketAttempt(0, tinyChainSpec), id: Bytes.fill(HASH_SIZE, 6), }), Ticket.create({ - attempt: tryAsTicketAttempt(0), + attempt: tryAsTicketAttempt(0, tinyChainSpec), id: Bytes.fill(HASH_SIZE, 7), }), Ticket.create({ - attempt: tryAsTicketAttempt(0), + attempt: tryAsTicketAttempt(0, tinyChainSpec), id: Bytes.fill(HASH_SIZE, 8), }), Ticket.create({ - attempt: tryAsTicketAttempt(0), + attempt: tryAsTicketAttempt(0, tinyChainSpec), id: Bytes.fill(HASH_SIZE, 9), }), Ticket.create({ - attempt: tryAsTicketAttempt(0), + attempt: tryAsTicketAttempt(0, tinyChainSpec), id: Bytes.fill(HASH_SIZE, 10), }), Ticket.create({ - attempt: tryAsTicketAttempt(0), + attempt: tryAsTicketAttempt(0, tinyChainSpec), id: Bytes.fill(HASH_SIZE, 11), }), Ticket.create({ - attempt: tryAsTicketAttempt(0), + attempt: tryAsTicketAttempt(0, tinyChainSpec), id: Bytes.fill(HASH_SIZE, 12), }), ], diff --git a/packages/jam/state-json/dump.ts b/packages/jam/state-json/dump.ts index a8941fe8d..90e2202b2 100644 --- a/packages/jam/state-json/dump.ts +++ b/packages/jam/state-json/dump.ts @@ -68,8 +68,8 @@ export const fullStateDumpFromJson = (spec: ChainSpec) => beta: json.nullable(recentBlocksHistoryFromJson), gamma: { gamma_k: json.array(validatorDataFromJson), - gamma_a: json.array(ticketFromJson), - gamma_s: TicketsOrKeys.fromJson, + gamma_a: json.array(ticketFromJson(spec)), + gamma_s: TicketsOrKeys.fromJson(spec), gamma_z: json.fromString((v) => Bytes.parseBytes(v, BANDERSNATCH_RING_ROOT_BYTES).asOpaque()), }, psi: disputesRecordsFromJson, diff --git a/packages/jam/state-json/safrole.ts b/packages/jam/state-json/safrole.ts index ba614096c..a7fc70003 100644 --- a/packages/jam/state-json/safrole.ts +++ b/packages/jam/state-json/safrole.ts @@ -6,19 +6,22 @@ import type { BandersnatchKey } from "@typeberry/crypto"; import { type FromJson, json } from "@typeberry/json-parser"; import { type SafroleSealingKeys, SafroleSealingKeysData } from "@typeberry/state"; -export const ticketFromJson: FromJson = json.object( - { - id: fromJson.bytes32(), - attempt: fromJson.ticketAttempt, - }, - Ticket.create, -); +export const ticketFromJson = (spec: ChainSpec): FromJson => + json.object( + { + id: fromJson.bytes32(), + attempt: fromJson.ticketAttempt(spec), + }, + Ticket.create, + ); export class TicketsOrKeys { - static fromJson: FromJson = { - keys: json.optional(json.array(fromJson.bytes32())), - tickets: json.optional(json.array(ticketFromJson)), - }; + static fromJson(spec: ChainSpec): FromJson { + return { + keys: json.optional(json.array(fromJson.bytes32())), + tickets: json.optional(json.array(ticketFromJson(spec))), + }; + } keys?: BandersnatchKey[]; tickets?: Ticket[]; diff --git a/packages/jam/state-vectors/index.ts b/packages/jam/state-vectors/index.ts index fc462a87e..b5c981f78 100644 --- a/packages/jam/state-vectors/index.ts +++ b/packages/jam/state-vectors/index.ts @@ -2,7 +2,7 @@ import { Block, type BlockView, Header, type StateRootHash } from "@typeberry/bl import { blockViewFromJson, fromJson, headerFromJson } from "@typeberry/block-json"; import type { BytesBlob } from "@typeberry/bytes"; import { codec } from "@typeberry/codec"; -import { tinyChainSpec } from "@typeberry/config"; +import { type ChainSpec, tinyChainSpec } from "@typeberry/config"; import { HASH_SIZE, TRUNCATED_HASH_SIZE, type TruncatedHash } from "@typeberry/hash"; import { type FromJson, json } from "@typeberry/json-parser"; @@ -36,10 +36,12 @@ export class TestState { } export class StateTransitionGenesis { - static fromJson: FromJson = { - header: headerFromJson, - state: TestState.fromJson, - }; + static fromJson(spec: ChainSpec = tinyChainSpec): FromJson { + return { + header: headerFromJson(spec), + state: TestState.fromJson, + }; + } static Codec = codec.object({ header: Header.Codec, diff --git a/packages/jam/state/test.utils.ts b/packages/jam/state/test.utils.ts index a5dd831c0..02f9b9890 100644 --- a/packages/jam/state/test.utils.ts +++ b/packages/jam/state/test.utils.ts @@ -360,7 +360,7 @@ export const testState = (): InMemoryState => { const emptyHash = () => b32("0x0000000000000000000000000000000000000000000000000000000000000000"); const testAuth = () => b32("0x0b27478648cd19b4f812f897a26976ecf312eac28508b4368d0c63ea949c7cb0"); -const attempt = (x: number) => tryAsTicketAttempt(x); +const attempt = (x: number) => tryAsTicketAttempt(x, spec); const b32 = (s: string) => Bytes.parseBytes(s, HASH_SIZE).asOpaque(); const repeat = (len: number, factory: () => T) => Array.from({ length: len }, factory); diff --git a/packages/jam/transition/hasher.test.ts b/packages/jam/transition/hasher.test.ts index c7fc730a7..31ea93282 100644 --- a/packages/jam/transition/hasher.test.ts +++ b/packages/jam/transition/hasher.test.ts @@ -53,7 +53,7 @@ describe("TransitionHasher", () => { function prepareTickets() { return rawTickets.map(({ attempt, signature }) => SignedTicket.create({ - attempt: tryAsTicketAttempt(attempt), + attempt: tryAsTicketAttempt(attempt, tinyChainSpec), signature: Bytes.parseBytes(signature, BANDERSNATCH_PROOF_BYTES).asOpaque(), }), ); diff --git a/packages/workers/block-authorship/main.ts b/packages/workers/block-authorship/main.ts index c0af9a103..bab1faa0b 100644 --- a/packages/workers/block-authorship/main.ts +++ b/packages/workers/block-authorship/main.ts @@ -205,6 +205,7 @@ export async function main(config: Config, comms: GeneratorInternal) { validatorKeys, ticketEntropy, chainSpec.ticketsPerValidator, + chainSpec, ); if (ticketsResult.isError) { diff --git a/packages/workers/block-authorship/ticket-generator.test.ts b/packages/workers/block-authorship/ticket-generator.test.ts index e2fa2aa55..9f9031693 100644 --- a/packages/workers/block-authorship/ticket-generator.test.ts +++ b/packages/workers/block-authorship/ticket-generator.test.ts @@ -2,6 +2,7 @@ import assert from "node:assert"; import { afterEach, beforeEach, describe, it, mock } from "node:test"; import { type SignedTicket, tryAsTicketAttempt } from "@typeberry/block/tickets.js"; import { Bytes } from "@typeberry/bytes"; +import { tinyChainSpec } from "@typeberry/config"; import { BANDERSNATCH_KEY_BYTES, initWasm, SEED_SIZE } from "@typeberry/crypto"; import { HASH_SIZE } from "@typeberry/hash"; import bandersnatchVrf from "@typeberry/safrole/bandersnatch-vrf.js"; @@ -41,7 +42,7 @@ describe("Ticket Generator", () => { const tickets: SignedTicket[] = []; for (let attempt = 0; attempt < ticketsPerValidator; attempt++) { tickets.push({ - attempt: tryAsTicketAttempt(attempt), + attempt: tryAsTicketAttempt(attempt, tinyChainSpec), signature: Bytes.zero(784).asOpaque(), } as SignedTicket); } @@ -66,6 +67,7 @@ describe("Ticket Generator", () => { validatorKeys, MOCK_ENTROPY, ticketsPerValidator, + tinyChainSpec, ); assert.ok(result.isOk); @@ -83,21 +85,29 @@ describe("Ticket Generator", () => { validatorKeys, MOCK_ENTROPY, ticketsPerValidator, + tinyChainSpec, ); assert.ok(result.isOk); const tickets = result.ok; - assert.strictEqual(tickets[0].attempt, tryAsTicketAttempt(0)); - assert.strictEqual(tickets[1].attempt, tryAsTicketAttempt(1)); - assert.strictEqual(tickets[2].attempt, tryAsTicketAttempt(0)); - assert.strictEqual(tickets[3].attempt, tryAsTicketAttempt(1)); + assert.strictEqual(tickets[0].attempt, tryAsTicketAttempt(0, tinyChainSpec)); + assert.strictEqual(tickets[1].attempt, tryAsTicketAttempt(1, tinyChainSpec)); + assert.strictEqual(tickets[2].attempt, tryAsTicketAttempt(0, tinyChainSpec)); + assert.strictEqual(tickets[3].attempt, tryAsTicketAttempt(1, tinyChainSpec)); }); it("should return empty array for no validator keys", async () => { const ringKeys = createMockRingKeys(3); const ticketsPerValidator = 2; - const result = await generateTickets(MOCK_BANDERSNATCH, ringKeys, [], MOCK_ENTROPY, ticketsPerValidator); + const result = await generateTickets( + MOCK_BANDERSNATCH, + ringKeys, + [], + MOCK_ENTROPY, + ticketsPerValidator, + tinyChainSpec, + ); assert.ok(result.isOk); assert.strictEqual(result.ok.length, 0); @@ -121,6 +131,7 @@ describe("Ticket Generator", () => { validatorKeys, MOCK_ENTROPY, ticketsPerValidator, + tinyChainSpec, ); assert.ok(result.isOk); @@ -148,6 +159,7 @@ describe("Ticket Generator", () => { invalidValidatorKeys, MOCK_ENTROPY, ticketsPerValidator, + tinyChainSpec, ); assert.ok(result.isError); diff --git a/packages/workers/block-authorship/ticket-generator.ts b/packages/workers/block-authorship/ticket-generator.ts index 31c723b71..871ef51f7 100644 --- a/packages/workers/block-authorship/ticket-generator.ts +++ b/packages/workers/block-authorship/ticket-generator.ts @@ -1,5 +1,6 @@ import type { EntropyHash } from "@typeberry/block"; -import { type SignedTicket, tryAsTicketAttempt } from "@typeberry/block/tickets.js"; +import type { SignedTicket } from "@typeberry/block/tickets.js"; +import type { ChainSpec } from "@typeberry/config"; import type { BandersnatchKey, BandersnatchSecretSeed } from "@typeberry/crypto"; import { Logger } from "@typeberry/logger"; import bandersnatchVrf from "@typeberry/safrole/bandersnatch-vrf.js"; @@ -30,6 +31,7 @@ export async function generateTickets( validatorKeys: ValidatorKey[], entropy: EntropyHash, ticketsPerValidator: number, + chainSpec: ChainSpec, ): Promise> { const allTickets: SignedTicket[] = []; @@ -46,7 +48,8 @@ export async function generateTickets( proverIndex, validatorKey.secret, entropy, - tryAsTicketAttempt(ticketsPerValidator), + ticketsPerValidator, + chainSpec, ); if (ticketResult.isOk) { From f6a904a06b2c19f2698e43e7a6be968e867b0e26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Wed, 18 Feb 2026 18:05:41 +0100 Subject: [PATCH 2/2] Fix build --- package-lock.json | 458 ++++++------------ packages/jam/jamnp-s/network.ts | 2 +- .../jamnp-s/tasks/ticket-distribution.test.ts | 5 +- .../jam/jamnp-s/tasks/ticket-distribution.ts | 7 +- 4 files changed, 153 insertions(+), 319 deletions(-) diff --git a/package-lock.json b/package-lock.json index 627b6e47b..d2c7eeeee 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1048,9 +1048,9 @@ } }, "node_modules/@eslint-community/eslint-utils": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz", - "integrity": "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==", + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1131,9 +1131,9 @@ } }, "node_modules/@eslint/eslintrc": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", - "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.3.tgz", + "integrity": "sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1143,7 +1143,7 @@ "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", + "js-yaml": "^4.1.1", "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" }, @@ -1155,9 +1155,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.39.1", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.1.tgz", - "integrity": "sha512-S26Stp4zCy88tH94QbBv3XCuzRQiZ9yXofEILmglYTh/Ug/a9/umqvgFtYBAo3Lp0nsI/5/qH1CCrbdK3AP1Tw==", + "version": "9.39.2", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.2.tgz", + "integrity": "sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA==", "dev": true, "license": "MIT", "engines": { @@ -1646,44 +1646,6 @@ "url": "https://paulmillr.com/funding/" } }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, "node_modules/@opentelemetry/api": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", @@ -4082,21 +4044,20 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.46.4", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.46.4.tgz", - "integrity": "sha512-R48VhmTJqplNyDxCyqqVkFSZIx1qX6PzwqgcXn1olLrzxcSBDlOsbtcnQuQhNtnNiJ4Xe5gREI1foajYaYU2Vg==", + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.56.0.tgz", + "integrity": "sha512-lRyPDLzNCuae71A3t9NEINBiTn7swyOhvUj3MyUOxb8x6g6vPEFoOU+ZRmGMusNC3X3YMhqMIX7i8ShqhT74Pw==", "dev": true, "license": "MIT", "dependencies": { - "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.46.4", - "@typescript-eslint/type-utils": "8.46.4", - "@typescript-eslint/utils": "8.46.4", - "@typescript-eslint/visitor-keys": "8.46.4", - "graphemer": "^1.4.0", - "ignore": "^7.0.0", + "@eslint-community/regexpp": "^4.12.2", + "@typescript-eslint/scope-manager": "8.56.0", + "@typescript-eslint/type-utils": "8.56.0", + "@typescript-eslint/utils": "8.56.0", + "@typescript-eslint/visitor-keys": "8.56.0", + "ignore": "^7.0.5", "natural-compare": "^1.4.0", - "ts-api-utils": "^2.1.0" + "ts-api-utils": "^2.4.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4106,8 +4067,8 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.46.4", - "eslint": "^8.57.0 || ^9.0.0", + "@typescript-eslint/parser": "^8.56.0", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, @@ -4122,17 +4083,17 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.46.4", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.46.4.tgz", - "integrity": "sha512-tK3GPFWbirvNgsNKto+UmB/cRtn6TZfyw0D6IKrW55n6Vbs7KJoZtI//kpTKzE/DUmmnAFD8/Ca46s7Obs92/w==", + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.56.0.tgz", + "integrity": "sha512-IgSWvLobTDOjnaxAfDTIHaECbkNlAlKv2j5SjpB2v7QHKv1FIfjwMy8FsDbVfDX/KjmCmYICcw7uGaXLhtsLNg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.46.4", - "@typescript-eslint/types": "8.46.4", - "@typescript-eslint/typescript-estree": "8.46.4", - "@typescript-eslint/visitor-keys": "8.46.4", - "debug": "^4.3.4" + "@typescript-eslint/scope-manager": "8.56.0", + "@typescript-eslint/types": "8.56.0", + "@typescript-eslint/typescript-estree": "8.56.0", + "@typescript-eslint/visitor-keys": "8.56.0", + "debug": "^4.4.3" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4142,20 +4103,20 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.46.4", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.46.4.tgz", - "integrity": "sha512-nPiRSKuvtTN+no/2N1kt2tUh/HoFzeEgOm9fQ6XQk4/ApGqjx0zFIIaLJ6wooR1HIoozvj2j6vTi/1fgAz7UYQ==", + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.56.0.tgz", + "integrity": "sha512-M3rnyL1vIQOMeWxTWIW096/TtVP+8W3p/XnaFflhmcFp+U4zlxUxWj4XwNs6HbDeTtN4yun0GNTTDBw/SvufKg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.46.4", - "@typescript-eslint/types": "^8.46.4", - "debug": "^4.3.4" + "@typescript-eslint/tsconfig-utils": "^8.56.0", + "@typescript-eslint/types": "^8.56.0", + "debug": "^4.4.3" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4169,14 +4130,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.46.4", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.46.4.tgz", - "integrity": "sha512-tMDbLGXb1wC+McN1M6QeDx7P7c0UWO5z9CXqp7J8E+xGcJuUuevWKxuG8j41FoweS3+L41SkyKKkia16jpX7CA==", + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.56.0.tgz", + "integrity": "sha512-7UiO/XwMHquH+ZzfVCfUNkIXlp/yQjjnlYUyYz7pfvlK3/EyyN6BK+emDmGNyQLBtLGaYrTAI6KOw8tFucWL2w==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.46.4", - "@typescript-eslint/visitor-keys": "8.46.4" + "@typescript-eslint/types": "8.56.0", + "@typescript-eslint/visitor-keys": "8.56.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4187,9 +4148,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.46.4", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.46.4.tgz", - "integrity": "sha512-+/XqaZPIAk6Cjg7NWgSGe27X4zMGqrFqZ8atJsX3CWxH/jACqWnrWI68h7nHQld0y+k9eTTjb9r+KU4twLoo9A==", + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.56.0.tgz", + "integrity": "sha512-bSJoIIt4o3lKXD3xmDh9chZcjCz5Lk8xS7Rxn+6l5/pKrDpkCwtQNQQwZ2qRPk7TkUYhrq3WPIHXOXlbXP0itg==", "dev": true, "license": "MIT", "engines": { @@ -4204,17 +4165,17 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.46.4", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.46.4.tgz", - "integrity": "sha512-V4QC8h3fdT5Wro6vANk6eojqfbv5bpwHuMsBcJUJkqs2z5XnYhJzyz9Y02eUmF9u3PgXEUiOt4w4KHR3P+z0PQ==", + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.56.0.tgz", + "integrity": "sha512-qX2L3HWOU2nuDs6GzglBeuFXviDODreS58tLY/BALPC7iu3Fa+J7EOTwnX9PdNBxUI7Uh0ntP0YWGnxCkXzmfA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.46.4", - "@typescript-eslint/typescript-estree": "8.46.4", - "@typescript-eslint/utils": "8.46.4", - "debug": "^4.3.4", - "ts-api-utils": "^2.1.0" + "@typescript-eslint/types": "8.56.0", + "@typescript-eslint/typescript-estree": "8.56.0", + "@typescript-eslint/utils": "8.56.0", + "debug": "^4.4.3", + "ts-api-utils": "^2.4.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4224,14 +4185,14 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/types": { - "version": "8.46.4", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.46.4.tgz", - "integrity": "sha512-USjyxm3gQEePdUwJBFjjGNG18xY9A2grDVGuk7/9AkjIF1L+ZrVnwR5VAU5JXtUnBL/Nwt3H31KlRDaksnM7/w==", + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.56.0.tgz", + "integrity": "sha512-DBsLPs3GsWhX5HylbP9HNG15U0bnwut55Lx12bHB9MpXxQ+R5GC8MwQe+N1UFXxAeQDvEsEDY6ZYwX03K7Z6HQ==", "dev": true, "license": "MIT", "engines": { @@ -4243,22 +4204,21 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.46.4", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.46.4.tgz", - "integrity": "sha512-7oV2qEOr1d4NWNmpXLR35LvCfOkTNymY9oyW+lUHkmCno7aOmIf/hMaydnJBUTBMRCOGZh8YjkFOc8dadEoNGA==", + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.56.0.tgz", + "integrity": "sha512-ex1nTUMWrseMltXUHmR2GAQ4d+WjkZCT4f+4bVsps8QEdh0vlBsaCokKTPlnqBFqqGaxilDNJG7b8dolW2m43Q==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.46.4", - "@typescript-eslint/tsconfig-utils": "8.46.4", - "@typescript-eslint/types": "8.46.4", - "@typescript-eslint/visitor-keys": "8.46.4", - "debug": "^4.3.4", - "fast-glob": "^3.3.2", - "is-glob": "^4.0.3", - "minimatch": "^9.0.4", - "semver": "^7.6.0", - "ts-api-utils": "^2.1.0" + "@typescript-eslint/project-service": "8.56.0", + "@typescript-eslint/tsconfig-utils": "8.56.0", + "@typescript-eslint/types": "8.56.0", + "@typescript-eslint/visitor-keys": "8.56.0", + "debug": "^4.4.3", + "minimatch": "^9.0.5", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.4.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4298,16 +4258,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.46.4", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.46.4.tgz", - "integrity": "sha512-AbSv11fklGXV6T28dp2Me04Uw90R2iJ30g2bgLz529Koehrmkbs1r7paFqr1vPCZi7hHwYxYtxfyQMRC8QaVSg==", + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.56.0.tgz", + "integrity": "sha512-RZ3Qsmi2nFGsS+n+kjLAYDPVlrzf7UhTffrDIKr+h2yzAlYP/y5ZulU0yeDEPItos2Ph46JAL5P/On3pe7kDIQ==", "dev": true, "license": "MIT", "dependencies": { - "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.46.4", - "@typescript-eslint/types": "8.46.4", - "@typescript-eslint/typescript-estree": "8.46.4" + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.56.0", + "@typescript-eslint/types": "8.56.0", + "@typescript-eslint/typescript-estree": "8.56.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4317,19 +4277,19 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.46.4", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.46.4.tgz", - "integrity": "sha512-/++5CYLQqsO9HFGLI7APrxBJYo+5OCMpViuhV8q5/Qa3o5mMrF//eQHks+PXcsAVaLdn817fMuS7zqoXNNZGaw==", + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.56.0.tgz", + "integrity": "sha512-q+SL+b+05Ud6LbEE35qe4A99P+htKTKVbyiNEe45eCbJFyh/HVK9QXwlrbz+Q4L8SOW4roxSVwXYj4DMBT7Ieg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.46.4", - "eslint-visitor-keys": "^4.2.1" + "@typescript-eslint/types": "8.56.0", + "eslint-visitor-keys": "^5.0.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4339,6 +4299,19 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.0.tgz", + "integrity": "sha512-A0XeIi7CXU7nPlfHS9loMYEKxUaONu/hTEzHTGba9Huu94Cq1hPivf+DE5erJozZOky0LfvXAyrV/tcswpLI0Q==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, "node_modules/acorn": { "version": "8.15.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", @@ -4704,19 +4677,6 @@ "concat-map": "0.0.1" } }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dev": true, - "license": "MIT", - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/call-bind": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", @@ -5268,9 +5228,9 @@ } }, "node_modules/eslint": { - "version": "9.39.1", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.1.tgz", - "integrity": "sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==", + "version": "9.39.2", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.2.tgz", + "integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==", "dev": true, "license": "MIT", "dependencies": { @@ -5280,7 +5240,7 @@ "@eslint/config-helpers": "^0.4.2", "@eslint/core": "^0.17.0", "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.39.1", + "@eslint/js": "9.39.2", "@eslint/plugin-kit": "^0.4.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", @@ -5608,36 +5568,6 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "license": "MIT" }, - "node_modules/fast-glob": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", - "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.8" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/fast-glob/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", @@ -5652,14 +5582,22 @@ "dev": true, "license": "MIT" }, - "node_modules/fastq": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", - "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", "dev": true, - "license": "ISC", - "dependencies": { - "reusify": "^1.0.4" + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } } }, "node_modules/file-entry-cache": { @@ -5675,19 +5613,6 @@ "node": ">=16.0.0" } }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, - "license": "MIT", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/flat-cache": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", @@ -5990,13 +5915,6 @@ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "license": "ISC" }, - "node_modules/graphemer": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "dev": true, - "license": "MIT" - }, "node_modules/has-bigints": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", @@ -6408,16 +6326,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, "node_modules/is-number-object": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", @@ -6858,9 +6766,9 @@ "peer": true }, "node_modules/markdown-it": { - "version": "14.1.0", - "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz", - "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==", + "version": "14.1.1", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.1.tgz", + "integrity": "sha512-BuU2qnTti9YKgK5N+IeMubp14ZUKUUw7yeJbkjtosvHiP0AZ5c8IAgEMk79D0eC8F23r4Ac/q8cAIFdm2FtyoA==", "dev": true, "license": "MIT", "peer": true, @@ -6894,30 +6802,6 @@ "license": "MIT", "peer": true }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/micromatch": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "dev": true, - "license": "MIT", - "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, "node_modules/mimic-fn": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", @@ -7270,13 +7154,13 @@ } }, "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", "engines": { - "node": ">=8.6" + "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/jonschlinkert" @@ -7416,27 +7300,6 @@ "node": ">=6.0.0" } }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, "node_modules/reflect-metadata": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz", @@ -7566,17 +7429,6 @@ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "license": "ISC" }, - "node_modules/reusify": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", - "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", - "dev": true, - "license": "MIT", - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, "node_modules/rollup": { "version": "4.53.2", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.53.2.tgz", @@ -7618,30 +7470,6 @@ "fsevents": "~2.3.2" } }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, "node_modules/safe-array-concat": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", @@ -7698,9 +7526,9 @@ } }, "node_modules/semver": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", "dev": true, "license": "ISC", "bin": { @@ -8022,17 +7850,21 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", "dev": true, "license": "MIT", "dependencies": { - "is-number": "^7.0.0" + "fdir": "^6.5.0", + "picomatch": "^4.0.3" }, "engines": { - "node": ">=8.0" + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" } }, "node_modules/tr46": { @@ -8042,9 +7874,9 @@ "license": "MIT" }, "node_modules/ts-api-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", - "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.4.0.tgz", + "integrity": "sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==", "dev": true, "license": "MIT", "engines": { @@ -8304,16 +8136,16 @@ } }, "node_modules/typescript-eslint": { - "version": "8.46.4", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.46.4.tgz", - "integrity": "sha512-KALyxkpYV5Ix7UhvjTwJXZv76VWsHG+NjNlt/z+a17SOQSiOcBdUXdbJdyXi7RPxrBFECtFOiPwUJQusJuCqrg==", + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.56.0.tgz", + "integrity": "sha512-c7toRLrotJ9oixgdW7liukZpsnq5CZ7PuKztubGYlNppuTqhIoWfhgHo/7EU0v06gS2l/x0i2NEFK1qMIf0rIg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.46.4", - "@typescript-eslint/parser": "8.46.4", - "@typescript-eslint/typescript-estree": "8.46.4", - "@typescript-eslint/utils": "8.46.4" + "@typescript-eslint/eslint-plugin": "8.56.0", + "@typescript-eslint/parser": "8.56.0", + "@typescript-eslint/typescript-estree": "8.56.0", + "@typescript-eslint/utils": "8.56.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -8323,7 +8155,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, diff --git a/packages/jam/jamnp-s/network.ts b/packages/jam/jamnp-s/network.ts index 42ee07311..2fc34b519 100644 --- a/packages/jam/jamnp-s/network.ts +++ b/packages/jam/jamnp-s/network.ts @@ -41,7 +41,7 @@ export async function setup( // start the networking tasks const syncTask = SyncTask.start(spec, blake2b, streamManager, connections, blocks, onNewBlocks); - const ticketTask = TicketDistributionTask.start(streamManager, connections); + const ticketTask = TicketDistributionTask.start(streamManager, connections, spec); setImmediate(async () => { while (network.isRunning) { diff --git a/packages/jam/jamnp-s/tasks/ticket-distribution.test.ts b/packages/jam/jamnp-s/tasks/ticket-distribution.test.ts index 83550c013..1eb670304 100644 --- a/packages/jam/jamnp-s/tasks/ticket-distribution.test.ts +++ b/packages/jam/jamnp-s/tasks/ticket-distribution.test.ts @@ -4,6 +4,7 @@ import { setTimeout } from "node:timers/promises"; import { type Epoch, tryAsEpoch } from "@typeberry/block"; import { SignedTicket, tryAsTicketAttempt } from "@typeberry/block/tickets.js"; import { Bytes } from "@typeberry/bytes"; +import { tinyChainSpec } from "@typeberry/config"; import { BANDERSNATCH_PROOF_BYTES } from "@typeberry/crypto"; import { Logger } from "@typeberry/logger"; import { createTestPeerPair, MockNetwork } from "@typeberry/networking/testing.js"; @@ -23,7 +24,7 @@ function createTestTicket(attempt: number, signatureByte = 0): SignedTicket { signatureBytes.raw[0] = attempt; signatureBytes.raw[1] = signatureByte; return SignedTicket.create({ - attempt: tryAsTicketAttempt(attempt), + attempt: tryAsTicketAttempt(attempt, tinyChainSpec), signature: signatureBytes.asOpaque(), }); } @@ -38,7 +39,7 @@ describe("TicketDistributionTask", () => { const receivedTickets: { epochIndex: Epoch; ticket: SignedTicket }[] = []; // Use real TicketDistributionTask - const ticketTask = TicketDistributionTask.start(streamManager, connections); + const ticketTask = TicketDistributionTask.start(streamManager, connections, tinyChainSpec); // Intercept received tickets by wrapping onTicketReceived behavior // The task already adds received tickets to pending queue via addTicket, diff --git a/packages/jam/jamnp-s/tasks/ticket-distribution.ts b/packages/jam/jamnp-s/tasks/ticket-distribution.ts index 4025e3f06..5f3efece2 100644 --- a/packages/jam/jamnp-s/tasks/ticket-distribution.ts +++ b/packages/jam/jamnp-s/tasks/ticket-distribution.ts @@ -1,5 +1,6 @@ import type { Epoch } from "@typeberry/block"; import type { SignedTicket } from "@typeberry/block/tickets.js"; +import type { ChainSpec } from "@typeberry/config"; import { Logger } from "@typeberry/logger"; import { OK } from "@typeberry/utils"; import type { AuxData, Connections } from "../peers.js"; @@ -27,18 +28,18 @@ const TICKET_AUX: AuxData = { * and periodically distributed to peers that haven't received them yet. */ export class TicketDistributionTask { - static start(streamManager: StreamManager, connections: Connections) { + static start(streamManager: StreamManager, connections: Connections, chainSpec: ChainSpec) { const task = new TicketDistributionTask(streamManager, connections); // server mode: receive tickets from peers streamManager.registerIncomingHandlers( - new ce131.ServerHandler(ce131.STREAM_KIND_PROXY_TO_ALL, (epochIndex, ticket) => { + new ce131.ServerHandler(chainSpec, ce131.STREAM_KIND_PROXY_TO_ALL, (epochIndex, ticket) => { task.onTicketReceived(epochIndex, ticket); }), ); // client mode: send tickets to peers - streamManager.registerOutgoingHandlers(new ce131.ClientHandler(ce131.STREAM_KIND_PROXY_TO_ALL)); + streamManager.registerOutgoingHandlers(new ce131.ClientHandler(chainSpec, ce131.STREAM_KIND_PROXY_TO_ALL)); return task; }