From 2e080cc1281b9ba7fcab6eeded5b9d78ac1d8e67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Tue, 28 Oct 2025 16:52:44 +0100 Subject: [PATCH 01/15] Run STF vectors on both ananas and builtin PVM --- bin/test-runner/common.ts | 237 ++++++++++++------ .../state-transition/state-transition.ts | 19 +- bin/test-runner/w3f.ts | 5 +- bin/test-runner/w3f/accumulate.ts | 15 +- bin/test-runner/w3f/runners.ts | 14 +- bin/test-runner/w3f/spec.ts | 9 - packages/jam/config/chain-spec.ts | 5 + .../jam/transition/accumulate/accumulate.ts | 4 +- packages/jam/transition/chain-stf.ts | 2 +- 9 files changed, 197 insertions(+), 113 deletions(-) delete mode 100644 bin/test-runner/w3f/spec.ts diff --git a/bin/test-runner/common.ts b/bin/test-runner/common.ts index 5c072df0b..583756473 100644 --- a/bin/test-runner/common.ts +++ b/bin/test-runner/common.ts @@ -6,64 +6,102 @@ import path from "node:path"; import test, { type TestContext } from "node:test"; import util from "node:util"; import { type Decode, Decoder } from "@typeberry/codec"; -import { type ChainSpec, tinyChainSpec } from "@typeberry/config"; +import { tinyChainSpec, type ChainSpec } from "@typeberry/config"; import { initWasm } from "@typeberry/crypto"; import { type FromJson, parseFromJson } from "@typeberry/json-parser"; import { Level, Logger } from "@typeberry/logger"; +import {check} from "@typeberry/utils"; Logger.configureAll(process.env.JAM_LOG ?? "", Level.LOG); export const logger = Logger.new(import.meta.filename, "test-runner"); -export function runner( - name: string, - fromJson: FromJson, - run: (test: T, path: string, t: TestContext, chainSpec: ChainSpec) => Promise, - chainSpec: ChainSpec = tinyChainSpec, - fromCodec?: Decode, -): Runner { - return { name, fromJson, fromCodec, run, chainSpec } as Runner; +export function runner( + path: string, + parser: testFile.Kind, + run: RunFunction, + { + chainSpecs = [tinyChainSpec], + variants = [], + }: { + chainSpecs: ChainSpec[], + variants: V[], + } +): Runner { + check`${chainSpecs.length > 0} At least one chainspec missing in ${path} runner.`; + return { path, parser, run, variants, chainSpecs } as Runner; } -export type Runner = { - name: string; - fromJson: FromJson; - run: (test: T, path: string, t: TestContext, chainSpec: ChainSpec) => Promise; - fromCodec?: Decode; +export type RunOptions = { + test: TestContext; chainSpec: ChainSpec; + path: string; }; -enum TestFileKind { - Binary = 0, - JSON = 1, -} +export type RunFunction = ( + test: T, + variant: V, + options: RunOptions, +) => Promise; + +export type Runner = { + path: string; + parser: testFile.Kind; + run: RunFunction; + variants: V[]; + chainSpecs: ChainSpec[]; +}; + +export namespace testFile { + export const JSON = '.json'; + export type JSON = typeof JSON; + export const BIN = '.bin'; + export type BIN = typeof BIN; + + export type Kind = { + kind: JSON; + fromJson: FromJson; + } | { + kind: BIN; + codec: Decode; + }; -type TestFile = - | { - kind: TestFileKind.JSON; - content: unknown; + export type Content = + | { + kind: JSON; + content: string; } - | { - kind: TestFileKind.Binary; + | { + kind: BIN; content: Uint8Array; }; + + export function json(fromJson: FromJson): Kind { + return { kind: JSON, fromJson }; + } + + export function bin(codec: Decode): Kind { + return { kind: BIN, codec: codec }; + } +} + export async function main( - runners: Runner[], + runners: Runner[], initialFiles: string[], directoryToScan: string, { - pattern = ".json", + pattern = testFile.JSON, accepted, ignored, }: { - pattern?: string; + pattern?: testFile.JSON | testFile.BIN; accepted?: string[]; ignored?: string[]; } = {}, ) { await initWasm(); const relPath = `${import.meta.dirname}/../..`; - const tests: TestAndRunner[] = []; + const tests: TestAndRunner[] = []; const ignoredPatterns = ignored ?? []; let testFiles = initialFiles; @@ -73,38 +111,38 @@ export async function main( } logger.info`Preparing tests for ${testFiles.length} files.`; - for (const testFile of testFiles) { - const absolutePath = path.resolve(`${relPath}/${testFile}`); + for (const testFilePath of testFiles) { + const absolutePath = path.resolve(`${relPath}/${testFilePath}`); if (ignoredPatterns.some((x) => absolutePath.includes(x))) { logger.log`Ignoring: ${absolutePath}`; continue; } - let testFileContent: TestFile; - if (absolutePath.endsWith(".bin")) { + let testFileContent: testFile.Content; + if (absolutePath.endsWith(testFile.BIN)) { const content: Buffer = await fs.readFile(absolutePath); testFileContent = { - kind: TestFileKind.Binary, + kind: '.bin', content: new Uint8Array(content), }; } else { const content = await fs.readFile(absolutePath, "utf8"); testFileContent = { - kind: TestFileKind.JSON, - content: JSON.parse(content), + kind: '.json', + content, }; } - const test = prepareTest(runners, testFileContent, testFile, absolutePath); - - test.shouldSkip = accepted !== undefined && !accepted.some((x) => absolutePath.includes(x)); - - tests.push(test); + const testVariants = prepareTest(runners, testFileContent, testFilePath, absolutePath); + for (const test of testVariants) { + test.shouldSkip = accepted !== undefined && !accepted.some((x) => absolutePath.includes(x)); + tests.push(test); + } } // aggregate the tests by their runner. - const aggregated = new Map(); + const aggregated = new Map[]>(); for (const test of tests) { const sameRunner = aggregated.get(test.runner) ?? []; sameRunner.push(test); @@ -136,10 +174,11 @@ export async function main( const runnersBatch = testRunners.slice(i * batchSize, (i + 1) * batchSize); for (const runner of runnersBatch) { const fileName = runner.file.replace(pathToReplace, ""); + const testCase = runner.variant !== null ? `[${runner.variant}] ${fileName}` : fileName; if (runner.shouldSkip) { - test.it.skip(fileName, runner.test); + test.it.skip(testCase, runner.test); } else { - test.it(fileName, { timeout }, runner.test); + test.it(testCase, { timeout }, runner.test); } } }, @@ -163,67 +202,103 @@ async function scanDir(relPath: string, dir: string, filePattern: string): Promi } } -type TestAndRunner = { +type TestAndRunner = { shouldSkip: boolean; runner: string; file: string; + variant: V; test: (ctx: TestContext) => Promise; }; -function prepareTest(runners: Runner[], testContent: TestFile, file: string, path: string): TestAndRunner { +function prepareTest( + runners: Runner[], + testContent: testFile.Content, + fileName: string, + fullPath: string +): TestAndRunner[] { const errors: [string, unknown][] = []; const handleError = (name: string, e: unknown) => errors.push([name, e]); + // NOTE This is not safe, but if the test does not specify + // variants it means it doesn't care about them. + const noneVariant = '' as V; // Find the first runner that is able to parse the input data. - for (const { name, fromJson, fromCodec, run, chainSpec } of runners) { - // NOTE: this `if` statement is needed to distinguish between tiny and full chain spec - // without this `if` some tests (for example statistics) will be run twice - if (!name.split("/").every((pathPart) => path.includes(pathPart))) { + for (const { path, parser, run, variants, chainSpecs } of runners) { + // NOTE: this `if` statement is intended to speed up parsing of the test files + // instead of trying each and every runner, we make sure that the absolute + // path to the file includes each part of our "test path" definition. + if (!path.split("/").every((pathPart) => fullPath.includes(pathPart))) { continue; } - const createTestDefinition = (parsedTest: unknown): TestAndRunner => ({ - shouldSkip: false, - runner: name, - file, - test: (ctx) => { - logger.log`[${name}] running test from ${file}`; - logger.trace` ${util.inspect(parsedTest)}`; - return run(parsedTest, path, ctx, chainSpec); - }, - }); + for (const chainSpec of chainSpecs) { + if (parser.kind === testFile.BIN && testContent.kind === testFile.BIN) { + try { + const parsedTest = Decoder.decodeObject(parser.codec, testContent.content, chainSpec); + return createTestDefinitions(path, run, variants, parsedTest, chainSpec); + } catch (e) { + handleError(path, e); + } + } - if (testContent.kind === TestFileKind.Binary) { - if (fromCodec !== undefined) { + if (parser.kind === testFile.JSON && testContent.kind === testFile.JSON) { try { - const parsedTest = Decoder.decodeObject(fromCodec, testContent.content, chainSpec); - return createTestDefinition(parsedTest); + const parsedTest = parseFromJson(testContent.content, parser.fromJson); + return createTestDefinitions(path, run, variants, parsedTest, chainSpec); } catch (e) { - handleError(name, e); + handleError(path, e); } - } else { - handleError(name, new Error(`Missing codec definition for: ${name}`)); } - } else { - try { - const parsedTest = parseFromJson(testContent.content, fromJson); - return createTestDefinition(parsedTest); - } catch (e) { - handleError(name, e); + + if (testContent.kind !== parser.kind) { + handleError(path, new Error(`Mismatching parser: got ${parser.kind}, need: ${testContent.kind}`)); } } } - return { - shouldSkip: false, - runner: "Invalid", - file, - test: () => { - for (const [runner, error] of errors) { - logger.error`[${runner}] Parsing error: ${error}`; - } + return [ + { + shouldSkip: false, + runner: "Invalid", + file: fileName, + variant: noneVariant, + test: () => { + for (const [runner, error] of errors) { + logger.error`[${runner}] Parsing error: ${error}`; + } - fail(`Unrecognized test case in ${file}`); + fail(`Unrecognized test case in ${fileName}`); + }, }, + ]; + + function createTestDefinitions( + path: string, + run: RunFunction, + variants: V[], + parsedTest: T, + chainSpec: ChainSpec + ) { + const results: TestAndRunner[] = []; + const possibleVariants: V[] = variants.length === 0 ? [noneVariant] : variants; + + for (const variant of possibleVariants) { + results.push({ + shouldSkip: false, + runner: path, + file: fileName, + variant, + test: (ctx) => { + logger.log`[${path}:${variant}] running test from ${fileName} (spec: ${chainSpec.name})`; + logger.trace` ${util.inspect(parsedTest, true, 2)}`; + return run(parsedTest, variant, { + test: ctx, + path: fullPath, + chainSpec, + }); + }, + }); + } + return results; }; } diff --git a/bin/test-runner/state-transition/state-transition.ts b/bin/test-runner/state-transition/state-transition.ts index 47f11e3f3..5b2e8e677 100644 --- a/bin/test-runner/state-transition/state-transition.ts +++ b/bin/test-runner/state-transition/state-transition.ts @@ -1,7 +1,6 @@ import assert from "node:assert"; import fs from "node:fs"; import path from "node:path"; -import type { TestContext } from "node:test"; import { Block, emptyBlock, Header } from "@typeberry/block"; import { blockFromJson, headerFromJson } from "@typeberry/block-json"; import { codec, Decoder, Encoder } from "@typeberry/codec"; @@ -16,6 +15,7 @@ import { BlockVerifier } from "@typeberry/transition/block-verifier.js"; import { OnChain } from "@typeberry/transition/chain-stf.js"; import { deepEqual, resultToString } from "@typeberry/utils"; import { loadState, TestState } from "./state-loader.js"; +import {RunOptions} from "../common.js"; export class StateTransitionGenesis { static fromJson: FromJson = { @@ -95,22 +95,23 @@ function blockAsView(spec: ChainSpec, block: Block) { // that were run pre-V1 fuzzer version. const jamConformance070V0Spec = new ChainSpec({ ...tinyChainSpec, + name: 'jam-conformance-v070v0', maxLookupAnchorAge: tryAsU32(14_400), }); export async function runStateTransition( testContent: StateTransition, - testPath: string, - t: TestContext, - chainSpec: ChainSpec, + variant: "ananas" | "builtin", + options: RunOptions, ) { + const pvm = variant === "ananas" ? PvmBackend.Ananas : PvmBackend.BuiltIn; const blake2b = await Blake2b.createHasher(); // a bit of a hack, but the new value for `maxLookupAnchorAge` was proposed with V1 // version of the fuzzer, yet these tests were still depending on the older value. // To simplify the chain spec, we just special case this one vector here. - const spec = testPath.includes("fuzz-reports/0.7.0/traces/1756548916/00000082.json") + const spec = options.path.includes("fuzz-reports/0.7.0/traces/1756548916/00000082.json") ? jamConformance070V0Spec - : chainSpec; + : options.chainSpec; const preState = loadState(spec, blake2b, testContent.pre_state.keyvals); const postState = loadState(spec, blake2b, testContent.post_state.keyvals); @@ -118,7 +119,7 @@ export async function runStateTransition( const postStateRoot = postState.backend.getRootHash(blake2b); const blockView = blockAsView(spec, testContent.block); - const allBlocks = loadBlocks(testPath, spec); + const allBlocks = loadBlocks(options.path, spec); const myBlockIndex = allBlocks.findIndex( ({ header }) => header.timeSlotIndex === testContent.block.header.timeSlotIndex, ); @@ -134,7 +135,7 @@ export async function runStateTransition( }), ); - const stf = new OnChain(spec, preState, blocksDb, hasher, PvmBackend.BuiltIn); + const stf = new OnChain(spec, preState, blocksDb, hasher, pvm); // verify that we compute the state root exactly the same. assert.deepStrictEqual(testContent.pre_state.state_root.toString(), preStateRoot.toString()); @@ -166,7 +167,7 @@ export async function runStateTransition( // some conformance test vectors have an empty state, we run them, yet do not perform any assertions. if (testContent.post_state.keyvals.length === 0) { - t.skip(`Successfuly run a test vector with empty post state!. Please verify: ${testPath}`); + options.test.skip(`Successfuly run a test vector with empty post state!. Please verify: ${options.path}`); return; } diff --git a/bin/test-runner/w3f.ts b/bin/test-runner/w3f.ts index 245c2e61f..2a0426c6e 100644 --- a/bin/test-runner/w3f.ts +++ b/bin/test-runner/w3f.ts @@ -3,7 +3,10 @@ import { runners } from "./w3f/runners.js"; main(runners, process.argv.slice(2), "test-vectors/w3f-fluffy", { ignored: [ - "traces/", + "genesis.json", + // Ignored - not working correctly in 0.6.7 and we ditched fixing them. + "traces/preimages_light/00000070.json", + "traces/preimages_light/00000073.json", // TODO [ToDr] Erasure coding test vectors need to be updated to GP 0.7.0 "erasure/", // Ignored due to incorrect expected gas diff --git a/bin/test-runner/w3f/accumulate.ts b/bin/test-runner/w3f/accumulate.ts index 85e0176b6..53aae467c 100644 --- a/bin/test-runner/w3f/accumulate.ts +++ b/bin/test-runner/w3f/accumulate.ts @@ -27,7 +27,7 @@ import { JsonService } from "@typeberry/state-json/accounts.js"; import { AccumulateOutput } from "@typeberry/transition/accumulate/accumulate-output.js"; import { Accumulate, type AccumulateRoot } from "@typeberry/transition/accumulate/index.js"; import { Compatibility, deepEqual, GpVersion, Result } from "@typeberry/utils"; -import { getChainSpec } from "./spec.js"; +import {RunOptions} from "../common.js"; class Input { static fromJson: FromJson = { @@ -141,14 +141,13 @@ export class AccumulateTest { post_state!: TestState; } -export async function runAccumulateTest(test: AccumulateTest, path: string) { - await runAccumulateInternal(test, path, PvmBackend.BuiltIn); - // TODO [ToDr] Make them run separately and fix them. - // await runAccumulateInternal(test, path, PvmBackend.Ananas); -} -async function runAccumulateInternal(test: AccumulateTest, path: string, pvm: PvmBackend) { - const chainSpec = getChainSpec(path); +export async function runAccumulateTest( + test: AccumulateTest, + variant: "ananas" | "builtin", + { chainSpec }: RunOptions, +) { + const pvm = variant === "ananas" ? PvmBackend.Ananas : PvmBackend.BuiltIn; /** * entropy has to be moved to input because state is incompatibile - * in test state we have: `entropy: EntropyHash;` diff --git a/bin/test-runner/w3f/runners.ts b/bin/test-runner/w3f/runners.ts index c69bf7f63..89cab4544 100644 --- a/bin/test-runner/w3f/runners.ts +++ b/bin/test-runner/w3f/runners.ts @@ -12,7 +12,7 @@ import { workResultFromJson, } from "@typeberry/block-json"; import { fullChainSpec, tinyChainSpec } from "@typeberry/config"; -import { runner } from "../common.js"; +import { runner, testFile } from "../common.js"; import { runStateTransition, StateTransition } from "../state-transition/state-transition.js"; import { AccumulateTest, runAccumulateTest } from "./accumulate.js"; import { AssurancesTestFull, AssurancesTestTiny, runAssurancesTestFull, runAssurancesTestTiny } from "./assurances.js"; @@ -48,8 +48,17 @@ import { runShufflingTests, shufflingTests } from "./shuffling.js"; import { runStatisticsTestFull, runStatisticsTestTiny, StatisticsTestFull, StatisticsTestTiny } from "./statistics.js"; import { runTrieTest, trieTestSuiteFromJson } from "./trie.js"; +const pvmVariants: ("ananas" | "builtin")[] = ["ananas", "builtin"]; + export const runners = [ - runner("accumulate", AccumulateTest.fromJson, runAccumulateTest), + runner("accumulate/tiny", testFile.json(AccumulateTest.fromJson), runAccumulateTest, { + chainSpecs: [tinyChainSpec], + variants: pvmVariants, + }), + runner("accumulate/full", AccumulateTest.fromJson, runAccumulateTest, { + chainSpecs: [tinyChainSpec], + variants: pvmVariants, + }), runner("assurances/tiny", AssurancesTestTiny.fromJson, runAssurancesTestTiny, tinyChainSpec), runner("assurances/full", AssurancesTestFull.fromJson, runAssurancesTestFull, fullChainSpec), runner("authorizations", AuthorizationsTest.fromJson, runAuthorizationsTest), @@ -73,6 +82,7 @@ export const runners = [ runner("statistics/full", StatisticsTestFull.fromJson, runStatisticsTestFull, fullChainSpec), runner("trie", trieTestSuiteFromJson, runTrieTest), runner("traces", StateTransition.fromJson, runStateTransition, tinyChainSpec, StateTransition.Codec), + runner("traces", StateTransition.fromJson, runStateTransition, ["ananas", "builtin"]), ]; function codecRunners(flavor: "tiny" | "full") { diff --git a/bin/test-runner/w3f/spec.ts b/bin/test-runner/w3f/spec.ts deleted file mode 100644 index 3e5fc1eed..000000000 --- a/bin/test-runner/w3f/spec.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { fullChainSpec, tinyChainSpec } from "@typeberry/config"; - -export function getChainSpec(path: string) { - if (path.includes("tiny")) { - return tinyChainSpec; - } - - return fullChainSpec; -} diff --git a/packages/jam/config/chain-spec.ts b/packages/jam/config/chain-spec.ts index b75d6a928..0ed358053 100644 --- a/packages/jam/config/chain-spec.ts +++ b/packages/jam/config/chain-spec.ts @@ -39,6 +39,8 @@ export const EC_SEGMENT_SIZE = 4104; * Additional data that has to be passed to the codec to correctly parse incoming bytes. */ export class ChainSpec extends WithDebug { + /** Human-readable name of the chain spec. */ + readonly name: string; /** Number of validators. */ readonly validatorsCount: U16; /** 1/3 of number of validators */ @@ -83,6 +85,7 @@ export class ChainSpec extends WithDebug { constructor(data: Omit) { super(); + this.name = data.name; this.validatorsCount = data.validatorsCount; this.thirdOfValidators = tryAsU16(Math.floor(data.validatorsCount / 3)); this.validatorsSuperMajority = tryAsU16(Math.floor(data.validatorsCount / 3) * 2 + 1); @@ -104,6 +107,7 @@ export class ChainSpec extends WithDebug { /** Set of values for "tiny" chain as defined in JAM test vectors. */ export const tinyChainSpec = new ChainSpec({ + name: 'tiny', validatorsCount: tryAsU16(6), coresCount: tryAsU16(2), epochLength: tryAsU32(12), @@ -126,6 +130,7 @@ export const tinyChainSpec = new ChainSpec({ * Please note that only validatorsCount and epochLength are "full", the rest is copied from "tiny". */ export const fullChainSpec = new ChainSpec({ + name: 'full', validatorsCount: tryAsU16(1023), coresCount: tryAsU16(341), epochLength: tryAsU32(600), diff --git a/packages/jam/transition/accumulate/accumulate.ts b/packages/jam/transition/accumulate/accumulate.ts index f485fef0e..bbb8994fd 100644 --- a/packages/jam/transition/accumulate/accumulate.ts +++ b/packages/jam/transition/accumulate/accumulate.ts @@ -11,7 +11,7 @@ import type { WorkReport } from "@typeberry/block/work-report.js"; import { Bytes } from "@typeberry/bytes"; import { codec, Encoder } from "@typeberry/codec"; import { ArrayView, HashSet, SortedArray } from "@typeberry/collections"; -import type { ChainSpec, PvmBackend } from "@typeberry/config"; +import { type ChainSpec, PvmBackend } from "@typeberry/config"; import { type Blake2b, HASH_SIZE } from "@typeberry/hash"; import type { PendingTransfer } from "@typeberry/jam-host-calls"; import { @@ -235,7 +235,7 @@ export class Accumulate { entropy: EntropyHash, inputStateUpdate: AccumulationStateUpdate, ) { - logger.log`Accumulating service ${serviceId}, transfers: ${transfers.length} operands: ${operands.length} at slot: ${slot}.`; + logger.log`Accumulating service ${serviceId}, transfers: ${transfers.length} operands: ${operands.length} at slot: ${slot} (PVM: ${PvmBackend[this.pvm]})`; const updatedState = new PartiallyUpdatedState(this.state, inputStateUpdate); diff --git a/packages/jam/transition/chain-stf.ts b/packages/jam/transition/chain-stf.ts index 4236cf501..8edfd45a4 100644 --- a/packages/jam/transition/chain-stf.ts +++ b/packages/jam/transition/chain-stf.ts @@ -302,7 +302,7 @@ export class OnChain { const { preimages, ...preimagesRest } = preimagesResult.ok; assertEmpty(preimagesRest); - const timerAccumulate = measure("import:accumulate"); + const timerAccumulate = measure(`import:accumulate (${PvmBackend[this.accumulate.pvm]})`); // accumulate const accumulateResult = await this.accumulate.transition({ slot: timeSlot, From 85df2e9f6c8dfd182c327c55f8fbe7fa4cef0629 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Mon, 27 Oct 2025 22:21:05 +0100 Subject: [PATCH 02/15] Clean up some todos. --- packages/core/numbers/index.ts | 6 +----- .../pvm-debugger-adapter/debugger-adapter.ts | 5 ----- packages/core/utils/opaque.ts | 3 +-- .../externalities/partial-state.ts | 7 ------- packages/jam/safrole/bandersnatch-vrf.ts | 2 +- .../serialize-state-update.ts | 1 - .../jam/transition/accumulate/accumulate.ts | 4 ++-- packages/jam/transition/hasher.ts | 17 ++--------------- packages/jam/transition/preimages.ts | 1 - 9 files changed, 7 insertions(+), 39 deletions(-) diff --git a/packages/core/numbers/index.ts b/packages/core/numbers/index.ts index e81540b63..6384f83cb 100644 --- a/packages/core/numbers/index.ts +++ b/packages/core/numbers/index.ts @@ -1,10 +1,6 @@ import { check } from "@typeberry/utils"; -/** - * TODO [ToDr] This should be `unique symbol`, but for some reason - * I can't figure out how to build `@typeberry/blocks` package. - */ -declare const __REPRESENTATION_BYTES__: "REPRESENTATION_BYTES"; +declare const __REPRESENTATION_BYTES__: unique symbol; type WithBytesRepresentation = { readonly [__REPRESENTATION_BYTES__]: Bytes; diff --git a/packages/core/pvm-debugger-adapter/debugger-adapter.ts b/packages/core/pvm-debugger-adapter/debugger-adapter.ts index 22f6c6eb1..85a75ccaa 100644 --- a/packages/core/pvm-debugger-adapter/debugger-adapter.ts +++ b/packages/core/pvm-debugger-adapter/debugger-adapter.ts @@ -11,11 +11,6 @@ export class DebuggerAdapter { this.pvm = new Interpreter({ useSbrkGas }); } - // TODO [MaSi]: a temporary solution that is needed to implement host calls in PVM debugger - getInterpreter() { - return this.pvm; - } - resetGeneric(rawProgram: Uint8Array, flatRegisters: Uint8Array, initialGas: bigint) { this.pvm.resetGeneric(rawProgram, 0, tryAsGas(initialGas), new Registers(flatRegisters)); } diff --git a/packages/core/utils/opaque.ts b/packages/core/utils/opaque.ts index 1dce6c406..dba06a99f 100644 --- a/packages/core/utils/opaque.ts +++ b/packages/core/utils/opaque.ts @@ -21,8 +21,7 @@ type Uninstantiable = void & { __brand: "uninstantiable" }; type StringLiteral = Type extends string ? (string extends Type ? never : Type) : never; -// TODO [MaSi]: it should be "unique symbol" but in debugger adapter we have opaque types from different packages and it is problematic. -declare const __OPAQUE_TYPE__ = "__INTERNAL_OPAQUE_ID__"; +declare const __OPAQUE_TYPE__: unique symbol; export type WithOpaque = { readonly [__OPAQUE_TYPE__]: Token; diff --git a/packages/jam/jam-host-calls/externalities/partial-state.ts b/packages/jam/jam-host-calls/externalities/partial-state.ts index f7cc0a7b5..49470e078 100644 --- a/packages/jam/jam-host-calls/externalities/partial-state.ts +++ b/packages/jam/jam-host-calls/externalities/partial-state.ts @@ -105,13 +105,6 @@ export enum ForgetPreimageError { /** * Errors that may occur when the transfer is invoked. - * - * TODO [ToDr] Since I don't fully understand yet which of these - * could be checked directly in the host call (i.e. if we will - * have access to the service account state there) for now I keep - * them safely in the `AccumulationPartialState` implementation. - * However, if possible, these should be moved directly to the - * host call implementation. */ export enum TransferError { /** The destination service does not exist. */ diff --git a/packages/jam/safrole/bandersnatch-vrf.ts b/packages/jam/safrole/bandersnatch-vrf.ts index b76316a36..4c79d34fc 100644 --- a/packages/jam/safrole/bandersnatch-vrf.ts +++ b/packages/jam/safrole/bandersnatch-vrf.ts @@ -33,7 +33,7 @@ type CacheEntry = { value: Promise>; }; -// TODO [ToDr] We export the entire object to allow mocking in tests. +// NOTE [ToDr] We export the entire object to allow mocking in tests. // Ideally we would just export functions and figure out how to mock // properly in ESM. export default { diff --git a/packages/jam/state-merkleization/serialize-state-update.ts b/packages/jam/state-merkleization/serialize-state-update.ts index 3ae6743b4..4b1d11823 100644 --- a/packages/jam/state-merkleization/serialize-state-update.ts +++ b/packages/jam/state-merkleization/serialize-state-update.ts @@ -54,7 +54,6 @@ function* serializeRemovedServices(servicesRemoved: ServiceId[] | undefined): Ge return; } for (const serviceId of servicesRemoved) { - // TODO [ToDr] what about all data associated with a service? const codec = serialize.serviceData(serviceId); yield [StateEntryUpdateAction.Remove, codec.key, EMPTY_BLOB]; } diff --git a/packages/jam/transition/accumulate/accumulate.ts b/packages/jam/transition/accumulate/accumulate.ts index bbb8994fd..8e793be72 100644 --- a/packages/jam/transition/accumulate/accumulate.ts +++ b/packages/jam/transition/accumulate/accumulate.ts @@ -11,7 +11,7 @@ import type { WorkReport } from "@typeberry/block/work-report.js"; import { Bytes } from "@typeberry/bytes"; import { codec, Encoder } from "@typeberry/codec"; import { ArrayView, HashSet, SortedArray } from "@typeberry/collections"; -import { type ChainSpec, PvmBackend } from "@typeberry/config"; +import type { ChainSpec, PvmBackend } from "@typeberry/config"; import { type Blake2b, HASH_SIZE } from "@typeberry/hash"; import type { PendingTransfer } from "@typeberry/jam-host-calls"; import { @@ -235,7 +235,7 @@ export class Accumulate { entropy: EntropyHash, inputStateUpdate: AccumulationStateUpdate, ) { - logger.log`Accumulating service ${serviceId}, transfers: ${transfers.length} operands: ${operands.length} at slot: ${slot} (PVM: ${PvmBackend[this.pvm]})`; + logger.log`Accumulating service ${serviceId}, transfers: ${transfers.length} operands: ${operands.length} at slot: ${slot}`; const updatedState = new PartiallyUpdatedState(this.state, inputStateUpdate); diff --git a/packages/jam/transition/hasher.ts b/packages/jam/transition/hasher.ts index 39592c7df..e8c83fb6f 100644 --- a/packages/jam/transition/hasher.ts +++ b/packages/jam/transition/hasher.ts @@ -1,10 +1,8 @@ import type { ExtrinsicHash, ExtrinsicView, HeaderHash, HeaderView, WorkReportHash } from "@typeberry/block"; -import type { WorkPackageHash } from "@typeberry/block/refine-context.js"; -import { WorkPackage } from "@typeberry/block/work-package.js"; import { BytesBlob } from "@typeberry/bytes"; -import { type Codec, codec, Encoder } from "@typeberry/codec"; +import { codec, Encoder } from "@typeberry/codec"; import type { ChainSpec } from "@typeberry/config"; -import { type Blake2b, type KeccakHash, keccak, type OpaqueHash, WithHash, WithHashAndBytes } from "@typeberry/hash"; +import { type Blake2b, type KeccakHash, keccak, WithHash, WithHashAndBytes } from "@typeberry/hash"; import type { MmrHasher } from "@typeberry/mmr"; import { tryAsU32 } from "@typeberry/numbers"; @@ -63,15 +61,4 @@ export class TransitionHasher implements MmrHasher { return new WithHashAndBytes(this.blake2b.hashBytes(encoded).asOpaque(), extrinsicView, encoded); } - - /** Creates hash for given WorkPackage */ - workPackage(workPackage: WorkPackage): WithHashAndBytes { - return this.encode(WorkPackage.Codec, workPackage); - } - - private encode(codec: Codec, data: T): WithHashAndBytes { - // TODO [ToDr] Use already allocated encoding destination and hash bytes from some arena. - const encoded = Encoder.encodeObject(codec, data, this.context); - return new WithHashAndBytes(this.blake2b.hashBytes(encoded).asOpaque(), data, encoded); - } } diff --git a/packages/jam/transition/preimages.ts b/packages/jam/transition/preimages.ts index 1d331c57a..64568b37c 100644 --- a/packages/jam/transition/preimages.ts +++ b/packages/jam/transition/preimages.ts @@ -20,7 +20,6 @@ export enum PreimagesErrorCode { AccountNotFound = "account_not_found", } -// TODO [SeKo] consider whether this module is the right place to remove expired preimages export class Preimages { constructor( public readonly state: PreimagesState, From eebec3bc407e564587a19ec47c0f1ee251006efb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Mon, 27 Oct 2025 22:26:29 +0100 Subject: [PATCH 03/15] Change default PVM backend to ananas. --- packages/jam/config-node/node-config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/jam/config-node/node-config.ts b/packages/jam/config-node/node-config.ts index d60ca9cf4..bf51c2847 100644 --- a/packages/jam/config-node/node-config.ts +++ b/packages/jam/config-node/node-config.ts @@ -19,7 +19,7 @@ export const DEFAULT_CONFIG = "default"; export const NODE_DEFAULTS = { name: isBrowser() ? "browser" : os.hostname(), config: DEFAULT_CONFIG, - pvm: PvmBackend.BuiltIn, + pvm: PvmBackend.Ananas, }; /** Chain spec chooser. */ From f6067d1dd4659195af30874b984f53f9ebc5f58e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Tue, 28 Oct 2025 22:02:33 +0100 Subject: [PATCH 04/15] Refactor test-runner definitions to support binary & variants. --- bin/test-runner/common.ts | 215 +++++++++++------- bin/test-runner/jamduna-067.ts | 7 +- bin/test-runner/javajam.ts | 7 +- .../state-transition/state-transition.ts | 6 +- bin/test-runner/w3f/accumulate.ts | 5 +- bin/test-runner/w3f/assurances.ts | 16 +- bin/test-runner/w3f/authorizations.ts | 6 +- bin/test-runner/w3f/codec/index.ts | 23 +- bin/test-runner/w3f/codec/work-item.ts | 5 +- bin/test-runner/w3f/codec/work-package.ts | 3 +- bin/test-runner/w3f/disputes.ts | 5 +- bin/test-runner/w3f/erasure-coding.ts | 6 +- bin/test-runner/w3f/host-calls-accumulate.ts | 109 --------- bin/test-runner/w3f/host-calls-general.ts | 92 -------- bin/test-runner/w3f/host-calls-refine.ts | 62 ----- bin/test-runner/w3f/reports.ts | 13 +- bin/test-runner/w3f/runners.ts | 100 ++++---- bin/test-runner/w3f/safrole.ts | 5 +- bin/test-runner/w3f/shuffling.ts | 2 +- packages/jam/config/chain-spec.ts | 4 +- packages/jam/transition/chain-stf.ts | 2 +- 21 files changed, 230 insertions(+), 463 deletions(-) delete mode 100644 bin/test-runner/w3f/host-calls-accumulate.ts delete mode 100644 bin/test-runner/w3f/host-calls-general.ts delete mode 100644 bin/test-runner/w3f/host-calls-refine.ts diff --git a/bin/test-runner/common.ts b/bin/test-runner/common.ts index 583756473..543d175e6 100644 --- a/bin/test-runner/common.ts +++ b/bin/test-runner/common.ts @@ -6,29 +6,67 @@ import path from "node:path"; import test, { type TestContext } from "node:test"; import util from "node:util"; import { type Decode, Decoder } from "@typeberry/codec"; -import { tinyChainSpec, type ChainSpec } from "@typeberry/config"; +import { type ChainSpec, tinyChainSpec } from "@typeberry/config"; import { initWasm } from "@typeberry/crypto"; import { type FromJson, parseFromJson } from "@typeberry/json-parser"; import { Level, Logger } from "@typeberry/logger"; -import {check} from "@typeberry/utils"; Logger.configureAll(process.env.JAM_LOG ?? "", Level.LOG); export const logger = Logger.new(import.meta.filename, "test-runner"); -export function runner( - path: string, - parser: testFile.Kind, - run: RunFunction, - { - chainSpecs = [tinyChainSpec], - variants = [], - }: { - chainSpecs: ChainSpec[], - variants: V[], +export class RunnerBuilder implements Runner { + public readonly parsers: testFile.Kind[] = []; + public readonly variants: V[] = []; + public readonly chainSpecs: ChainSpec[] = []; + + constructor( + public readonly path: string, + public readonly run: RunFunction, + ) {} + + fromJson(fromJson: FromJson) { + this.parsers.push({ kind: testFile.json, fromJson }); + return this; + } + + fromBin(codec: Decode) { + this.parsers.push({ kind: testFile.bin, codec }); + return this; + } + + withChainSpecDetection(chainSpec: ChainSpec[]) { + this.chainSpecs.push(...chainSpec); + return this; + } + + withVariants(variants: V[]) { + this.variants.push(...variants); + return this; + } + + build(): Runner { + const { path, run, parsers, variants, chainSpecs } = this; + if (parsers.length === 0) { + throw new Error(`No parsers for ${path}!`); + } + + return { + path, + run, + parsers, + variants, + chainSpecs, + } as Runner; } -): Runner { - check`${chainSpecs.length > 0} At least one chainspec missing in ${path} runner.`; - return { path, parser, run, variants, chainSpecs } as Runner; +} + +/** Test runner builder function. */ +export function runner(path: string, run: RunFunction, chainSpecs?: ChainSpec[]) { + const builder = new RunnerBuilder(path, run); + if (chainSpecs) { + return builder.withChainSpecDetection(chainSpecs); + } + return builder; } export type RunOptions = { @@ -37,52 +75,41 @@ export type RunOptions = { path: string; }; -export type RunFunction = ( - test: T, - variant: V, - options: RunOptions, -) => Promise; +export type RunFunction = (test: T, options: RunOptions, variant: V) => Promise; export type Runner = { path: string; - parser: testFile.Kind; + parsers: testFile.Kind[]; run: RunFunction; variants: V[]; chainSpecs: ChainSpec[]; }; export namespace testFile { - export const JSON = '.json'; - export type JSON = typeof JSON; - export const BIN = '.bin'; - export type BIN = typeof BIN; - - export type Kind = { - kind: JSON; - fromJson: FromJson; - } | { - kind: BIN; - codec: Decode; - }; + export const json = ".json"; + export type json = typeof json; + export const bin = ".bin"; + export type bin = typeof bin; - export type Content = + export type Kind = | { - kind: JSON; - content: string; - } + kind: json; + fromJson: FromJson; + } | { - kind: BIN; - content: Uint8Array; - }; - - - export function json(fromJson: FromJson): Kind { - return { kind: JSON, fromJson }; - } + kind: bin; + codec: Decode; + }; - export function bin(codec: Decode): Kind { - return { kind: BIN, codec: codec }; - } + export type Content = + | { + kind: json; + content: unknown; + } + | { + kind: bin; + content: Uint8Array; + }; } export async function main( @@ -90,11 +117,11 @@ export async function main( initialFiles: string[], directoryToScan: string, { - pattern = testFile.JSON, + pattern = testFile.json, accepted, ignored, }: { - pattern?: testFile.JSON | testFile.BIN; + pattern?: testFile.json | testFile.bin; accepted?: string[]; ignored?: string[]; } = {}, @@ -120,17 +147,17 @@ export async function main( } let testFileContent: testFile.Content; - if (absolutePath.endsWith(testFile.BIN)) { + if (absolutePath.endsWith(testFile.bin)) { const content: Buffer = await fs.readFile(absolutePath); testFileContent = { - kind: '.bin', + kind: ".bin", content: new Uint8Array(content), }; } else { const content = await fs.readFile(absolutePath, "utf8"); testFileContent = { - kind: '.json', - content, + kind: ".json", + content: JSON.parse(content), }; } @@ -174,7 +201,7 @@ export async function main( const runnersBatch = testRunners.slice(i * batchSize, (i + 1) * batchSize); for (const runner of runnersBatch) { const fileName = runner.file.replace(pathToReplace, ""); - const testCase = runner.variant !== null ? `[${runner.variant}] ${fileName}` : fileName; + const testCase = `${runner.variant}` !== "" ? `[${runner.variant}] ${fileName}` : fileName; if (runner.shouldSkip) { test.it.skip(testCase, runner.test); } else { @@ -214,44 +241,52 @@ function prepareTest( runners: Runner[], testContent: testFile.Content, fileName: string, - fullPath: string + fullPath: string, ): TestAndRunner[] { const errors: [string, unknown][] = []; const handleError = (name: string, e: unknown) => errors.push([name, e]); // NOTE This is not safe, but if the test does not specify // variants it means it doesn't care about them. - const noneVariant = '' as V; + const noneVariant = "" as V; // Find the first runner that is able to parse the input data. - for (const { path, parser, run, variants, chainSpecs } of runners) { - // NOTE: this `if` statement is intended to speed up parsing of the test files - // instead of trying each and every runner, we make sure that the absolute - // path to the file includes each part of our "test path" definition. - if (!path.split("/").every((pathPart) => fullPath.includes(pathPart))) { - continue; - } + for (const { path, parsers, run, variants, chainSpecs } of runners) { + const specs = chainSpecs.length > 0 ? chainSpecs : [tinyChainSpec]; + const matchPath = chainSpecs.length > 0; + for (const chainSpec of specs) { + // NOTE: this `if` statement is intended to speed up parsing of the test files + // instead of trying each and every runner, we make sure that the absolute + // path to the file includes each part of our "test path" definition. + if (!path.split("/").every((pathPart) => fullPath.includes(pathPart))) { + continue; + } + // if we care about the chain spec, we also need to match the path + if (matchPath && !fullPath.includes(chainSpec.name)) { + continue; + } - for (const chainSpec of chainSpecs) { - if (parser.kind === testFile.BIN && testContent.kind === testFile.BIN) { - try { - const parsedTest = Decoder.decodeObject(parser.codec, testContent.content, chainSpec); - return createTestDefinitions(path, run, variants, parsedTest, chainSpec); - } catch (e) { - handleError(path, e); + for (const parser of parsers) { + if (parser.kind === testFile.bin && testContent.kind === testFile.bin) { + try { + const parsedTest = Decoder.decodeObject(parser.codec, testContent.content, chainSpec); + return createTestDefinitions(path, run, variants, parsedTest, chainSpec); + } catch (e) { + handleError(path, e); + } } - } - if (parser.kind === testFile.JSON && testContent.kind === testFile.JSON) { - try { - const parsedTest = parseFromJson(testContent.content, parser.fromJson); - return createTestDefinitions(path, run, variants, parsedTest, chainSpec); - } catch (e) { - handleError(path, e); + if (parser.kind === testFile.json && testContent.kind === testFile.json) { + try { + const parsedTest = parseFromJson(testContent.content, parser.fromJson); + return createTestDefinitions(path, run, variants, parsedTest, chainSpec); + } catch (e) { + handleError(path, e); + } } - } - if (testContent.kind !== parser.kind) { - handleError(path, new Error(`Mismatching parser: got ${parser.kind}, need: ${testContent.kind}`)); + if (testContent.kind !== parser.kind) { + handleError(path, new Error(`Mismatching parser: got ${parser.kind}, need: ${testContent.kind}`)); + } } } } @@ -277,7 +312,7 @@ function prepareTest( run: RunFunction, variants: V[], parsedTest: T, - chainSpec: ChainSpec + chainSpec: ChainSpec, ) { const results: TestAndRunner[] = []; const possibleVariants: V[] = variants.length === 0 ? [noneVariant] : variants; @@ -291,14 +326,18 @@ function prepareTest( test: (ctx) => { logger.log`[${path}:${variant}] running test from ${fileName} (spec: ${chainSpec.name})`; logger.trace` ${util.inspect(parsedTest, true, 2)}`; - return run(parsedTest, variant, { - test: ctx, - path: fullPath, - chainSpec, - }); + return run( + parsedTest, + { + test: ctx, + path: fullPath, + chainSpec, + }, + variant, + ); }, }); } return results; - }; + } } diff --git a/bin/test-runner/jamduna-067.ts b/bin/test-runner/jamduna-067.ts index c710a7e64..f26df58ed 100644 --- a/bin/test-runner/jamduna-067.ts +++ b/bin/test-runner/jamduna-067.ts @@ -1,7 +1,12 @@ import { logger, main, runner } from "./common.js"; import { runStateTransition, StateTransition } from "./state-transition/state-transition.js"; -const runners = [runner("traces", StateTransition.fromJson, runStateTransition)]; +const runners = [ + runner("state_transition", runStateTransition) + .fromJson(StateTransition.fromJson) + .fromBin(StateTransition.Codec) + .withVariants(["ananas", "builtin"]), +].map((x) => x.build()); main(runners, process.argv.slice(2), "test-vectors/jamduna_067", { accepted: ["traces/"], diff --git a/bin/test-runner/javajam.ts b/bin/test-runner/javajam.ts index a739d25a5..79373ffbf 100644 --- a/bin/test-runner/javajam.ts +++ b/bin/test-runner/javajam.ts @@ -1,7 +1,12 @@ import { logger, main, runner } from "./common.js"; import { runStateTransition, StateTransition } from "./state-transition/state-transition.js"; -const runners = [runner("state_transition", StateTransition.fromJson, runStateTransition)]; +const runners = [ + runner("state_transition", runStateTransition) + .fromJson(StateTransition.fromJson) + .fromBin(StateTransition.Codec) + .withVariants(["ananas", "builtin"]), +].map((x) => x.build()); main(runners, process.argv.slice(2), "test-vectors/javajam", { accepted: ["stf/state_transitions/"], diff --git a/bin/test-runner/state-transition/state-transition.ts b/bin/test-runner/state-transition/state-transition.ts index 5b2e8e677..6af1b4075 100644 --- a/bin/test-runner/state-transition/state-transition.ts +++ b/bin/test-runner/state-transition/state-transition.ts @@ -14,8 +14,8 @@ import { TransitionHasher } from "@typeberry/transition"; import { BlockVerifier } from "@typeberry/transition/block-verifier.js"; import { OnChain } from "@typeberry/transition/chain-stf.js"; import { deepEqual, resultToString } from "@typeberry/utils"; +import type { RunOptions } from "../common.js"; import { loadState, TestState } from "./state-loader.js"; -import {RunOptions} from "../common.js"; export class StateTransitionGenesis { static fromJson: FromJson = { @@ -95,14 +95,14 @@ function blockAsView(spec: ChainSpec, block: Block) { // that were run pre-V1 fuzzer version. const jamConformance070V0Spec = new ChainSpec({ ...tinyChainSpec, - name: 'jam-conformance-v070v0', + name: "jam-conformance-v070v0", maxLookupAnchorAge: tryAsU32(14_400), }); export async function runStateTransition( testContent: StateTransition, - variant: "ananas" | "builtin", options: RunOptions, + variant: "ananas" | "builtin", ) { const pvm = variant === "ananas" ? PvmBackend.Ananas : PvmBackend.BuiltIn; const blake2b = await Blake2b.createHasher(); diff --git a/bin/test-runner/w3f/accumulate.ts b/bin/test-runner/w3f/accumulate.ts index 53aae467c..b3b67423a 100644 --- a/bin/test-runner/w3f/accumulate.ts +++ b/bin/test-runner/w3f/accumulate.ts @@ -27,7 +27,7 @@ import { JsonService } from "@typeberry/state-json/accounts.js"; import { AccumulateOutput } from "@typeberry/transition/accumulate/accumulate-output.js"; import { Accumulate, type AccumulateRoot } from "@typeberry/transition/accumulate/index.js"; import { Compatibility, deepEqual, GpVersion, Result } from "@typeberry/utils"; -import {RunOptions} from "../common.js"; +import type { RunOptions } from "../common.js"; class Input { static fromJson: FromJson = { @@ -141,11 +141,10 @@ export class AccumulateTest { post_state!: TestState; } - export async function runAccumulateTest( test: AccumulateTest, - variant: "ananas" | "builtin", { chainSpec }: RunOptions, + variant: "ananas" | "builtin", ) { const pvm = variant === "ananas" ? PvmBackend.Ananas : PvmBackend.BuiltIn; /** diff --git a/bin/test-runner/w3f/assurances.ts b/bin/test-runner/w3f/assurances.ts index bd873af92..fcd90b901 100644 --- a/bin/test-runner/w3f/assurances.ts +++ b/bin/test-runner/w3f/assurances.ts @@ -17,6 +17,7 @@ import { } from "@typeberry/transition/assurances.js"; import { copyAndUpdateState } from "@typeberry/transition/test.utils.js"; import { deepEqual, Result } from "@typeberry/utils"; +import type { RunOptions } from "../common.js"; const blake2b = Blake2b.createHasher(); @@ -144,29 +145,28 @@ export class AssurancesTestFull { post_state!: TestState; } -export async function runAssurancesTestTiny(testContent: AssurancesTestTiny, path: string) { - const spec = tinyChainSpec; +export async function runAssurancesTestTiny(testContent: AssurancesTestTiny, options: RunOptions) { + const spec = options.chainSpec; const preState = TestState.toAssurancesState(testContent.pre_state, spec); const postState = TestState.toAssurancesState(testContent.post_state, spec); const input = Input.toAssurancesInput(testContent.input, spec, preState.availabilityAssignment); const expectedResult = Output.toAssurancesTransitionResult(testContent.output); - await runAssurancesTest(path, spec, preState, postState, input, expectedResult); + await runAssurancesTest(options, preState, postState, input, expectedResult); } -export async function runAssurancesTestFull(testContent: AssurancesTestFull, path: string) { - const spec = fullChainSpec; +export async function runAssurancesTestFull(testContent: AssurancesTestFull, options: RunOptions) { + const spec = options.chainSpec; const preState = TestState.toAssurancesState(testContent.pre_state, spec); const postState = TestState.toAssurancesState(testContent.post_state, spec); const input = Input.toAssurancesInput(testContent.input, spec, preState.availabilityAssignment); const expectedResult = Output.toAssurancesTransitionResult(testContent.output); - await runAssurancesTest(path, spec, preState, postState, input, expectedResult); + await runAssurancesTest(options, preState, postState, input, expectedResult); } async function runAssurancesTest( - path: string, - spec: ChainSpec, + { chainSpec: spec, path }: RunOptions, preState: AssurancesState, postState: AssurancesState, input: AssurancesInput, diff --git a/bin/test-runner/w3f/authorizations.ts b/bin/test-runner/w3f/authorizations.ts index 0e55a1915..f9e56bb34 100644 --- a/bin/test-runner/w3f/authorizations.ts +++ b/bin/test-runner/w3f/authorizations.ts @@ -10,7 +10,7 @@ import { } from "@typeberry/transition/authorization.js"; import { copyAndUpdateState } from "@typeberry/transition/test.utils.js"; import { deepEqual } from "@typeberry/utils"; -import { getChainSpec } from "./spec.js"; +import type { RunOptions } from "../common.js"; class TestCoreAuthorizer { static fromJson: FromJson = { @@ -77,9 +77,7 @@ export class AuthorizationsTest { post_state!: AuthorizationState; } -export async function runAuthorizationsTest(test: AuthorizationsTest, path: string) { - const chainSpec = getChainSpec(path); - +export async function runAuthorizationsTest(test: AuthorizationsTest, { chainSpec }: RunOptions) { const authorization = new Authorization(chainSpec, test.pre_state); const update = authorization.transition(test.input); const result = copyAndUpdateState(test.pre_state, update); diff --git a/bin/test-runner/w3f/codec/index.ts b/bin/test-runner/w3f/codec/index.ts index c9b9aabd8..d24aed311 100644 --- a/bin/test-runner/w3f/codec/index.ts +++ b/bin/test-runner/w3f/codec/index.ts @@ -8,48 +8,49 @@ import { RefineContext } from "@typeberry/block/refine-context.js"; import { type TicketsExtrinsic, ticketsExtrinsicCodec } from "@typeberry/block/tickets.js"; import { WorkReport } from "@typeberry/block/work-report.js"; import { WorkResult } from "@typeberry/block/work-result.js"; +import type { RunOptions } from "../../common.js"; import { runCodecTest } from "./common.js"; -export async function runAssurancesExtrinsicTest(test: AssurancesExtrinsic, file: string) { +export async function runAssurancesExtrinsicTest(test: AssurancesExtrinsic, { path: file }: RunOptions) { runCodecTest(assurancesExtrinsicCodec, test, file); } -export async function runBlockTest(test: Block, file: string) { +export async function runBlockTest(test: Block, { path: file }: RunOptions) { runCodecTest(Block.Codec, test, file); } -export async function runDisputesExtrinsicTest(test: DisputesExtrinsic, file: string) { +export async function runDisputesExtrinsicTest(test: DisputesExtrinsic, { path: file }: RunOptions) { runCodecTest(DisputesExtrinsic.Codec, test, file); } -export async function runExtrinsicTest(test: Extrinsic, file: string) { +export async function runExtrinsicTest(test: Extrinsic, { path: file }: RunOptions) { runCodecTest(Extrinsic.Codec, test, file); } -export async function runGuaranteesExtrinsicTest(test: GuaranteesExtrinsic, file: string) { +export async function runGuaranteesExtrinsicTest(test: GuaranteesExtrinsic, { path: file }: RunOptions) { runCodecTest(guaranteesExtrinsicCodec, test, file); } -export async function runHeaderTest(test: Header, file: string) { +export async function runHeaderTest(test: Header, { path: file }: RunOptions) { runCodecTest(Header.Codec, test, file); } -export async function runPreimagesExtrinsicTest(test: PreimagesExtrinsic, file: string) { +export async function runPreimagesExtrinsicTest(test: PreimagesExtrinsic, { path: file }: RunOptions) { runCodecTest(preimagesExtrinsicCodec, test, file); } -export async function runRefineContextTest(test: RefineContext, file: string) { +export async function runRefineContextTest(test: RefineContext, { path: file }: RunOptions) { runCodecTest(RefineContext.Codec, test, file); } -export async function runTicketsExtrinsicTest(test: TicketsExtrinsic, file: string) { +export async function runTicketsExtrinsicTest(test: TicketsExtrinsic, { path: file }: RunOptions) { runCodecTest(ticketsExtrinsicCodec, test, file); } -export async function runWorkReportTest(test: WorkReport, file: string) { +export async function runWorkReportTest(test: WorkReport, { path: file }: RunOptions) { runCodecTest(WorkReport.Codec, test, file); } -export async function runWorkResultTest(test: WorkResult, file: string) { +export async function runWorkResultTest(test: WorkResult, { path: file }: RunOptions) { runCodecTest(WorkResult.Codec, test, file); } diff --git a/bin/test-runner/w3f/codec/work-item.ts b/bin/test-runner/w3f/codec/work-item.ts index 38265f5f9..65726667b 100644 --- a/bin/test-runner/w3f/codec/work-item.ts +++ b/bin/test-runner/w3f/codec/work-item.ts @@ -4,6 +4,7 @@ import { fromJson, type JsonObject } from "@typeberry/block-json"; import { BytesBlob } from "@typeberry/bytes"; import { json } from "@typeberry/json-parser"; import type { U16 } from "@typeberry/numbers"; +import type { RunOptions } from "../../common.js"; import { runCodecTest } from "./common.js"; const importSpecFromJson = json.object, ImportSpec>( @@ -57,6 +58,6 @@ type JsonWorkItem = { export_count: U16; }; -export async function runWorkItemTest(test: WorkItem, file: string) { - runCodecTest(WorkItem.Codec, test, file); +export async function runWorkItemTest(test: WorkItem, { path }: RunOptions) { + runCodecTest(WorkItem.Codec, test, path); } diff --git a/bin/test-runner/w3f/codec/work-package.ts b/bin/test-runner/w3f/codec/work-package.ts index f97ac2471..2828c2765 100644 --- a/bin/test-runner/w3f/codec/work-package.ts +++ b/bin/test-runner/w3f/codec/work-package.ts @@ -7,6 +7,7 @@ import { BytesBlob } from "@typeberry/bytes"; import { FixedSizeArray } from "@typeberry/collections"; import { type FromJson, json } from "@typeberry/json-parser"; import { Compatibility, GpVersion } from "@typeberry/utils"; +import type { RunOptions } from "../../common.js"; import { runCodecTest } from "./common.js"; import { workItemFromJson } from "./work-item.js"; @@ -76,6 +77,6 @@ type JsonWorkPackage = { items: WorkItem[]; }; -export async function runWorkPackageTest(test: WorkPackage, file: string) { +export async function runWorkPackageTest(test: WorkPackage, { path: file }: RunOptions) { runCodecTest(WorkPackage.Codec, test, file); } diff --git a/bin/test-runner/w3f/disputes.ts b/bin/test-runner/w3f/disputes.ts index 3a5c5bfc2..4ad3b484a 100644 --- a/bin/test-runner/w3f/disputes.ts +++ b/bin/test-runner/w3f/disputes.ts @@ -11,7 +11,7 @@ import { type FromJson, json } from "@typeberry/json-parser"; import { type AvailabilityAssignment, type DisputesRecords, tryAsPerCore, type ValidatorData } from "@typeberry/state"; import { availabilityAssignmentFromJson, disputesRecordsFromJson, validatorDataFromJson } from "@typeberry/state-json"; import { copyAndUpdateState } from "@typeberry/transition/test.utils.js"; -import { getChainSpec } from "./spec.js"; +import type { RunOptions } from "../common.js"; class DisputesOutputMarks { static fromJson: FromJson = { @@ -88,8 +88,7 @@ export class DisputesTest { post_state!: TestState; } -export async function runDisputesTest(testContent: DisputesTest, path: string) { - const chainSpec = getChainSpec(path); +export async function runDisputesTest(testContent: DisputesTest, { chainSpec }: RunOptions) { const preState = testContent.pre_state; const disputes = new Disputes( diff --git a/bin/test-runner/w3f/erasure-coding.ts b/bin/test-runner/w3f/erasure-coding.ts index 8f107ec68..e246f48f0 100644 --- a/bin/test-runner/w3f/erasure-coding.ts +++ b/bin/test-runner/w3f/erasure-coding.ts @@ -15,7 +15,7 @@ import { } from "@typeberry/erasure-coding"; import { type FromJson, json } from "@typeberry/json-parser"; import { check, deepEqual } from "@typeberry/utils"; -import { getChainSpec } from "./spec.js"; +import type { RunOptions } from "../common.js"; export class EcTest { static fromJson: FromJson = { @@ -27,9 +27,7 @@ export class EcTest { shards!: PerValidator; } -export async function runEcTest(test: EcTest, path: string) { - const spec = getChainSpec(path); - +export async function runEcTest(test: EcTest, { chainSpec: spec }: RunOptions) { await initEc(); it("should encode data & decode it back", () => { diff --git a/bin/test-runner/w3f/host-calls-accumulate.ts b/bin/test-runner/w3f/host-calls-accumulate.ts deleted file mode 100644 index c1c841d6b..000000000 --- a/bin/test-runner/w3f/host-calls-accumulate.ts +++ /dev/null @@ -1,109 +0,0 @@ -import assert from "node:assert"; -import type { CodeHash } from "@typeberry/block"; -import { fromJson } from "@typeberry/block-json"; -import { Bytes } from "@typeberry/bytes"; -import { type FromJson, json } from "@typeberry/json-parser"; -import type { ValidatorData } from "@typeberry/state"; -import { validatorDataFromJson } from "@typeberry/state-json"; -import { Memory, ServiceAccount } from "./host-calls-general.js"; - -namespace localFromJson { - export const bytes32 = >() => - json.fromString((v) => (v.length === 0 ? Bytes.zero(32).asOpaque() : Bytes.parseBytes(v, 32).asOpaque())); -} - -class PrivilegesState { - static fromJson: FromJson = { - chi_m: "number", - chi_a: "number", - chi_v: "number", - chi_g: json.record("number"), - }; - chi_m!: number; - chi_a!: number; - chi_v!: number; - chi_g!: Record; -} - -class PartialState { - static fromJson: FromJson = { - D: json.record(ServiceAccount.fromJson), - I: json.array(validatorDataFromJson), - Q: json.array(json.array("string")), - X: PrivilegesState.fromJson, - }; - D!: Record; - I!: ValidatorData[]; - Q!: string[][]; - X!: PrivilegesState; -} - -class DeferredTransfer { - static fromJson: FromJson = { - sender_index: "number", - receiver_index: "number", - amount: "number", - memo: fromJson.uint8Array, - gas_limit: "number", - }; - - sender_index!: number; - receiver_index!: number; - amount!: number; - memo!: Uint8Array; - gas_limit!: number; -} - -class XContent { - static fromJson: FromJson = { - I: "number", - S: "number", - U: json.optional(PartialState.fromJson), - T: json.optional(json.array(DeferredTransfer.fromJson)), - Y: json.optional(localFromJson.bytes32()), - }; - - I!: number; - S!: number; - U?: PartialState; - T?: DeferredTransfer[]; - Y?: CodeHash; -} - -export class HostCallAccumulateTest { - static fromJson: FromJson = { - name: "string", - "initial-gas": "number", - "initial-regs": json.record(fromJson.bigUint64), - "initial-memory": Memory.fromJson, - "initial-xcontent-x": XContent.fromJson, - "initial-xcontent-y": XContent.fromJson, - "initial-timeslot": "number", - "expected-gas": "number", - "expected-regs": json.record(fromJson.bigUint64), - "expected-memory": Memory.fromJson, - "expected-xcontent-x": XContent.fromJson, - "expected-xcontent-y": XContent.fromJson, - }; - - name!: string; - "initial-gas": number; - "initial-regs": Record; - "initial-memory": Memory; - "initial-xcontent-x": XContent; - "initial-xcontent-y": XContent; - "initial-timeslot": number; - "expected-gas": number; - "expected-regs": Record; - "expected-memory": Memory; - "expected-xcontent-x": XContent; - "expected-xcontent-y": XContent; -} - -export async function runHostCallAccumulateTest(testContent: HostCallAccumulateTest) { - const name = testContent.name; - if (name === undefined) { - assert.fail("name should be defined"); - } - assert.strictEqual(name.substring(0, 4), "host"); -} diff --git a/bin/test-runner/w3f/host-calls-general.ts b/bin/test-runner/w3f/host-calls-general.ts deleted file mode 100644 index 0d128db9f..000000000 --- a/bin/test-runner/w3f/host-calls-general.ts +++ /dev/null @@ -1,92 +0,0 @@ -import assert from "node:assert"; -import { fromJson } from "@typeberry/block-json"; -import { type FromJson, json } from "@typeberry/json-parser"; - -class MemoryPage { - static fromJson: FromJson = { - value: fromJson.uint8Array, - access: { - inaccessible: "boolean", - writable: "boolean", - readable: "boolean", - }, - }; - - value!: Uint8Array; - access!: { - inaccessible: boolean; - writable: boolean; - readable: boolean; - }; -} - -export class Memory { - static fromJson: FromJson = { - pages: json.record(MemoryPage.fromJson), - }; - - pages!: { - [key: string]: MemoryPage; - }; -} - -export class ServiceAccount { - static fromJson: FromJson = { - s_map: json.record(json.array("number")), - l_map: json.record({ - t: fromJson.uint8Array, - l: "number", - }), - p_map: json.record(json.array("number")), - code_hash: "string", - balance: "number", - g: "number", - m: "number", - }; - - s_map!: Record; - l_map!: Record; - p_map!: Record; - code_hash!: string; - balance!: number; - g!: number; - m!: number; -} - -export class HostCallGeneralTest { - static fromJson: FromJson = { - name: "string", - "initial-gas": "number", - "initial-regs": json.record(fromJson.bigUint64), - "initial-memory": Memory.fromJson, - "initial-service-account": ServiceAccount.fromJson, - "initial-service-index": "number", - "initial-delta": json.record(ServiceAccount.fromJson), - "expected-gas": "number", - "expected-regs": json.record(fromJson.bigUint64), - "expected-memory": Memory.fromJson, - "expected-delta": json.record(ServiceAccount.fromJson), - "expected-service-account": ServiceAccount.fromJson, - }; - - name!: string; - "initial-gas": number; - "initial-regs": Record; - "initial-memory": Memory; - "initial-service-account": ServiceAccount; - "initial-service-index": number; - "initial-delta": Record; - "expected-gas": number; - "expected-regs": Record; - "expected-memory": Memory; - "expected-delta": Record; - "expected-service-account": ServiceAccount; -} - -export async function runHostCallGeneralTest(testContent: HostCallGeneralTest) { - const name = testContent.name; - if (name === undefined) { - assert.fail("name should be defined"); - } - assert.strictEqual(name.substring(0, 4), "host"); -} diff --git a/bin/test-runner/w3f/host-calls-refine.ts b/bin/test-runner/w3f/host-calls-refine.ts deleted file mode 100644 index e0713259f..000000000 --- a/bin/test-runner/w3f/host-calls-refine.ts +++ /dev/null @@ -1,62 +0,0 @@ -import assert from "node:assert"; -import { fromJson } from "@typeberry/block-json"; -import { type FromJson, json } from "@typeberry/json-parser"; -import { Memory, ServiceAccount } from "./host-calls-general.js"; - -class MapItem { - static fromJson: FromJson = { - P: fromJson.uint8Array, - U: Memory.fromJson, - I: "number", - }; - - P!: Uint8Array; - U!: Memory; - I!: number; -} - -export class HostCallRefineTest { - static fromJson: FromJson = { - name: "string", - "initial-gas": "number", - "initial-regs": json.record(fromJson.bigUint64), - "initial-memory": Memory.fromJson, - "initial-service-index": "number", - "initial-delta": json.optional(json.record(ServiceAccount.fromJson)), - "initial-timeslot": "number", - "initial-refine-map": json.optional(json.record(MapItem.fromJson)), - "initial-export-segment": json.optional(json.array(fromJson.uint8Array)), - "initial-import-segment": json.optional(json.array(fromJson.uint8Array)), - "initial-export-segment-index": "number", - "expected-gas": "number", - "expected-regs": json.record(fromJson.bigUint64), - "expected-memory": Memory.fromJson, - "expected-refine-map": json.optional(json.record(MapItem.fromJson)), - "expected-export-segment": json.optional(json.array(fromJson.uint8Array)), - }; - - name!: string; - "initial-gas": number; - "initial-regs": Record; - "initial-memory": Memory; - "initial-service-index": number; - "initial-delta"?: Record; - "initial-timeslot": number; - "initial-refine-map"?: Record; - "initial-export-segment"?: Uint8Array[]; - "initial-import-segment"?: Uint8Array[]; - "initial-export-segment-index": number; - "expected-gas": number; - "expected-regs": Record; - "expected-memory": Memory; - "expected-refine-map"?: Record; - "expected-export-segment"?: Uint8Array[]; -} - -export async function runHostCallRefineTest(testContent: HostCallRefineTest) { - const name = testContent.name; - if (name === undefined) { - assert.fail("name should be defined"); - } - assert.strictEqual(name.substring(0, 4), "host"); -} diff --git a/bin/test-runner/w3f/reports.ts b/bin/test-runner/w3f/reports.ts index ce3b14be5..ff3fe6a19 100644 --- a/bin/test-runner/w3f/reports.ts +++ b/bin/test-runner/w3f/reports.ts @@ -9,7 +9,7 @@ import type { GuaranteesExtrinsic } from "@typeberry/block/guarantees.js"; import type { AuthorizerHash, WorkPackageHash, WorkPackageInfo } from "@typeberry/block/refine-context.js"; import { fromJson, guaranteesExtrinsicFromJson, segmentRootLookupItemFromJson } from "@typeberry/block-json"; import { asKnownSize, FixedSizeArray, HashDictionary, HashSet } from "@typeberry/collections"; -import { type ChainSpec, fullChainSpec, tinyChainSpec } from "@typeberry/config"; +import type { ChainSpec } from "@typeberry/config"; import type { Ed25519Key } from "@typeberry/crypto"; import { Blake2b } from "@typeberry/hash"; import { type FromJson, json } from "@typeberry/json-parser"; @@ -43,6 +43,7 @@ import { guaranteesAsView } from "@typeberry/transition/reports/test.utils.js"; import type { HeaderChain } from "@typeberry/transition/reports/verify-contextual.js"; import { copyAndUpdateState } from "@typeberry/transition/test.utils.js"; import { deepEqual, Result } from "@typeberry/utils"; +import type { RunOptions } from "../common.js"; type TestReportsOutput = Omit; @@ -247,15 +248,7 @@ export class ReportsTest { post_state!: TestState; } -export async function runReportsTestTiny(testContent: ReportsTest) { - await runReportsTest(testContent, tinyChainSpec); -} - -export async function runReportsTestFull(testContent: ReportsTest) { - await runReportsTest(testContent, fullChainSpec); -} - -async function runReportsTest(testContent: ReportsTest, spec: ChainSpec) { +export async function runReportsTest(testContent: ReportsTest, { chainSpec: spec }: RunOptions) { const preState = TestState.toReportsState(testContent.pre_state, spec); const postState = TestState.toReportsState(testContent.post_state, spec); const input = Input.toReportsInput( diff --git a/bin/test-runner/w3f/runners.ts b/bin/test-runner/w3f/runners.ts index 89cab4544..f2159c999 100644 --- a/bin/test-runner/w3f/runners.ts +++ b/bin/test-runner/w3f/runners.ts @@ -12,7 +12,7 @@ import { workResultFromJson, } from "@typeberry/block-json"; import { fullChainSpec, tinyChainSpec } from "@typeberry/config"; -import { runner, testFile } from "../common.js"; +import { runner } from "../common.js"; import { runStateTransition, StateTransition } from "../state-transition/state-transition.js"; import { AccumulateTest, runAccumulateTest } from "./accumulate.js"; import { AssurancesTestFull, AssurancesTestTiny, runAssurancesTestFull, runAssurancesTestTiny } from "./assurances.js"; @@ -34,77 +34,69 @@ import { runWorkItemTest, workItemFromJson } from "./codec/work-item.js"; import { runWorkPackageTest, workPackageFromJson } from "./codec/work-package.js"; import { DisputesTest, runDisputesTest } from "./disputes.js"; import { EcTest, runEcTest } from "./erasure-coding.js"; -import { HostCallAccumulateTest, runHostCallAccumulateTest } from "./host-calls-accumulate.js"; -import { HostCallGeneralTest, runHostCallGeneralTest } from "./host-calls-general.js"; -import { HostCallRefineTest, runHostCallRefineTest } from "./host-calls-refine.js"; import { PreImagesTest, runPreImagesTest } from "./preimages.js"; import { PvmTest, runPvmTest } from "./pvm.js"; import { PvmGasCostTest, runPvmGasCostTest } from "./pvm-gas-cost.js"; import { HistoryTest, runHistoryTest } from "./recent-history.js"; -import { ReportsTest, runReportsTestFull, runReportsTestTiny } from "./reports.js"; +import { ReportsTest, runReportsTest } from "./reports.js"; import { runSafroleTest, SafroleTest } from "./safrole.js"; import { ignoreSchemaFiles, JsonSchema } from "./schema.js"; -import { runShufflingTests, shufflingTests } from "./shuffling.js"; +import { runShufflingTests, shufflingTestsFromJson } from "./shuffling.js"; import { runStatisticsTestFull, runStatisticsTestTiny, StatisticsTestFull, StatisticsTestTiny } from "./statistics.js"; import { runTrieTest, trieTestSuiteFromJson } from "./trie.js"; -const pvmVariants: ("ananas" | "builtin")[] = ["ananas", "builtin"]; +const pvms: ("ananas" | "builtin")[] = ["ananas", "builtin"]; +const tiny = [tinyChainSpec]; +const full = [fullChainSpec]; +const tinyFull = [...tiny, ...full]; export const runners = [ - runner("accumulate/tiny", testFile.json(AccumulateTest.fromJson), runAccumulateTest, { - chainSpecs: [tinyChainSpec], - variants: pvmVariants, - }), - runner("accumulate/full", AccumulateTest.fromJson, runAccumulateTest, { - chainSpecs: [tinyChainSpec], - variants: pvmVariants, - }), - runner("assurances/tiny", AssurancesTestTiny.fromJson, runAssurancesTestTiny, tinyChainSpec), - runner("assurances/full", AssurancesTestFull.fromJson, runAssurancesTestFull, fullChainSpec), - runner("authorizations", AuthorizationsTest.fromJson, runAuthorizationsTest), + runner("accumulate", runAccumulateTest, tinyFull).fromJson(AccumulateTest.fromJson).withVariants(pvms), + runner("assurances/tiny", runAssurancesTestTiny, tiny).fromJson(AssurancesTestTiny.fromJson), + runner("assurances/full", runAssurancesTestFull, full).fromJson(AssurancesTestFull.fromJson), + runner("authorizations", runAuthorizationsTest, tinyFull).fromJson(AuthorizationsTest.fromJson), ...codecRunners("tiny"), ...codecRunners("full"), - runner("disputes", DisputesTest.fromJson, runDisputesTest), - runner("erasure_coding", EcTest.fromJson, runEcTest), - runner("history", HistoryTest.fromJson, runHistoryTest), - runner("schema", JsonSchema.fromJson, ignoreSchemaFiles), // ignore schema files - runner("preimages", PreImagesTest.fromJson, runPreImagesTest), - runner("pvm", PvmTest.fromJson, runPvmTest), - runner("gas-cost-tests", PvmGasCostTest.fromJson, runPvmGasCostTest), - runner("host_function", HostCallGeneralTest.fromJson, runHostCallGeneralTest), - runner("host_function", HostCallAccumulateTest.fromJson, runHostCallAccumulateTest), - runner("host_function", HostCallRefineTest.fromJson, runHostCallRefineTest), - runner("reports/tiny", ReportsTest.fromJson, runReportsTestTiny, tinyChainSpec), - runner("reports/full", ReportsTest.fromJson, runReportsTestFull, fullChainSpec), - runner("safrole", SafroleTest.fromJson, runSafroleTest), - runner("shuffle", shufflingTests, runShufflingTests), - runner("statistics/tiny", StatisticsTestTiny.fromJson, runStatisticsTestTiny, tinyChainSpec), - runner("statistics/full", StatisticsTestFull.fromJson, runStatisticsTestFull, fullChainSpec), - runner("trie", trieTestSuiteFromJson, runTrieTest), - runner("traces", StateTransition.fromJson, runStateTransition, tinyChainSpec, StateTransition.Codec), - runner("traces", StateTransition.fromJson, runStateTransition, ["ananas", "builtin"]), -]; + runner("disputes", runDisputesTest, tinyFull).fromJson(DisputesTest.fromJson), + runner("erasure_coding", runEcTest, tinyFull).fromJson(EcTest.fromJson), + runner("history", runHistoryTest, tinyFull).fromJson(HistoryTest.fromJson), + runner("schema", ignoreSchemaFiles).fromJson(JsonSchema.fromJson), // ignore schema files + runner("preimages", runPreImagesTest, tinyFull).fromJson(PreImagesTest.fromJson), + 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("shuffle", runShufflingTests).fromJson(shufflingTestsFromJson), + runner("statistics/tiny", runStatisticsTestTiny, tiny).fromJson(StatisticsTestTiny.fromJson), + runner("statistics/full", runStatisticsTestFull, full).fromJson(StatisticsTestFull.fromJson), + runner("trie", runTrieTest).fromJson(trieTestSuiteFromJson), + runner("traces", runStateTransition) + .fromJson(StateTransition.fromJson) + .fromBin(StateTransition.Codec) + .withVariants(pvms), +].map((b) => b.build()); function codecRunners(flavor: "tiny" | "full") { const spec = flavor === "tiny" ? tinyChainSpec : fullChainSpec; return [ - runner( - `codec/${flavor}/assurances_extrinsic`, + runner(`codec/${flavor}/assurances_extrinsic`, runAssurancesExtrinsicTest, [spec]).fromJson( getAssurancesExtrinsicFromJson(spec), - runAssurancesExtrinsicTest, - spec, ), - runner(`codec/${flavor}/block`, blockFromJson(spec), runBlockTest, spec), - runner(`codec/${flavor}/disputes_extrinsic`, disputesExtrinsicFromJson, runDisputesExtrinsicTest, spec), - runner(`codec/${flavor}/extrinsic`, getExtrinsicFromJson(spec), runExtrinsicTest, spec), - runner(`codec/${flavor}/guarantees_extrinsic`, guaranteesExtrinsicFromJson, runGuaranteesExtrinsicTest, spec), - runner(`codec/${flavor}/header`, headerFromJson, runHeaderTest, spec), - runner(`codec/${flavor}/preimages_extrinsic`, preimagesExtrinsicFromJson, runPreimagesExtrinsicTest, spec), - runner(`codec/${flavor}/refine_context`, refineContextFromJson, runRefineContextTest, spec), - runner(`codec/${flavor}/tickets_extrinsic`, ticketsExtrinsicFromJson, runTicketsExtrinsicTest, spec), - runner(`codec/${flavor}/work_item`, workItemFromJson, runWorkItemTest, spec), - runner(`codec/${flavor}/work_package`, workPackageFromJson, runWorkPackageTest, spec), - runner(`codec/${flavor}/work_report`, workReportFromJson, runWorkReportTest, spec), - runner(`codec/${flavor}/work_result`, workResultFromJson, runWorkResultTest, spec), + runner(`codec/${flavor}/block`, runBlockTest, [spec]).fromJson(blockFromJson(spec)), + runner(`codec/${flavor}/disputes_extrinsic`, runDisputesExtrinsicTest, [spec]).fromJson(disputesExtrinsicFromJson), + runner(`codec/${flavor}/extrinsic`, runExtrinsicTest, [spec]).fromJson(getExtrinsicFromJson(spec)), + runner(`codec/${flavor}/guarantees_extrinsic`, runGuaranteesExtrinsicTest, [spec]).fromJson( + guaranteesExtrinsicFromJson, + ), + runner(`codec/${flavor}/header`, runHeaderTest, [spec]).fromJson(headerFromJson), + 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}/work_item`, runWorkItemTest, [spec]).fromJson(workItemFromJson), + runner(`codec/${flavor}/work_package`, runWorkPackageTest, [spec]).fromJson(workPackageFromJson), + runner(`codec/${flavor}/work_report`, runWorkReportTest, [spec]).fromJson(workReportFromJson), + runner(`codec/${flavor}/work_result`, runWorkResultTest, [spec]).fromJson(workResultFromJson), ]; } diff --git a/bin/test-runner/w3f/safrole.ts b/bin/test-runner/w3f/safrole.ts index 53507660f..f0085b2af 100644 --- a/bin/test-runner/w3f/safrole.ts +++ b/bin/test-runner/w3f/safrole.ts @@ -30,7 +30,7 @@ import { ENTROPY_ENTRIES, hashComparator, type ValidatorData } from "@typeberry/ import { TicketsOrKeys, ticketFromJson, validatorDataFromJson } from "@typeberry/state-json"; import { copyAndUpdateState } from "@typeberry/transition/test.utils.js"; import { deepEqual, Result } from "@typeberry/utils"; -import { getChainSpec } from "./spec.js"; +import type { RunOptions } from "../common.js"; namespace safroleFromJson { export const ticketEnvelope: FromJson = { @@ -211,8 +211,7 @@ export class SafroleTest { export const bwasm = BandernsatchWasm.new(); -export async function runSafroleTest(testContent: SafroleTest, path: string) { - const chainSpec = getChainSpec(path); +export async function runSafroleTest(testContent: SafroleTest, { chainSpec }: RunOptions) { const preState = JsonState.toSafroleState(testContent.pre_state, chainSpec); const punishSet = SortedSet.fromArrayUnique(hashComparator, testContent.pre_state.post_offenders); const safrole = new Safrole(chainSpec, await Blake2b.createHasher(), preState, bwasm); diff --git a/bin/test-runner/w3f/shuffling.ts b/bin/test-runner/w3f/shuffling.ts index 265175048..20c7dfc02 100644 --- a/bin/test-runner/w3f/shuffling.ts +++ b/bin/test-runner/w3f/shuffling.ts @@ -20,7 +20,7 @@ class ShufflingTest { output!: number[]; } -export const shufflingTests = json.array(ShufflingTest.fromJson); +export const shufflingTestsFromJson = json.array(ShufflingTest.fromJson); export async function runShufflingTests(testContents: ShufflingTest[]) { const blake2b = await Blake2b.createHasher(); diff --git a/packages/jam/config/chain-spec.ts b/packages/jam/config/chain-spec.ts index 0ed358053..52a831f36 100644 --- a/packages/jam/config/chain-spec.ts +++ b/packages/jam/config/chain-spec.ts @@ -107,7 +107,7 @@ export class ChainSpec extends WithDebug { /** Set of values for "tiny" chain as defined in JAM test vectors. */ export const tinyChainSpec = new ChainSpec({ - name: 'tiny', + name: "tiny", validatorsCount: tryAsU16(6), coresCount: tryAsU16(2), epochLength: tryAsU32(12), @@ -130,7 +130,7 @@ export const tinyChainSpec = new ChainSpec({ * Please note that only validatorsCount and epochLength are "full", the rest is copied from "tiny". */ export const fullChainSpec = new ChainSpec({ - name: 'full', + name: "full", validatorsCount: tryAsU16(1023), coresCount: tryAsU16(341), epochLength: tryAsU32(600), diff --git a/packages/jam/transition/chain-stf.ts b/packages/jam/transition/chain-stf.ts index 8edfd45a4..cb1d8dbd5 100644 --- a/packages/jam/transition/chain-stf.ts +++ b/packages/jam/transition/chain-stf.ts @@ -2,7 +2,7 @@ import type { BlockView, CoreIndex, EntropyHash, HeaderHash, ServiceId, TimeSlot import type { GuaranteesExtrinsicView } from "@typeberry/block/guarantees.js"; import type { AuthorizerHash } from "@typeberry/block/refine-context.js"; import { asKnownSize, HashSet } from "@typeberry/collections"; -import type { ChainSpec, PvmBackend } from "@typeberry/config"; +import { type ChainSpec, PvmBackend } from "@typeberry/config"; import type { Ed25519Key } from "@typeberry/crypto"; import type { BlocksDb } from "@typeberry/database"; import { Disputes, type DisputesStateUpdate } from "@typeberry/disputes"; From 9097539b0b8f5cef5cdf49e78bcb4d1a29b90e24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Tue, 28 Oct 2025 22:08:13 +0100 Subject: [PATCH 05/15] Address review comments. --- packages/core/numbers/index.ts | 5 +++-- packages/core/utils/opaque.ts | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/core/numbers/index.ts b/packages/core/numbers/index.ts index 6384f83cb..7752497cf 100644 --- a/packages/core/numbers/index.ts +++ b/packages/core/numbers/index.ts @@ -1,10 +1,11 @@ import { check } from "@typeberry/utils"; -declare const __REPRESENTATION_BYTES__: unique symbol; +export declare const __REPRESENTATION_BYTES__: unique symbol; -type WithBytesRepresentation = { +export type WithBytesRepresentation = { readonly [__REPRESENTATION_BYTES__]: Bytes; }; + const asTypedNumber = (v: T): T & WithBytesRepresentation => v as T & WithBytesRepresentation; diff --git a/packages/core/utils/opaque.ts b/packages/core/utils/opaque.ts index dba06a99f..8e08a452c 100644 --- a/packages/core/utils/opaque.ts +++ b/packages/core/utils/opaque.ts @@ -21,7 +21,7 @@ type Uninstantiable = void & { __brand: "uninstantiable" }; type StringLiteral = Type extends string ? (string extends Type ? never : Type) : never; -declare const __OPAQUE_TYPE__: unique symbol; +export declare const __OPAQUE_TYPE__: unique symbol; export type WithOpaque = { readonly [__OPAQUE_TYPE__]: Token; From 3dc18bf318b31c37d9062fb2b50d4db83eb76f06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Tue, 28 Oct 2025 22:44:02 +0100 Subject: [PATCH 06/15] Fix running the multiple tests on the same test state. --- bin/test-runner/common.ts | 2 +- bin/test-runner/w3f/accumulate.ts | 3 +-- packages/jam/state/in-memory-state.ts | 14 +++++++++++++- .../externalities/accumulate-externalities.test.ts | 4 ++-- .../externalities/accumulate-externalities.ts | 2 +- 5 files changed, 18 insertions(+), 7 deletions(-) diff --git a/bin/test-runner/common.ts b/bin/test-runner/common.ts index 543d175e6..ad43962be 100644 --- a/bin/test-runner/common.ts +++ b/bin/test-runner/common.ts @@ -63,7 +63,7 @@ export class RunnerBuilder implements Runner { /** Test runner builder function. */ export function runner(path: string, run: RunFunction, chainSpecs?: ChainSpec[]) { const builder = new RunnerBuilder(path, run); - if (chainSpecs) { + if (chainSpecs !== undefined) { return builder.withChainSpecDetection(chainSpecs); } return builder; diff --git a/bin/test-runner/w3f/accumulate.ts b/bin/test-runner/w3f/accumulate.ts index b3b67423a..d5b3098ff 100644 --- a/bin/test-runner/w3f/accumulate.ts +++ b/bin/test-runner/w3f/accumulate.ts @@ -110,7 +110,7 @@ class TestState { AutoAccumulate.create({ gasLimit: gas, service: id }), ), }), - services: new Map(accounts.map((service) => [service.serviceId, service])), + services: new Map(accounts.map((service) => [service.serviceId, service.clone()])), }); } } @@ -156,7 +156,6 @@ export async function runAccumulateTest( const entropy = test.pre_state.entropy; const post_state = TestState.toAccumulateState(test.post_state, chainSpec); - const state = TestState.toAccumulateState(test.pre_state, chainSpec); const accumulate = new Accumulate(chainSpec, await Blake2b.createHasher(), state, pvm); const accumulateOutput = new AccumulateOutput(); diff --git a/packages/jam/state/in-memory-state.ts b/packages/jam/state/in-memory-state.ts index 094326444..794865adc 100644 --- a/packages/jam/state/in-memory-state.ts +++ b/packages/jam/state/in-memory-state.ts @@ -45,7 +45,7 @@ import { LookupHistoryItem, type LookupHistorySlots, PreimageItem, - type ServiceAccountInfo, + ServiceAccountInfo, StorageItem, type StorageKey, tryAsLookupHistorySlots, @@ -129,6 +129,18 @@ export class InMemoryService extends WithDebug implements Service { }; } + /** Return identical `InMemoryService` which does not share any references. */ + clone(): InMemoryService { + return new InMemoryService(this.serviceId, { + info: ServiceAccountInfo.create(this.data.info), + preimages: HashDictionary.fromEntries(Array.from(this.data.preimages.entries())), + lookupHistory: HashDictionary.fromEntries( + Array.from(this.data.lookupHistory.entries()).map(([k, v]) => [k, v.slice()]), + ), + storage: new Map(this.data.storage.entries()), + }); + } + /** * Create a new in-memory service from another state service * by copying all given entries. diff --git a/packages/jam/transition/externalities/accumulate-externalities.test.ts b/packages/jam/transition/externalities/accumulate-externalities.test.ts index d7e9b6b86..63425c808 100644 --- a/packages/jam/transition/externalities/accumulate-externalities.test.ts +++ b/packages/jam/transition/externalities/accumulate-externalities.test.ts @@ -2448,7 +2448,7 @@ describe("PartialState.eject", () => { // then deepEqual( result, - Result.error(EjectError.InvalidPreimage, () => "Previous code available: wrong status"), + Result.error(EjectError.InvalidPreimage, () => "Previous code available: wrong status: null"), ); assert.deepStrictEqual(state.stateUpdate.services.removed, []); }); @@ -2482,7 +2482,7 @@ describe("PartialState.eject", () => { // then deepEqual( result, - Result.error(EjectError.InvalidPreimage, () => "Previous code available: wrong status"), + Result.error(EjectError.InvalidPreimage, () => "Previous code available: wrong status: Available"), ); assert.deepStrictEqual(state.stateUpdate.services.removed, []); }); diff --git a/packages/jam/transition/externalities/accumulate-externalities.ts b/packages/jam/transition/externalities/accumulate-externalities.ts index e36c1ae3c..1514793ec 100644 --- a/packages/jam/transition/externalities/accumulate-externalities.ts +++ b/packages/jam/transition/externalities/accumulate-externalities.ts @@ -151,7 +151,7 @@ export class AccumulateExternalities const status = slots === null ? null : slotsToPreimageStatus(slots.slots); // The previous code needs to be forgotten and expired. if (status?.status !== PreimageStatusKind.Unavailable) { - return [false, "wrong status"]; + return [false, `wrong status: ${status !== null ? PreimageStatusKind[status.status] : null}`]; } const t = this.currentTimeslot; const isExpired = status.data[1] < t - this.chainSpec.preimageExpungePeriod; From cf4f4c29178c33d98ab43c6e988873ebc15393d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Thu, 30 Oct 2025 14:17:12 +0100 Subject: [PATCH 07/15] Update ananas. --- bin/test-runner/w3f.ts | 2 ++ package-lock.json | 22 +++++++++---------- packages/core/pvm-interpreter-ananas/index.ts | 11 ++-------- .../core/pvm-interpreter-ananas/package.json | 2 +- 4 files changed, 16 insertions(+), 21 deletions(-) diff --git a/bin/test-runner/w3f.ts b/bin/test-runner/w3f.ts index 2a0426c6e..790c4305b 100644 --- a/bin/test-runner/w3f.ts +++ b/bin/test-runner/w3f.ts @@ -4,6 +4,8 @@ import { runners } from "./w3f/runners.js"; main(runners, process.argv.slice(2), "test-vectors/w3f-fluffy", { ignored: [ "genesis.json", + // invalid tests + "host_function", // Ignored - not working correctly in 0.6.7 and we ditched fixing them. "traces/preimages_light/00000070.json", "traces/preimages_light/00000073.json", diff --git a/package-lock.json b/package-lock.json index 4848fd75d..d92ef1c18 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1171,6 +1171,16 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, + "node_modules/@fluffylabs/anan-as": { + "version": "1.1.1-fbc6aca", + "resolved": "https://registry.npmjs.org/@fluffylabs/anan-as/-/anan-as-1.1.1-fbc6aca.tgz", + "integrity": "sha512-XL2sztQsdhTDxeihBIojOyOKaXVmGFVL1/OXOSWhU4QONxuD5hVClzkPrYR38ClRX40zVd38LJOkSso8caw/mg==", + "license": "MPL-2.0", + "dependencies": { + "@typeberry/lib": "^0.2.0", + "json-bigint-patch": "^0.0.8" + } + }, "node_modules/@humanfs/core": { "version": "0.19.1", "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", @@ -6649,7 +6659,7 @@ "version": "0.1.3", "license": "MPL-2.0", "dependencies": { - "@fluffylabs/anan-as": "^1.0.0-a9b8141", + "@fluffylabs/anan-as": "^1.1.1-b8e442b", "@typeberry/codec": "*", "@typeberry/numbers": "*", "@typeberry/pvm-interface": "*", @@ -6657,16 +6667,6 @@ "assemblyscript-loader": "^0.3.0" } }, - "packages/core/pvm-interpreter-ananas/node_modules/@fluffylabs/anan-as": { - "version": "1.0.0-a9b8141", - "resolved": "https://registry.npmjs.org/@fluffylabs/anan-as/-/anan-as-1.0.0-a9b8141.tgz", - "integrity": "sha512-B6hC5I1jCWpen2Cze2GEMs9sZiNZ+0EgowIHKenj2Cjvq75FyUGpVJYktAeEYlLa/GgCaFAC0fP0bVWY/NZ26g==", - "license": "MPL-2.0", - "dependencies": { - "@typeberry/lib": "^0.2.0", - "json-bigint-patch": "^0.0.8" - } - }, "packages/core/pvm-program": { "name": "@typeberry/pvm-program", "version": "0.2.0", diff --git a/packages/core/pvm-interpreter-ananas/index.ts b/packages/core/pvm-interpreter-ananas/index.ts index d54467730..50bff4d08 100644 --- a/packages/core/pvm-interpreter-ananas/index.ts +++ b/packages/core/pvm-interpreter-ananas/index.ts @@ -1,6 +1,4 @@ -import { readFileSync } from "node:fs"; -import { fileURLToPath } from "node:url"; -import { instantiate } from "@fluffylabs/anan-as/raw"; +import { instantiate } from "@fluffylabs/anan-as/release-stub-inline"; import { tryAsU32, type U32 } from "@typeberry/numbers"; import { type Gas, @@ -20,10 +18,6 @@ import { check, OK, Result } from "@typeberry/utils"; type Ananas = Awaited>; -// TODO [ToDr] Temporary solution. We need to inline WASM files for the final build. -const wasmPath = fileURLToPath(new URL(import.meta.resolve("@fluffylabs/anan-as/release-mini.wasm"), import.meta.url)); -const wasmBuffer = readFileSync(wasmPath); - // Max u32 value const INF_STEPS = 2 ** 32 - 1; @@ -116,8 +110,7 @@ export class AnanasInterpreter implements IPvmInterpreter { } static async new() { - const wasmModule = await WebAssembly.compile(wasmBuffer); - const instance = await instantiate(wasmModule, { + const instance = await instantiate({ env: { abort: () => { throw new Error("Abort called from WASM"); diff --git a/packages/core/pvm-interpreter-ananas/package.json b/packages/core/pvm-interpreter-ananas/package.json index ffdbb25a3..e91766a26 100644 --- a/packages/core/pvm-interpreter-ananas/package.json +++ b/packages/core/pvm-interpreter-ananas/package.json @@ -4,7 +4,7 @@ "description": "Anan-as PVM implementation.", "main": "index.ts", "dependencies": { - "@fluffylabs/anan-as": "^1.0.0-a9b8141", + "@fluffylabs/anan-as": "^1.1.1-b8e442b", "@typeberry/codec": "*", "@typeberry/numbers": "*", "@typeberry/pvm-interface": "*", From 6920e80c82928257f8d2afa0d4586de869af48aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Thu, 30 Oct 2025 21:31:59 +0100 Subject: [PATCH 08/15] Update lockfile. --- package-lock.json | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/package-lock.json b/package-lock.json index d92ef1c18..bf805fa19 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1171,16 +1171,6 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@fluffylabs/anan-as": { - "version": "1.1.1-fbc6aca", - "resolved": "https://registry.npmjs.org/@fluffylabs/anan-as/-/anan-as-1.1.1-fbc6aca.tgz", - "integrity": "sha512-XL2sztQsdhTDxeihBIojOyOKaXVmGFVL1/OXOSWhU4QONxuD5hVClzkPrYR38ClRX40zVd38LJOkSso8caw/mg==", - "license": "MPL-2.0", - "dependencies": { - "@typeberry/lib": "^0.2.0", - "json-bigint-patch": "^0.0.8" - } - }, "node_modules/@humanfs/core": { "version": "0.19.1", "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", @@ -6667,6 +6657,16 @@ "assemblyscript-loader": "^0.3.0" } }, + "packages/core/pvm-interpreter-ananas/node_modules/@fluffylabs/anan-as": { + "version": "1.1.1-b8e442b", + "resolved": "https://registry.npmjs.org/@fluffylabs/anan-as/-/anan-as-1.1.1-b8e442b.tgz", + "integrity": "sha512-grLT4xItHo/CB/RaJWiqhWrHzSXlmG1bCB06rXGoqOpMnMbh5ElVWf9/rRAvBmT7iZ/G+X3Alfa1EEXGZ3K7+Q==", + "license": "MPL-2.0", + "dependencies": { + "@typeberry/lib": "^0.2.0", + "json-bigint-patch": "^0.0.8" + } + }, "packages/core/pvm-program": { "name": "@typeberry/pvm-program", "version": "0.2.0", From f6c327cdd3f984f5eda59ecca12cb1855b22ff4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Thu, 30 Oct 2025 21:58:47 +0100 Subject: [PATCH 09/15] Support both json & bin files with the same runner. --- .github/workflows/vectors-w3f-davxy-071.yml | 1 - bin/test-runner/common.ts | 10 +++++----- bin/test-runner/package.json | 1 - bin/test-runner/w3f-davxy-071-bin.ts | 21 --------------------- bin/test-runner/w3f-davxy-071.ts | 9 +++++---- 5 files changed, 10 insertions(+), 32 deletions(-) delete mode 100644 bin/test-runner/w3f-davxy-071-bin.ts diff --git a/.github/workflows/vectors-w3f-davxy-071.yml b/.github/workflows/vectors-w3f-davxy-071.yml index 18c3c908b..63af25149 100644 --- a/.github/workflows/vectors-w3f-davxy-071.yml +++ b/.github/workflows/vectors-w3f-davxy-071.yml @@ -40,7 +40,6 @@ jobs: cache: "npm" - run: npm ci - run: npm start -w @typeberry/test-runner -- w3f-davxy-071.ts - - run: npm start -w @typeberry/test-runner -- w3f-davxy-071-bin.ts - name: Display results if: always() run: cat ./dist/w3f-davxy-071.txt diff --git a/bin/test-runner/common.ts b/bin/test-runner/common.ts index ad43962be..6333cda6c 100644 --- a/bin/test-runner/common.ts +++ b/bin/test-runner/common.ts @@ -117,11 +117,9 @@ export async function main( initialFiles: string[], directoryToScan: string, { - pattern = testFile.json, accepted, ignored, }: { - pattern?: testFile.json | testFile.bin; accepted?: string[]; ignored?: string[]; } = {}, @@ -134,7 +132,9 @@ export async function main( let testFiles = initialFiles; if (initialFiles.length === 0) { // scan the given directory for fallback tests - testFiles = await scanDir(relPath, directoryToScan, pattern); + testFiles = await scanDir(relPath, directoryToScan, [ + testFile.bin, testFile.json + ]); } logger.info`Preparing tests for ${testFiles.length} files.`; @@ -217,12 +217,12 @@ export async function main( return "Tests registered successfully"; } -async function scanDir(relPath: string, dir: string, filePattern: string): Promise { +async function scanDir(relPath: string, dir: string, filePatterns: string[]): Promise { try { const files = await fs.readdir(`${relPath}/${dir}`, { recursive: true, }); - return files.filter((f) => f.endsWith(filePattern)).map((f) => `${dir}/${f}`); + return files.filter((f) => filePatterns.some(pattern => f.endsWith(pattern))).map((f) => `${dir}/${f}`); } catch (e) { logger.error`Unable to find test vectors in ${relPath}/${dir}: ${e}`; return []; diff --git a/bin/test-runner/package.json b/bin/test-runner/package.json index 7e58dc2c3..53522b6da 100644 --- a/bin/test-runner/package.json +++ b/bin/test-runner/package.json @@ -38,7 +38,6 @@ "w3f-davxy:0.6.7": "GP_VERSION=0.6.7 tsx ./w3f-davxy-067.ts", "w3f-davxy:0.7.0": "GP_VERSION=0.7.0 tsx ./w3f-davxy-070.ts", "w3f-davxy:0.7.1": "GP_VERSION=0.7.1 tsx ./w3f-davxy-071.ts", - "w3f-davxy:0.7.1-bin": "GP_VERSION=0.7.1 tsx ./w3f-davxy-071-bin.ts", "jam-conformance:0.7.0": "GP_VERSION=0.7.0 tsx ./jam-conformance-070.ts", "jam-conformance:0.7.1": "GP_VERSION=0.7.1 tsx ./jam-conformance-071.ts", "jamduna:0.6.7": "GP_VERSION=0.6.7 TEST_SUITE=jamduna tsx ./jamduna-067.ts", diff --git a/bin/test-runner/w3f-davxy-071-bin.ts b/bin/test-runner/w3f-davxy-071-bin.ts deleted file mode 100644 index 1fe5049d5..000000000 --- a/bin/test-runner/w3f-davxy-071-bin.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { logger, main } from "./common.js"; -import { runners } from "./w3f/runners.js"; - -main(runners, process.argv.slice(2), "test-vectors/w3f-davxy_071", { - pattern: ".bin", - accepted: ["traces/fuzzy"], - ignored: [ - "genesis.bin", - "report.bin", - - "fuzzy/00000014.bin", // statistics + alot - "fuzzy/00000016.bin", // statistics + alot - "fuzzy/00000037.bin", // statistics + afew - "fuzzy/00000111.bin", // statistics - ], -}) - .then((r) => logger.log`${r}`) - .catch((e) => { - logger.error`${e}`; - process.exit(-1); - }); diff --git a/bin/test-runner/w3f-davxy-071.ts b/bin/test-runner/w3f-davxy-071.ts index 22d3aef61..3c3e3bf37 100644 --- a/bin/test-runner/w3f-davxy-071.ts +++ b/bin/test-runner/w3f-davxy-071.ts @@ -5,10 +5,11 @@ main(runners, process.argv.slice(2), "test-vectors/w3f-davxy_071", { accepted: ["traces", "codec", "stf"], ignored: [ "genesis.json", - "fuzzy/00000014.json", // statistics + alot - "fuzzy/00000016.json", // statistics + alot - "fuzzy/00000037.json", // statistics + afew - "fuzzy/00000111.json", // statistics + "genesis.bin", + "fuzzy/00000014", // statistics + alot + "fuzzy/00000016", // statistics + alot + "fuzzy/00000037", // statistics + afew + "fuzzy/00000111", // statistics ], }) .then((r) => logger.log`${r}`) From e7ea34945b2c157c9ad2645ac94a1204712e210b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Thu, 30 Oct 2025 22:53:00 +0100 Subject: [PATCH 10/15] Refactor file bin/json runner & scanning. --- bin/test-runner/common.ts | 69 ++++++++++++------- bin/test-runner/jam-conformance-070.ts | 1 + bin/test-runner/jam-conformance-071.ts | 1 + bin/test-runner/jamduna-067.ts | 5 +- bin/test-runner/javajam.ts | 5 +- bin/test-runner/package.json | 2 +- bin/test-runner/w3f-davxy-067.ts | 5 +- bin/test-runner/w3f-davxy-070.ts | 5 +- bin/test-runner/w3f-davxy-071.ts | 5 +- bin/test-runner/w3f.ts | 1 + package-lock.json | 22 +++--- .../core/pvm-interpreter-ananas/package.json | 2 +- 12 files changed, 79 insertions(+), 44 deletions(-) diff --git a/bin/test-runner/common.ts b/bin/test-runner/common.ts index 6333cda6c..731d93e9e 100644 --- a/bin/test-runner/common.ts +++ b/bin/test-runner/common.ts @@ -117,10 +117,15 @@ export async function main( initialFiles: string[], directoryToScan: string, { + patterns = [testFile.bin, testFile.json], accepted, ignored, }: { - accepted?: string[]; + patterns?: (testFile.bin | testFile.json)[]; + accepted?: { + [testFile.bin]?: string[]; + [testFile.json]?: string[]; + }; ignored?: string[]; } = {}, ) { @@ -132,9 +137,7 @@ export async function main( let testFiles = initialFiles; if (initialFiles.length === 0) { // scan the given directory for fallback tests - testFiles = await scanDir(relPath, directoryToScan, [ - testFile.bin, testFile.json - ]); + testFiles = await scanDir(relPath, directoryToScan, patterns); } logger.info`Preparing tests for ${testFiles.length} files.`; @@ -142,28 +145,41 @@ export async function main( const absolutePath = path.resolve(`${relPath}/${testFilePath}`); if (ignoredPatterns.some((x) => absolutePath.includes(x))) { - logger.log`Ignoring: ${absolutePath}`; - continue; + if (testFiles.length === 1) { + logger.info`Executing ignored file, because it was explicitly requested: ${absolutePath}`; + } else { + logger.log`Ignoring: ${absolutePath}`; + continue; + } } let testFileContent: testFile.Content; if (absolutePath.endsWith(testFile.bin)) { const content: Buffer = await fs.readFile(absolutePath); testFileContent = { - kind: ".bin", + kind: testFile.bin, content: new Uint8Array(content), }; } else { const content = await fs.readFile(absolutePath, "utf8"); testFileContent = { - kind: ".json", + kind: testFile.json, content: JSON.parse(content), }; } + // we accept a test file when: + // 1. No explicit `accepted` is defined + const isAccepted = + accepted === undefined || + // 2. No explicit `accepted` for the file kind is defined + accepted[testFileContent.kind] === undefined || + // 3. If the list is defined, we make sure that the path is on that list. + (accepted[testFileContent.kind] ?? []).some((x) => absolutePath.includes(x)); + const testVariants = prepareTest(runners, testFileContent, testFilePath, absolutePath); for (const test of testVariants) { - test.shouldSkip = accepted !== undefined && !accepted.some((x) => absolutePath.includes(x)); + test.shouldSkip = !isAccepted; tests.push(test); } } @@ -176,7 +192,7 @@ export async function main( aggregated.set(test.runner, sameRunner); } - const pathToReplace = new RegExp(`/.*${directoryToScan}/`); + const pathToReplace = new RegExp(`.*${directoryToScan}/`); logger.info`Running ${tests.length} tests.`; // run in parallel and generate results. @@ -219,10 +235,10 @@ export async function main( async function scanDir(relPath: string, dir: string, filePatterns: string[]): Promise { try { - const files = await fs.readdir(`${relPath}/${dir}`, { + const files = await fs.readdir(dir.startsWith("/") ? dir : `${relPath}/${dir}`, { recursive: true, }); - return files.filter((f) => filePatterns.some(pattern => f.endsWith(pattern))).map((f) => `${dir}/${f}`); + return files.filter((f) => filePatterns.some((pattern) => f.endsWith(pattern))).map((f) => `${dir}/${f}`); } catch (e) { logger.error`Unable to find test vectors in ${relPath}/${dir}: ${e}`; return []; @@ -248,20 +264,21 @@ function prepareTest( // NOTE This is not safe, but if the test does not specify // variants it means it doesn't care about them. const noneVariant = "" as V; + const matchingRunners: TestAndRunner[] = []; // Find the first runner that is able to parse the input data. for (const { path, parsers, run, variants, chainSpecs } of runners) { + // NOTE: this `if` statement is intended to speed up parsing of the test files + // instead of trying each and every runner, we make sure that the absolute + // path to the file includes each part of our "test path" definition. + if (!path.split("/").every((pathPart) => fullPath.includes(pathPart))) { + continue; + } const specs = chainSpecs.length > 0 ? chainSpecs : [tinyChainSpec]; - const matchPath = chainSpecs.length > 0; + const matchChainSpecPath = chainSpecs.length > 0; for (const chainSpec of specs) { - // NOTE: this `if` statement is intended to speed up parsing of the test files - // instead of trying each and every runner, we make sure that the absolute - // path to the file includes each part of our "test path" definition. - if (!path.split("/").every((pathPart) => fullPath.includes(pathPart))) { - continue; - } // if we care about the chain spec, we also need to match the path - if (matchPath && !fullPath.includes(chainSpec.name)) { + if (matchChainSpecPath && !fullPath.includes(chainSpec.name)) { continue; } @@ -269,7 +286,7 @@ function prepareTest( if (parser.kind === testFile.bin && testContent.kind === testFile.bin) { try { const parsedTest = Decoder.decodeObject(parser.codec, testContent.content, chainSpec); - return createTestDefinitions(path, run, variants, parsedTest, chainSpec); + matchingRunners.push(...createTestDefinitions(path, run, variants, parsedTest, chainSpec)); } catch (e) { handleError(path, e); } @@ -278,19 +295,19 @@ function prepareTest( if (parser.kind === testFile.json && testContent.kind === testFile.json) { try { const parsedTest = parseFromJson(testContent.content, parser.fromJson); - return createTestDefinitions(path, run, variants, parsedTest, chainSpec); + matchingRunners.push(...createTestDefinitions(path, run, variants, parsedTest, chainSpec)); } catch (e) { handleError(path, e); } } - - if (testContent.kind !== parser.kind) { - handleError(path, new Error(`Mismatching parser: got ${parser.kind}, need: ${testContent.kind}`)); - } } } } + if (matchingRunners.length > 0) { + return matchingRunners; + } + return [ { shouldSkip: false, diff --git a/bin/test-runner/jam-conformance-070.ts b/bin/test-runner/jam-conformance-070.ts index 64942bfa7..ec0e6046b 100644 --- a/bin/test-runner/jam-conformance-070.ts +++ b/bin/test-runner/jam-conformance-070.ts @@ -2,6 +2,7 @@ import { logger, main } from "./common.js"; import { runners } from "./w3f/runners.js"; main(runners, process.argv.slice(2), "test-vectors/jam-conformance/fuzz-reports/0.7.0/traces", { + patterns: [".json"], ignored: [ // CORRECT: note [seko] test rejected at block parsing stage, which is considered valid behavior "traces/1757063641/00000180.json", diff --git a/bin/test-runner/jam-conformance-071.ts b/bin/test-runner/jam-conformance-071.ts index 2a1265349..a9cfb2d18 100644 --- a/bin/test-runner/jam-conformance-071.ts +++ b/bin/test-runner/jam-conformance-071.ts @@ -2,6 +2,7 @@ import { logger, main } from "./common.js"; import { runners } from "./w3f/runners.js"; main(runners, process.argv.slice(2), "test-vectors/jam-conformance/fuzz-reports/0.7.1/traces", { + patterns: [".json"], ignored: [ // genesis file is unparsable "genesis.json", diff --git a/bin/test-runner/jamduna-067.ts b/bin/test-runner/jamduna-067.ts index f26df58ed..01f2f6b19 100644 --- a/bin/test-runner/jamduna-067.ts +++ b/bin/test-runner/jamduna-067.ts @@ -9,7 +9,10 @@ const runners = [ ].map((x) => x.build()); main(runners, process.argv.slice(2), "test-vectors/jamduna_067", { - accepted: ["traces/"], + patterns: [".json"], + accepted: { + ".json": ["traces/"], + }, ignored: [], }) .then((r) => logger.log`${r}`) diff --git a/bin/test-runner/javajam.ts b/bin/test-runner/javajam.ts index 79373ffbf..e590139fd 100644 --- a/bin/test-runner/javajam.ts +++ b/bin/test-runner/javajam.ts @@ -9,7 +9,10 @@ const runners = [ ].map((x) => x.build()); main(runners, process.argv.slice(2), "test-vectors/javajam", { - accepted: ["stf/state_transitions/"], + patterns: [".json"], + accepted: { + ".json": ["stf/state_transitions/"], + }, ignored: ["testnetKeys.json", "stf/blocks/", "erasure_coding/"], }) .then((r) => logger.log`${r}`) diff --git a/bin/test-runner/package.json b/bin/test-runner/package.json index 53522b6da..4c83161bf 100644 --- a/bin/test-runner/package.json +++ b/bin/test-runner/package.json @@ -32,7 +32,7 @@ "json-bigint-patch": "0.0.8" }, "scripts": { - "start": "tsx --test-timeout=600000 ./index.ts", + "start": "tsx --test-timeout=900000 ./index.ts", "w3f:0.6.7": "GP_VERSION=0.6.7 tsx ./w3f.ts", "w3f:0.7.1": "GP_VERSION=0.7.1 tsx ./w3f.ts", "w3f-davxy:0.6.7": "GP_VERSION=0.6.7 tsx ./w3f-davxy-067.ts", diff --git a/bin/test-runner/w3f-davxy-067.ts b/bin/test-runner/w3f-davxy-067.ts index c8f602e53..5efd9e704 100644 --- a/bin/test-runner/w3f-davxy-067.ts +++ b/bin/test-runner/w3f-davxy-067.ts @@ -2,7 +2,10 @@ import { logger, main } from "./common.js"; import { runners } from "./w3f/runners.js"; main(runners, process.argv.slice(2), "test-vectors/w3f-davxy_067", { - accepted: ["traces"], + patterns: [".json"], + accepted: { + ".json": ["traces"], + }, ignored: [ "genesis.json", // note [seko] storage tests await implementation of read/write diff --git a/bin/test-runner/w3f-davxy-070.ts b/bin/test-runner/w3f-davxy-070.ts index eff17b1a9..1e5a19e4a 100644 --- a/bin/test-runner/w3f-davxy-070.ts +++ b/bin/test-runner/w3f-davxy-070.ts @@ -2,7 +2,10 @@ import { logger, main } from "./common.js"; import { runners } from "./w3f/runners.js"; main(runners, process.argv.slice(2), "test-vectors/w3f-davxy_070", { - accepted: ["traces"], + patterns: [".json"], + accepted: { + ".json": ["traces"], + }, ignored: ["genesis.json"], }) .then((r) => logger.log`${r}`) diff --git a/bin/test-runner/w3f-davxy-071.ts b/bin/test-runner/w3f-davxy-071.ts index 3c3e3bf37..38c7c8575 100644 --- a/bin/test-runner/w3f-davxy-071.ts +++ b/bin/test-runner/w3f-davxy-071.ts @@ -2,7 +2,10 @@ import { logger, main } from "./common.js"; import { runners } from "./w3f/runners.js"; main(runners, process.argv.slice(2), "test-vectors/w3f-davxy_071", { - accepted: ["traces", "codec", "stf"], + accepted: { + ".json": ["traces", "codec", "stf"], + ".bin": ["traces"], + }, ignored: [ "genesis.json", "genesis.bin", diff --git a/bin/test-runner/w3f.ts b/bin/test-runner/w3f.ts index 790c4305b..4db263236 100644 --- a/bin/test-runner/w3f.ts +++ b/bin/test-runner/w3f.ts @@ -2,6 +2,7 @@ import { logger, main } from "./common.js"; import { runners } from "./w3f/runners.js"; main(runners, process.argv.slice(2), "test-vectors/w3f-fluffy", { + patterns: [".json"], ignored: [ "genesis.json", // invalid tests diff --git a/package-lock.json b/package-lock.json index bf805fa19..733a6081a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1171,6 +1171,16 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, + "node_modules/@fluffylabs/anan-as": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@fluffylabs/anan-as/-/anan-as-1.1.2.tgz", + "integrity": "sha512-nWTUZYb5VObqnNKwL04xsmXqDgN0inmQqAk2Ez+tQcq+i8TXyQi8cGqgVuNQVy1H2YoRJMNt5i5Aq+7nNnRxFg==", + "license": "MPL-2.0", + "dependencies": { + "@typeberry/lib": "^0.2.0", + "json-bigint-patch": "^0.0.8" + } + }, "node_modules/@humanfs/core": { "version": "0.19.1", "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", @@ -6649,7 +6659,7 @@ "version": "0.1.3", "license": "MPL-2.0", "dependencies": { - "@fluffylabs/anan-as": "^1.1.1-b8e442b", + "@fluffylabs/anan-as": "^1.1.2", "@typeberry/codec": "*", "@typeberry/numbers": "*", "@typeberry/pvm-interface": "*", @@ -6657,16 +6667,6 @@ "assemblyscript-loader": "^0.3.0" } }, - "packages/core/pvm-interpreter-ananas/node_modules/@fluffylabs/anan-as": { - "version": "1.1.1-b8e442b", - "resolved": "https://registry.npmjs.org/@fluffylabs/anan-as/-/anan-as-1.1.1-b8e442b.tgz", - "integrity": "sha512-grLT4xItHo/CB/RaJWiqhWrHzSXlmG1bCB06rXGoqOpMnMbh5ElVWf9/rRAvBmT7iZ/G+X3Alfa1EEXGZ3K7+Q==", - "license": "MPL-2.0", - "dependencies": { - "@typeberry/lib": "^0.2.0", - "json-bigint-patch": "^0.0.8" - } - }, "packages/core/pvm-program": { "name": "@typeberry/pvm-program", "version": "0.2.0", diff --git a/packages/core/pvm-interpreter-ananas/package.json b/packages/core/pvm-interpreter-ananas/package.json index e91766a26..eb8a98ee2 100644 --- a/packages/core/pvm-interpreter-ananas/package.json +++ b/packages/core/pvm-interpreter-ananas/package.json @@ -4,7 +4,7 @@ "description": "Anan-as PVM implementation.", "main": "index.ts", "dependencies": { - "@fluffylabs/anan-as": "^1.1.1-b8e442b", + "@fluffylabs/anan-as": "^1.1.2", "@typeberry/codec": "*", "@typeberry/numbers": "*", "@typeberry/pvm-interface": "*", From 35d272bed7037e7beea1b2448c48bc50f4b13405 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Thu, 30 Oct 2025 23:02:53 +0100 Subject: [PATCH 11/15] Don't require ananas for npm. --- bin/jam/build-for-npm.sh | 5 ++--- packages/jam/node/main-fuzz.ts | 3 ++- packages/jam/node/main-importer.ts | 3 ++- packages/jam/node/main.ts | 4 ++-- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/bin/jam/build-for-npm.sh b/bin/jam/build-for-npm.sh index add9d7b48..f97820335 100755 --- a/bin/jam/build-for-npm.sh +++ b/bin/jam/build-for-npm.sh @@ -19,7 +19,7 @@ rm -rf $DIST_FOLDER/* # TODO [ToDr] Temporary require anan-as before https://github.com/tomusdrw/anan-as/issues/99 # is resolved. # Build the main binary -BUILD="npx @vercel/ncc build -a -s -e lmdb -e @matrixai/quic -e @fluffylabs/anan-as -e tsx/esm/api" +BUILD="npx @vercel/ncc build -a -s -e lmdb -e @matrixai/quic -e tsx/esm/api" $BUILD ./bin/jam/index.ts -o $DIST_FOLDER # Fix un-compiled worker files to point to the ones we will compile manually. @@ -81,8 +81,7 @@ cat > ./package.json << EOF }, "dependencies": { "lmdb": "3.1.3", - "@matrixai/quic": "2.0.9", - "@fluffylabs/anan-as": "1.0.0-a9b8141" + "@matrixai/quic": "2.0.9" }, "homepage": "https://typeberry.dev", "author": "Fluffy Labs ", diff --git a/packages/jam/node/main-fuzz.ts b/packages/jam/node/main-fuzz.ts index e02fbbdc5..890fb1efa 100644 --- a/packages/jam/node/main-fuzz.ts +++ b/packages/jam/node/main-fuzz.ts @@ -1,6 +1,7 @@ import { type BlockView, Header, type HeaderHash, type StateRootHash, type TimeSlot } from "@typeberry/block"; import { Bytes } from "@typeberry/bytes"; import { Encoder } from "@typeberry/codec"; +import { PvmBackend } from "@typeberry/config"; import { type FuzzVersion, startFuzzTarget } from "@typeberry/ext-ipc"; import { v1 as fuzzV1 } from "@typeberry/fuzz-proto"; import { HASH_SIZE } from "@typeberry/hash"; @@ -31,7 +32,7 @@ export function getFuzzDetails() { export async function mainFuzz(fuzzConfig: FuzzConfig, withRelPath: (v: string) => string) { logger.info`💨 Fuzzer V${fuzzConfig.version} starting up.`; - logger.info`🖥️ PVM Backend: ${fuzzConfig.jamNodeConfig.pvmBackend}.`; + logger.info`🖥️ PVM Backend: ${PvmBackend[fuzzConfig.jamNodeConfig.pvmBackend]}.`; const { jamNodeConfig: config } = fuzzConfig; diff --git a/packages/jam/node/main-importer.ts b/packages/jam/node/main-importer.ts index 93ddf97ec..2f291f817 100644 --- a/packages/jam/node/main-importer.ts +++ b/packages/jam/node/main-importer.ts @@ -1,5 +1,6 @@ import type { BlockView, HeaderHash, StateRootHash } from "@typeberry/block"; import { Bytes } from "@typeberry/bytes"; +import { PvmBackend } from "@typeberry/config"; import { initWasm } from "@typeberry/crypto"; import { Blake2b, HASH_SIZE } from "@typeberry/hash"; import { createImporter } from "@typeberry/importer"; @@ -17,7 +18,7 @@ export async function mainImporter(config: JamConfig, withRelPath: (v: string) = logger.info`🫐 Typeberry ${packageJson.version}. GP: ${CURRENT_VERSION} (${CURRENT_SUITE})`; logger.info`🎸 Starting importer: ${config.nodeName}.`; - logger.info`🖥️ PVM Backend: ${config.pvmBackend}.`; + logger.info`🖥️ PVM Backend: ${PvmBackend[config.pvmBackend]}.`; const chainSpec = getChainSpec(config.node.flavor); const blake2b = await Blake2b.createHasher(); const omitSealVerification = false; diff --git a/packages/jam/node/main.ts b/packages/jam/node/main.ts index 3ed96d018..f3a9eb8e0 100755 --- a/packages/jam/node/main.ts +++ b/packages/jam/node/main.ts @@ -1,6 +1,6 @@ import { isMainThread } from "node:worker_threads"; import type { BlockView, HeaderHash, HeaderView, StateRootHash } from "@typeberry/block"; -import type { ChainSpec } from "@typeberry/config"; +import { type ChainSpec, PvmBackend } from "@typeberry/config"; import { initWasm } from "@typeberry/crypto"; import { Blake2b, type WithHash } from "@typeberry/hash"; import { type ImporterApi, ImporterConfig } from "@typeberry/importer"; @@ -33,7 +33,7 @@ export async function main(config: JamConfig, withRelPath: (v: string) => string logger.info`🫐 Typeberry ${packageJson.version}. GP: ${CURRENT_VERSION} (${CURRENT_SUITE})`; logger.info`🎸 Starting node: ${config.nodeName}.`; - logger.info`🖥️ PVM Backend: ${config.pvmBackend}.`; + logger.info`🖥️ PVM Backend: ${PvmBackend[config.pvmBackend]}.`; const chainSpec = getChainSpec(config.node.flavor); const blake2b = await Blake2b.createHasher(); if (config.node.databaseBasePath === undefined) { From fed7accbde870ebf290f4f8fb3d22929ca88c219 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Thu, 30 Oct 2025 23:18:14 +0100 Subject: [PATCH 12/15] Only run JSON traces. --- bin/test-runner/w3f-davxy-071.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/test-runner/w3f-davxy-071.ts b/bin/test-runner/w3f-davxy-071.ts index 38c7c8575..096a2ab25 100644 --- a/bin/test-runner/w3f-davxy-071.ts +++ b/bin/test-runner/w3f-davxy-071.ts @@ -2,9 +2,9 @@ import { logger, main } from "./common.js"; import { runners } from "./w3f/runners.js"; main(runners, process.argv.slice(2), "test-vectors/w3f-davxy_071", { + patterns: [".json"], accepted: { ".json": ["traces", "codec", "stf"], - ".bin": ["traces"], }, ignored: [ "genesis.json", From 1bcf4e5fa93928c33889ce076b7ca32a704cd8fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Fri, 31 Oct 2025 11:44:17 +0100 Subject: [PATCH 13/15] Increase tests timeout. --- bin/test-runner/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/test-runner/index.ts b/bin/test-runner/index.ts index c1c2cc2cd..ca84838a5 100644 --- a/bin/test-runner/index.ts +++ b/bin/test-runner/index.ts @@ -18,7 +18,7 @@ if (suiteToRun === undefined) { const stream = run({ files: [`${import.meta.dirname}/${suiteToRun}`], argv: process.argv.slice(3), - timeout: 10 * 60 * 1000, + timeout: 20 * 60 * 1000, concurrency: true, }).on("test:fail", () => { process.exitCode = 1; From 73234b0cbdaf2e9bf6cf0b314a3f2c36a384c92d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Mon, 3 Nov 2025 11:08:24 +0100 Subject: [PATCH 14/15] Bump ananas. --- package-lock.json | 8 ++++---- packages/core/pvm-interpreter-ananas/package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 733a6081a..bad90db59 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1172,9 +1172,9 @@ } }, "node_modules/@fluffylabs/anan-as": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@fluffylabs/anan-as/-/anan-as-1.1.2.tgz", - "integrity": "sha512-nWTUZYb5VObqnNKwL04xsmXqDgN0inmQqAk2Ez+tQcq+i8TXyQi8cGqgVuNQVy1H2YoRJMNt5i5Aq+7nNnRxFg==", + "version": "1.1.2-5acc613", + "resolved": "https://registry.npmjs.org/@fluffylabs/anan-as/-/anan-as-1.1.2-5acc613.tgz", + "integrity": "sha512-bEJUOpkpU6bu6ls0PpbLO3YYzHDwajk+GhsdSg6Dsi8U2v8On5d8ZRbLrYShSWPDdmf01g57H1/Sm7OUWRTH3w==", "license": "MPL-2.0", "dependencies": { "@typeberry/lib": "^0.2.0", @@ -6659,7 +6659,7 @@ "version": "0.1.3", "license": "MPL-2.0", "dependencies": { - "@fluffylabs/anan-as": "^1.1.2", + "@fluffylabs/anan-as": "^1.1.2-5acc613", "@typeberry/codec": "*", "@typeberry/numbers": "*", "@typeberry/pvm-interface": "*", diff --git a/packages/core/pvm-interpreter-ananas/package.json b/packages/core/pvm-interpreter-ananas/package.json index eb8a98ee2..ddfba5bba 100644 --- a/packages/core/pvm-interpreter-ananas/package.json +++ b/packages/core/pvm-interpreter-ananas/package.json @@ -4,7 +4,7 @@ "description": "Anan-as PVM implementation.", "main": "index.ts", "dependencies": { - "@fluffylabs/anan-as": "^1.1.2", + "@fluffylabs/anan-as": "^1.1.2-5acc613", "@typeberry/codec": "*", "@typeberry/numbers": "*", "@typeberry/pvm-interface": "*", From 3505b5232ca04096b8cb86eb3739f22009beb55d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Mon, 3 Nov 2025 12:35:40 +0100 Subject: [PATCH 15/15] Bump ananas release version. --- package-lock.json | 8 ++++---- packages/core/pvm-interpreter-ananas/package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index bad90db59..f6b5e3a02 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1172,9 +1172,9 @@ } }, "node_modules/@fluffylabs/anan-as": { - "version": "1.1.2-5acc613", - "resolved": "https://registry.npmjs.org/@fluffylabs/anan-as/-/anan-as-1.1.2-5acc613.tgz", - "integrity": "sha512-bEJUOpkpU6bu6ls0PpbLO3YYzHDwajk+GhsdSg6Dsi8U2v8On5d8ZRbLrYShSWPDdmf01g57H1/Sm7OUWRTH3w==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@fluffylabs/anan-as/-/anan-as-1.1.3.tgz", + "integrity": "sha512-/EE8fRqL1i/f63DKO7gSTqLy9OpVoQf1bknT7MsQDB77uyKSwhtEF8Q7mIp8TDKwxUJXpQ1V3iWHHfLMo9R5ag==", "license": "MPL-2.0", "dependencies": { "@typeberry/lib": "^0.2.0", @@ -6659,7 +6659,7 @@ "version": "0.1.3", "license": "MPL-2.0", "dependencies": { - "@fluffylabs/anan-as": "^1.1.2-5acc613", + "@fluffylabs/anan-as": "^1.1.3", "@typeberry/codec": "*", "@typeberry/numbers": "*", "@typeberry/pvm-interface": "*", diff --git a/packages/core/pvm-interpreter-ananas/package.json b/packages/core/pvm-interpreter-ananas/package.json index ddfba5bba..eb270f81d 100644 --- a/packages/core/pvm-interpreter-ananas/package.json +++ b/packages/core/pvm-interpreter-ananas/package.json @@ -4,7 +4,7 @@ "description": "Anan-as PVM implementation.", "main": "index.ts", "dependencies": { - "@fluffylabs/anan-as": "^1.1.2-5acc613", + "@fluffylabs/anan-as": "^1.1.3", "@typeberry/codec": "*", "@typeberry/numbers": "*", "@typeberry/pvm-interface": "*",