diff --git a/bin/jam/args.test.ts b/bin/jam/args.test.ts index 22bd1e7cb..44e754379 100644 --- a/bin/jam/args.test.ts +++ b/bin/jam/args.test.ts @@ -1,6 +1,6 @@ import assert from "node:assert"; import { describe, it } from "node:test"; - +import { PvmBackend, PvmBackendNames } from "@typeberry/config"; import { NODE_DEFAULTS } from "@typeberry/config-node"; import { tryAsU16 } from "@typeberry/numbers"; import { deepEqual } from "@typeberry/utils"; @@ -11,6 +11,7 @@ describe("CLI", () => { const defaultOptions: SharedOptions = { nodeName: NODE_DEFAULTS.name, configPath: NODE_DEFAULTS.config, + pvm: NODE_DEFAULTS.pvm, }; it("should start with default arguments", () => { @@ -82,6 +83,30 @@ describe("CLI", () => { }); }); + it("should parse pvm option", () => { + const args = parse(["--pvm=ananas"]); + + deepEqual(args, { + command: Command.Run, + args: { + ...defaultOptions, + pvm: PvmBackend.Ananas, + }, + }); + }); + + it("should throw on invalid pvm option", () => { + const pvms = PvmBackendNames.join(", "); + assert.throws( + () => { + const _args = parse(["--pvm=unimplemented"]); + }, + { + message: `Invalid value 'unimplemented' for option 'pvm': Error: Use one of ${pvms}`, + }, + ); + }); + it("should throw on missing output path", () => { assert.throws( () => { diff --git a/bin/jam/args.ts b/bin/jam/args.ts index 2d6423306..1eef39b93 100644 --- a/bin/jam/args.ts +++ b/bin/jam/args.ts @@ -1,3 +1,4 @@ +import { type PvmBackend, PvmBackendNames } from "@typeberry/config"; import { DEFAULT_CONFIG, DEV_CONFIG, NODE_DEFAULTS } from "@typeberry/config-node"; import { isU16, type U16 } from "@typeberry/numbers"; import minimist from "minimist"; @@ -18,6 +19,8 @@ Options: [default: ${NODE_DEFAULTS.name}] --config Path to a config file or one of: ['${DEV_CONFIG}', '${DEFAULT_CONFIG}']. [default: ${NODE_DEFAULTS.config}] + --pvm PVM Backend, one of: [${PvmBackendNames.join(", ")}]. + [default: ${PvmBackendNames[NODE_DEFAULTS.pvm]}] `; /** Command to execute. */ @@ -37,6 +40,7 @@ export enum Command { export type SharedOptions = { nodeName: string; configPath: string; + pvm: PvmBackend; }; export type Arguments = @@ -79,10 +83,23 @@ function parseSharedOptions( (v) => (v === DEV_CONFIG || v === DEFAULT_CONFIG ? v : withRelPath(v)), defaultConfig, ); + const { pvm } = parseStringOption( + args, + "pvm", + (v) => { + const pvm = PvmBackendNames.indexOf(v); + if (pvm >= 0) { + return pvm as PvmBackend; + } + throw Error(`Use one of ${PvmBackendNames.join(", ")}`); + }, + NODE_DEFAULTS.pvm, + ); return { nodeName: name, configPath: config, + pvm, }; } diff --git a/bin/jam/build-for-npm.sh b/bin/jam/build-for-npm.sh index 1fe6f49ad..add9d7b48 100755 --- a/bin/jam/build-for-npm.sh +++ b/bin/jam/build-for-npm.sh @@ -16,9 +16,10 @@ DIST_FOLDER=./dist/jam mkdir $DIST_FOLDER || true 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 tsx/esm/api" +BUILD="npx @vercel/ncc build -a -s -e lmdb -e @matrixai/quic -e @fluffylabs/anan-as -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. @@ -80,7 +81,8 @@ cat > ./package.json << EOF }, "dependencies": { "lmdb": "3.1.3", - "@matrixai/quic": "2.0.9" + "@matrixai/quic": "2.0.9", + "@fluffylabs/anan-as": "1.0.0-a9b8141" }, "homepage": "https://typeberry.dev", "author": "Fluffy Labs ", diff --git a/bin/jam/index.ts b/bin/jam/index.ts index 309b4e0ca..6f5b2eb9e 100755 --- a/bin/jam/index.ts +++ b/bin/jam/index.ts @@ -63,6 +63,7 @@ async function prepareConfigFile(args: Arguments, blake2b: Blake2b): Promise { nodeName: NODE_DEFAULTS.name, nodeConfig: loadConfig(NODE_DEFAULTS.config), devConfig: DEFAULT_DEV_CONFIG, + pvmBackend: NODE_DEFAULTS.pvm, }); const key = Bytes.fill(SEED_SIZE, 1).asOpaque(); diff --git a/bin/tci/index.ts b/bin/tci/index.ts index a7170eb9a..5d939124e 100755 --- a/bin/tci/index.ts +++ b/bin/tci/index.ts @@ -80,7 +80,12 @@ export function createJamConfig(argv: CommonArguments): node.JamConfig { }; } - return node.JamConfig.new({ nodeName: NODE_DEFAULTS.name, nodeConfig, devConfig }); + return node.JamConfig.new({ + nodeName: NODE_DEFAULTS.name, + nodeConfig, + devConfig, + pvmBackend: NODE_DEFAULTS.pvm, + }); } if (import.meta.url === pathToFileURL(process.argv[1]).href) { diff --git a/bin/test-runner/package.json b/bin/test-runner/package.json index 407cbe911..53522b6da 100644 --- a/bin/test-runner/package.json +++ b/bin/test-runner/package.json @@ -10,6 +10,7 @@ "@typeberry/codec": "*", "@typeberry/collections": "*", "@typeberry/config": "*", + "@typeberry/config-node": "*", "@typeberry/crypto": "*", "@typeberry/database": "*", "@typeberry/disputes": "*", @@ -18,6 +19,7 @@ "@typeberry/json-parser": "*", "@typeberry/logger": "*", "@typeberry/numbers": "*", + "@typeberry/pvm-interface": "*", "@typeberry/pvm-interpreter": "*", "@typeberry/safrole": "*", "@typeberry/shuffling": "*", diff --git a/bin/test-runner/w3f/accumulate.ts b/bin/test-runner/w3f/accumulate.ts index f216732c3..9297f8638 100644 --- a/bin/test-runner/w3f/accumulate.ts +++ b/bin/test-runner/w3f/accumulate.ts @@ -12,7 +12,7 @@ import type { WorkPackageHash } from "@typeberry/block/refine-context.js"; import type { WorkReport } from "@typeberry/block/work-report.js"; import { fromJson, workReportFromJson } from "@typeberry/block-json"; import { asKnownSize, HashSet } from "@typeberry/collections"; -import type { ChainSpec } from "@typeberry/config"; +import { type ChainSpec, PvmBackend, PvmBackendNames } from "@typeberry/config"; import { Blake2b } from "@typeberry/hash"; import { type FromJson, json } from "@typeberry/json-parser"; import type { InMemoryService } from "@typeberry/state"; @@ -142,8 +142,15 @@ export class AccumulateTest { } export async function runAccumulateTest(test: AccumulateTest, path: string) { - const chainSpec = getChainSpec(path); + await runAccumulateInternal(test, path, PvmBackend.BuiltIn); +} +export async function runAccumulateTestAnanas(test: AccumulateTest, path: string) { + await runAccumulateInternal(test, path, PvmBackend.Ananas); +} + +async function runAccumulateInternal(test: AccumulateTest, path: string, pvm: PvmBackend) { + const chainSpec = getChainSpec(path); /** * entropy has to be moved to input because state is incompatibile - * in test state we have: `entropy: EntropyHash;` @@ -152,19 +159,19 @@ export async function runAccumulateTest(test: AccumulateTest, path: string) { */ const entropy = test.pre_state.entropy; - const state = TestState.toAccumulateState(test.pre_state as TestState, chainSpec); - const accumulate = new Accumulate(chainSpec, await Blake2b.createHasher(), state); + 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(); const result = await accumulate.transition({ ...test.input, entropy }); - if (result.isError) { - assert.fail(`Expected successfull accumulation, got: ${result}`); + assert.fail(`Expected successfull accumulation for ${PvmBackendNames[pvm]}, got: ${result}`); } - const accumulateRoot = await accumulateOutput.transition({ accumulationOutputLog: result.ok.accumulationOutputLog }); + const accumulateRoot = await accumulateOutput.transition({ + accumulationOutputLog: result.ok.accumulationOutputLog, + }); state.applyUpdate(result.ok.stateUpdate); - - const post_state = TestState.toAccumulateState(test.post_state as TestState, chainSpec); - deepEqual(state, post_state); deepEqual(accumulateRoot, test.output.ok); } diff --git a/bin/test-runner/w3f/pvm-gas-cost.ts b/bin/test-runner/w3f/pvm-gas-cost.ts index 2ab18784e..e2afcf9b5 100644 --- a/bin/test-runner/w3f/pvm-gas-cost.ts +++ b/bin/test-runner/w3f/pvm-gas-cost.ts @@ -1,7 +1,8 @@ import assert from "node:assert"; import { fromJson } from "@typeberry/block-json"; import { type FromJson, json } from "@typeberry/json-parser"; -import { Interpreter, tryAsGas } from "@typeberry/pvm-interpreter"; +import { tryAsGas } from "@typeberry/pvm-interface"; +import { Interpreter } from "@typeberry/pvm-interpreter"; export class PvmGasCostTest { static fromJson: FromJson = { @@ -15,7 +16,7 @@ export class PvmGasCostTest { export async function runPvmGasCostTest(testContent: PvmGasCostTest) { const pvm = new Interpreter(); - pvm.reset(testContent.program, 0, tryAsGas(1000)); + pvm.resetGeneric(testContent.program, 0, tryAsGas(1000)); const blockGasCosts = pvm.calculateBlockGasCost(); diff --git a/bin/test-runner/w3f/pvm.ts b/bin/test-runner/w3f/pvm.ts index 235c94b51..cd51d7983 100644 --- a/bin/test-runner/w3f/pvm.ts +++ b/bin/test-runner/w3f/pvm.ts @@ -1,14 +1,14 @@ import assert from "node:assert"; import { fromJson } from "@typeberry/block-json"; import { type FromJson, json } from "@typeberry/json-parser"; -import { Interpreter, tryAsGas } from "@typeberry/pvm-interpreter"; +import { MAX_MEMORY_INDEX, Status, tryAsGas } from "@typeberry/pvm-interface"; +import { Interpreter } from "@typeberry/pvm-interpreter"; import { MemoryBuilder } from "@typeberry/pvm-interpreter/memory/index.js"; -import { MAX_MEMORY_INDEX, PAGE_SIZE } from "@typeberry/pvm-interpreter/memory/memory-consts.js"; +import { PAGE_SIZE } from "@typeberry/pvm-interpreter/memory/memory-consts.js"; import { tryAsMemoryIndex, tryAsSbrkIndex } from "@typeberry/pvm-interpreter/memory/memory-index.js"; import { getPageNumber } from "@typeberry/pvm-interpreter/memory/memory-utils.js"; import { type PageNumber, tryAsPageNumber } from "@typeberry/pvm-interpreter/memory/pages/page-utils.js"; import { Registers } from "@typeberry/pvm-interpreter/registers.js"; -import { Status } from "@typeberry/pvm-interpreter/status.js"; import { safeAllocUint8Array } from "@typeberry/utils"; class MemoryChunkItem { @@ -113,12 +113,12 @@ export async function runPvmTest(testContent: PvmTest) { return "halt"; }; - pvm.reset(testContent.program, testContent["initial-pc"], tryAsGas(testContent["initial-gas"]), regs, memory); + pvm.resetGeneric(testContent.program, testContent["initial-pc"], tryAsGas(testContent["initial-gas"]), regs, memory); pvm.runProgram(); - assert.strictEqual(pvm.getGas(), BigInt(testContent["expected-gas"])); + assert.strictEqual(pvm.gas.get(), BigInt(testContent["expected-gas"])); assert.strictEqual(pvm.getPC(), testContent["expected-pc"]); - assert.deepStrictEqual(pvm.getRegisters().getAllU64(), testContent["expected-regs"]); + assert.deepStrictEqual(pvm.registers.getAllU64(), testContent["expected-regs"]); const testStatus = mapPvmStatus(pvm.getStatus()); const exitParam = pvm.getExitParam(); diff --git a/bin/test-runner/w3f/runners.ts b/bin/test-runner/w3f/runners.ts index befd1046e..b214e3c51 100644 --- a/bin/test-runner/w3f/runners.ts +++ b/bin/test-runner/w3f/runners.ts @@ -14,7 +14,7 @@ import { import { fullChainSpec, tinyChainSpec } from "@typeberry/config"; import { runner } from "../common.js"; import { runStateTransition, StateTransition } from "../state-transition/state-transition.js"; -import { AccumulateTest, runAccumulateTest } from "./accumulate.js"; +import { AccumulateTest, runAccumulateTest, runAccumulateTestAnanas } from "./accumulate.js"; import { AssurancesTestFull, AssurancesTestTiny, runAssurancesTestFull, runAssurancesTestTiny } from "./assurances.js"; import { AuthorizationsTest, runAuthorizationsTest } from "./authorizations.js"; import { @@ -50,6 +50,7 @@ import { runTrieTest, trieTestSuiteFromJson } from "./trie.js"; export const runners = [ runner("accumulate", AccumulateTest.fromJson, runAccumulateTest), + runner("accumulate_ananas", AccumulateTest.fromJson, runAccumulateTestAnanas), runner("assurances/tiny", AssurancesTestTiny.fromJson, runAssurancesTestTiny), runner("assurances/full", AssurancesTestFull.fromJson, runAssurancesTestFull), runner("authorizations", AuthorizationsTest.fromJson, runAuthorizationsTest), diff --git a/package-lock.json b/package-lock.json index f30062c20..4848fd75d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -35,7 +35,9 @@ "packages/core/ordering", "packages/core/pvm-debugger-adapter", "packages/core/pvm-host-calls", + "packages/core/pvm-interface", "packages/core/pvm-interpreter", + "packages/core/pvm-interpreter-ananas", "packages/core/pvm-program", "packages/core/pvm-spi-decoder", "packages/core/shuffling", @@ -236,7 +238,9 @@ "version": "0.2.0", "license": "MPL-2.0", "dependencies": { - "@typeberry/pvm-interpreter": "*" + "@typeberry/pvm-interface": "*", + "@typeberry/pvm-interpreter": "*", + "@typeberry/pvm-interpreter-ananas": "*" } }, "bin/rpc": { @@ -299,6 +303,7 @@ "@typeberry/codec": "*", "@typeberry/collections": "*", "@typeberry/config": "*", + "@typeberry/config-node": "*", "@typeberry/crypto": "*", "@typeberry/database": "*", "@typeberry/disputes": "*", @@ -307,6 +312,7 @@ "@typeberry/json-parser": "*", "@typeberry/logger": "*", "@typeberry/numbers": "*", + "@typeberry/pvm-interface": "*", "@typeberry/pvm-interpreter": "*", "@typeberry/safrole": "*", "@typeberry/shuffling": "*", @@ -2425,10 +2431,18 @@ "resolved": "packages/core/pvm-host-calls", "link": true }, + "node_modules/@typeberry/pvm-interface": { + "resolved": "packages/core/pvm-interface", + "link": true + }, "node_modules/@typeberry/pvm-interpreter": { "resolved": "packages/core/pvm-interpreter", "link": true }, + "node_modules/@typeberry/pvm-interpreter-ananas": { + "resolved": "packages/core/pvm-interpreter-ananas", + "link": true + }, "node_modules/@typeberry/pvm-program": { "resolved": "packages/core/pvm-program", "link": true @@ -2536,6 +2550,12 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/long": { + "version": "3.0.32", + "resolved": "https://registry.npmjs.org/@types/long/-/long-3.0.32.tgz", + "integrity": "sha512-ZXyOOm83p7X8p3s0IYM3VeueNmHpkk/yMlP8CLeOnEcu6hIwPH7YjZBvhQkR0ZFS2DqZAxKtJ/M5fcuv3OU5BA==", + "license": "MIT" + }, "node_modules/@types/minimist": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.5.tgz", @@ -2558,6 +2578,12 @@ "integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==", "license": "MIT" }, + "node_modules/@types/webassembly-js-api": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/@types/webassembly-js-api/-/webassembly-js-api-0.0.1.tgz", + "integrity": "sha512-QPUg0o1GcGl035Z+lhje3uMapJh4/MqYQPLo/5wB4XwJbRQXkYMPVlDWZwFxy3CatnBIzaK7eFHpRn7tS83xzg==", + "license": "MIT" + }, "node_modules/@types/ws": { "version": "8.18.1", "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", @@ -3016,6 +3042,17 @@ "node": ">=12.0.0" } }, + "node_modules/assemblyscript-loader": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/assemblyscript-loader/-/assemblyscript-loader-0.3.0.tgz", + "integrity": "sha512-Z4320V+ImJqtCHO9M1ZsZY931YQ+Dq3/KmwgB49DEHrdr8AD+pF+9VSiQio5Wa8YmMOPP8kLjZ1rqTTvSlylYQ==", + "license": "Apache-2.0", + "dependencies": { + "@types/long": "^3.0.32", + "@types/webassembly-js-api": "0.0.1", + "long": "^3.2.0" + } + }, "node_modules/astral-regex": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", @@ -5004,6 +5041,15 @@ "node": ">=8" } }, + "node_modules/long": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/long/-/long-3.2.0.tgz", + "integrity": "sha512-ZYvPPOMqUwPoDsbJaR10iQJYnMuZhRTvHYl62ErLIEX7RgFlziSBUUvrt3OVfc47QlHHpzPZYP17g3Fv7oeJkg==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.6" + } + }, "node_modules/magic-string": { "version": "0.30.17", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", @@ -6552,6 +6598,7 @@ "@typeberry/jam-host-calls": "*", "@typeberry/numbers": "*", "@typeberry/pvm-host-calls": "*", + "@typeberry/pvm-interface": "*", "@typeberry/pvm-interpreter": "*", "@typeberry/pvm-program": "*", "@typeberry/pvm-spi-decoder": "*", @@ -6563,14 +6610,26 @@ "version": "0.2.0", "license": "MPL-2.0", "dependencies": { + "@typeberry/config": "^0.2.0", "@typeberry/logger": "*", "@typeberry/numbers": "*", + "@typeberry/pvm-interface": "*", "@typeberry/pvm-interpreter": "*", + "@typeberry/pvm-interpreter-ananas": "*", "@typeberry/pvm-program": "*", "@typeberry/pvm-spi-decoder": "*", "@typeberry/utils": "*" } }, + "packages/core/pvm-interface": { + "name": "@typeberry/pvm-interface", + "version": "0.1.3", + "license": "MPL-2.0", + "dependencies": { + "@typeberry/numbers": "*", + "@typeberry/utils": "*" + } + }, "packages/core/pvm-interpreter": { "name": "@typeberry/pvm-interpreter", "version": "0.2.0", @@ -6580,9 +6639,34 @@ "@typeberry/codec": "*", "@typeberry/logger": "*", "@typeberry/numbers": "*", + "@typeberry/pvm-interface": "*", + "@typeberry/pvm-program": "*", "@typeberry/utils": "*" } }, + "packages/core/pvm-interpreter-ananas": { + "name": "@typeberry/pvm-interpreter-ananas", + "version": "0.1.3", + "license": "MPL-2.0", + "dependencies": { + "@fluffylabs/anan-as": "^1.0.0-a9b8141", + "@typeberry/codec": "*", + "@typeberry/numbers": "*", + "@typeberry/pvm-interface": "*", + "@typeberry/utils": "*", + "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", @@ -6918,6 +7002,7 @@ "@typeberry/logger": "*", "@typeberry/numbers": "*", "@typeberry/pvm-host-calls": "*", + "@typeberry/pvm-interface": "*", "@typeberry/pvm-interpreter": "*", "@typeberry/state": "*", "@typeberry/utils": "*" @@ -7108,6 +7193,7 @@ "@typeberry/mmr": "*", "@typeberry/numbers": "*", "@typeberry/pvm-host-calls": "*", + "@typeberry/pvm-interface": "*", "@typeberry/pvm-interpreter": "*", "@typeberry/pvm-program": "*", "@typeberry/safrole": "*", @@ -7471,6 +7557,7 @@ "@typeberry/codec": "*", "@typeberry/collections": "*", "@typeberry/config": "*", + "@typeberry/config-node": "*", "@typeberry/crypto": "*", "@typeberry/database": "*", "@typeberry/hash": "*", diff --git a/package.json b/package.json index 274fa346e..a9797ff90 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,9 @@ "packages/core/ordering", "packages/core/pvm-debugger-adapter", "packages/core/pvm-host-calls", + "packages/core/pvm-interface", "packages/core/pvm-interpreter", + "packages/core/pvm-interpreter-ananas", "packages/core/pvm-program", "packages/core/pvm-spi-decoder", "packages/core/shuffling", diff --git a/packages/README.md b/packages/README.md index 937de2ac6..c5232b2de 100644 --- a/packages/README.md +++ b/packages/README.md @@ -23,7 +23,9 @@ Contains fundamental packages that provide low-level functionality and utilities - **ordering** - Ordering and sorting utilities - **pvm-debugger-adapter** - PVM debugger adapter - **pvm-host-calls** - PVM host call implementations +- **pvm-interface** - PVM interface definitions - **pvm-interpreter** - PVM interpreter functionality +- **pvm-interpreter-ananas** - Ananas PVM interpreter implementation - **pvm-program** - PVM program utilities - **pvm-spi-decoder** - PVM SPI decoder - **shuffling** - Shuffling algorithms @@ -41,6 +43,7 @@ Contains JAM (Join-Accumulate Machine) specific packages: - **database-lmdb** - LMDB database implementation - **fuzz-proto** - Fuzzing protocol implementation - **jam-host-calls** - JAM-specific host calls +- **jamnp-protocol** - JAM network protocol definitions - **jamnp-s** - JAM network protocol implementation - **node** - JAM node implementation - **safrole** - Safrole consensus mechanism diff --git a/packages/core/pvm-debugger-adapter/debugger-adapter.ts b/packages/core/pvm-debugger-adapter/debugger-adapter.ts index 601238207..22f6c6eb1 100644 --- a/packages/core/pvm-debugger-adapter/debugger-adapter.ts +++ b/packages/core/pvm-debugger-adapter/debugger-adapter.ts @@ -1,8 +1,7 @@ +import { Status, tryAsGas } from "@typeberry/pvm-interface"; import { Interpreter, type Memory, tryAsMemoryIndex } from "@typeberry/pvm-interpreter"; -import { tryAsGas } from "@typeberry/pvm-interpreter/gas.js"; import { PAGE_SIZE } from "@typeberry/pvm-interpreter/memory/memory-consts.js"; import { Registers } from "@typeberry/pvm-interpreter/registers.js"; -import { Status } from "@typeberry/pvm-interpreter/status.js"; import { check, safeAllocUint8Array } from "@typeberry/utils"; export class DebuggerAdapter { @@ -18,11 +17,11 @@ export class DebuggerAdapter { } resetGeneric(rawProgram: Uint8Array, flatRegisters: Uint8Array, initialGas: bigint) { - this.pvm.reset(rawProgram, 0, tryAsGas(initialGas), new Registers(flatRegisters)); + this.pvm.resetGeneric(rawProgram, 0, tryAsGas(initialGas), new Registers(flatRegisters)); } reset(rawProgram: Uint8Array, pc: number, gas: bigint, maybeRegisters?: Registers, maybeMemory?: Memory) { - this.pvm.reset(rawProgram, pc, tryAsGas(gas), maybeRegisters, maybeMemory); + this.pvm.resetGeneric(rawProgram, pc, tryAsGas(gas), maybeRegisters, maybeMemory); } getPageDump(pageNumber: number): null | Uint8Array { @@ -45,7 +44,7 @@ export class DebuggerAdapter { } setMemory(address: number, value: Uint8Array) { - this.pvm.getMemory().storeFrom(tryAsMemoryIndex(address), value); + this.pvm.memory.storeFrom(tryAsMemoryIndex(address), value); } getExitArg(): number { @@ -72,11 +71,11 @@ export class DebuggerAdapter { } getRegisters(): BigUint64Array { - return this.pvm.getRegisters().getAllU64(); + return this.pvm.registers.getAllU64(); } setRegisters(registers: Uint8Array) { - this.pvm.getRegisters().copyFrom(new Registers(registers)); + this.pvm.registers.copyFrom(new Registers(registers)); } getProgramCounter(): number { @@ -88,10 +87,10 @@ export class DebuggerAdapter { } getGasLeft(): bigint { - return BigInt(this.pvm.getGas()); + return BigInt(this.pvm.gas.get()); } setGasLeft(gas: bigint) { - this.pvm.getGasCounter().set(tryAsGas(gas)); + this.pvm.gas.set(tryAsGas(gas)); } } diff --git a/packages/core/pvm-debugger-adapter/index.ts b/packages/core/pvm-debugger-adapter/index.ts index 7c2bffcbb..bc4f317b9 100644 --- a/packages/core/pvm-debugger-adapter/index.ts +++ b/packages/core/pvm-debugger-adapter/index.ts @@ -3,7 +3,8 @@ export * as bytes from "@typeberry/bytes"; export * as hash from "@typeberry/hash"; export * from "@typeberry/jam-host-calls"; export * as numbers from "@typeberry/numbers"; -export { HostCallMemory, HostCallRegisters, IHostCallMemory, IHostCallRegisters } from "@typeberry/pvm-host-calls"; +export { HostCallMemory, HostCallRegisters } from "@typeberry/pvm-host-calls"; +export { NO_OF_REGISTERS } from "@typeberry/pvm-interface/registers.js"; export * as interpreter from "@typeberry/pvm-interpreter"; export { Args, ArgsDecoder } from "@typeberry/pvm-interpreter/args-decoder/args-decoder.js"; export { createResults } from "@typeberry/pvm-interpreter/args-decoder/args-decoding-results.js"; @@ -15,7 +16,7 @@ export { instructionArgumentTypeMap } from "@typeberry/pvm-interpreter/args-deco export { BasicBlocks } from "@typeberry/pvm-interpreter/basic-blocks/index.js"; export { Mask } from "@typeberry/pvm-interpreter/program-decoder/mask.js"; export { ProgramDecoder } from "@typeberry/pvm-interpreter/program-decoder/program-decoder.js"; -export { NO_OF_REGISTERS, Registers } from "@typeberry/pvm-interpreter/registers.js"; +export { Registers } from "@typeberry/pvm-interpreter/registers.js"; export { extractCodeAndMetadata, Program } from "@typeberry/pvm-program"; export { decodeStandardProgram, MemorySegment, SpiMemory, SpiProgram } from "@typeberry/pvm-spi-decoder"; export * from "@typeberry/utils/debug.js"; diff --git a/packages/core/pvm-debugger-adapter/package.json b/packages/core/pvm-debugger-adapter/package.json index 32b96f03e..f243af5f8 100644 --- a/packages/core/pvm-debugger-adapter/package.json +++ b/packages/core/pvm-debugger-adapter/package.json @@ -10,6 +10,7 @@ "@typeberry/jam-host-calls": "*", "@typeberry/numbers": "*", "@typeberry/pvm-host-calls": "*", + "@typeberry/pvm-interface": "*", "@typeberry/pvm-interpreter": "*", "@typeberry/pvm-program": "*", "@typeberry/pvm-spi-decoder": "*", diff --git a/packages/core/pvm-host-calls/bin.ts b/packages/core/pvm-host-calls/bin.ts index 3debefec2..c8d834ace 100644 --- a/packages/core/pvm-host-calls/bin.ts +++ b/packages/core/pvm-host-calls/bin.ts @@ -1,31 +1,30 @@ -import { tryAsGas } from "@typeberry/pvm-interpreter/gas.js"; -import { Program } from "@typeberry/pvm-program"; +import { PvmBackend } from "@typeberry/config"; +import { tryAsGas } from "@typeberry/pvm-interface"; import { HostCalls } from "./host-calls.js"; import { HostCallsManager, NoopMissing } from "./host-calls-manager.js"; import { InterpreterInstanceManager } from "./interpreter-instance-manager.js"; const hostCalls = new HostCallsManager({ missing: new NoopMissing(), handlers: [] }); -const pvmInstanceManager = new InterpreterInstanceManager(1); +const pvmInstanceManager = await InterpreterInstanceManager.new(PvmBackend.BuiltIn); const pvmHostCallExtension = new HostCalls(pvmInstanceManager, hostCalls); const program = new Uint8Array([ - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x1, 0x0, 0x0, 0xf9, 0x0, 0x0, 0x0, 0x0, 0x0, 0x80, 0xd9, 0x5, 0x12, 0x0, - 0x0, 0x0, 0x5, 0x11, 0x0, 0x0, 0x0, 0x5, 0xa3, 0x0, 0x0, 0x0, 0x5, 0xc6, 0x0, 0x4, 0x7, 0x13, 0x0, 0x2, 0x11, 0xa4, - 0x3, 0x10, 0x58, 0x3, 0x15, 0x54, 0x3, 0x16, 0x50, 0xd, 0x11, 0x1c, 0xd, 0x11, 0x18, 0xd, 0x11, 0x14, 0xd, 0x11, 0x10, - 0xd, 0x11, 0xc, 0xd, 0x11, 0x8, 0xd, 0x11, 0x4, 0xd, 0x1, 0xd, 0x11, 0x3c, 0xd, 0x11, 0x38, 0xd, 0x11, 0x34, 0xd, - 0x11, 0x30, 0xd, 0x11, 0x2c, 0xd, 0x11, 0x28, 0xd, 0x11, 0x24, 0xd, 0x11, 0x20, 0xd, 0x11, 0x40, 0x52, 0x18, 0x4, 0x9, - 0x20, 0x2, 0x1a, 0x40, 0x4, 0xb, 0x4, 0x4, 0x7, 0x4e, 0x2, 0x1, 0x15, 0x40, 0xd, 0x11, 0x44, 0x2, 0x18, 0x20, 0x4, - 0x9, 0x20, 0x2, 0x1a, 0x44, 0x4, 0xb, 0x4, 0x4, 0x7, 0x4e, 0x2, 0x1, 0x16, 0x44, 0x3, 0x15, 0x48, 0x52, 0x17, 0x4, - 0x8, 0x20, 0x2, 0x19, 0x48, 0x4, 0xa, 0x4, 0x4e, 0x3, 0x3, 0x16, 0x4c, 0x2, 0x17, 0x20, 0x4, 0x8, 0x20, 0x2, 0x19, - 0x4c, 0x4, 0xa, 0x4, 0x4e, 0x3, 0x4, 0x7, 0x1, 0x10, 0x58, 0x1, 0x15, 0x54, 0x1, 0x16, 0x50, 0x2, 0x11, 0x5c, 0x13, - 0x0, 0x2, 0x11, 0xec, 0x3, 0x10, 0x10, 0xd, 0x11, 0x8, 0xd, 0x11, 0x4, 0xd, 0x1, 0x1a, 0x11, 0xf, 0x2, 0x17, 0xf, 0x4, - 0x8, 0x1, 0x52, 0x19, 0x4, 0xa, 0xc, 0x4e, 0x3, 0x4, 0x7, 0x1, 0x10, 0x10, 0x2, 0x11, 0x14, 0x13, 0x0, 0x4, 0x7, 0x13, - 0x0, 0x21, 0x84, 0x54, 0x92, 0x24, 0x49, 0x92, 0x92, 0x24, 0x49, 0x52, 0x92, 0x4a, 0x92, 0xa4, 0x92, 0x92, 0x94, 0x24, - 0xa9, 0x24, 0x29, 0x49, 0x4a, 0x52, 0x2a, 0xa9, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x1, 0x0, 0x0, 0xf9, 0x0, 0x0, 0x0, 0x0, 0x0, 0x80, 0xd9, 0x5, 0x12, + 0x0, 0x0, 0x0, 0x5, 0x11, 0x0, 0x0, 0x0, 0x5, 0xa3, 0x0, 0x0, 0x0, 0x5, 0xc6, 0x0, 0x4, 0x7, 0x13, 0x0, 0x2, 0x11, + 0xa4, 0x3, 0x10, 0x58, 0x3, 0x15, 0x54, 0x3, 0x16, 0x50, 0xd, 0x11, 0x1c, 0xd, 0x11, 0x18, 0xd, 0x11, 0x14, 0xd, 0x11, + 0x10, 0xd, 0x11, 0xc, 0xd, 0x11, 0x8, 0xd, 0x11, 0x4, 0xd, 0x1, 0xd, 0x11, 0x3c, 0xd, 0x11, 0x38, 0xd, 0x11, 0x34, + 0xd, 0x11, 0x30, 0xd, 0x11, 0x2c, 0xd, 0x11, 0x28, 0xd, 0x11, 0x24, 0xd, 0x11, 0x20, 0xd, 0x11, 0x40, 0x52, 0x18, 0x4, + 0x9, 0x20, 0x2, 0x1a, 0x40, 0x4, 0xb, 0x4, 0x4, 0x7, 0x4e, 0x2, 0x1, 0x15, 0x40, 0xd, 0x11, 0x44, 0x2, 0x18, 0x20, + 0x4, 0x9, 0x20, 0x2, 0x1a, 0x44, 0x4, 0xb, 0x4, 0x4, 0x7, 0x4e, 0x2, 0x1, 0x16, 0x44, 0x3, 0x15, 0x48, 0x52, 0x17, + 0x4, 0x8, 0x20, 0x2, 0x19, 0x48, 0x4, 0xa, 0x4, 0x4e, 0x3, 0x3, 0x16, 0x4c, 0x2, 0x17, 0x20, 0x4, 0x8, 0x20, 0x2, + 0x19, 0x4c, 0x4, 0xa, 0x4, 0x4e, 0x3, 0x4, 0x7, 0x1, 0x10, 0x58, 0x1, 0x15, 0x54, 0x1, 0x16, 0x50, 0x2, 0x11, 0x5c, + 0x13, 0x0, 0x2, 0x11, 0xec, 0x3, 0x10, 0x10, 0xd, 0x11, 0x8, 0xd, 0x11, 0x4, 0xd, 0x1, 0x1a, 0x11, 0xf, 0x2, 0x17, + 0xf, 0x4, 0x8, 0x1, 0x52, 0x19, 0x4, 0xa, 0xc, 0x4e, 0x3, 0x4, 0x7, 0x1, 0x10, 0x10, 0x2, 0x11, 0x14, 0x13, 0x0, 0x4, + 0x7, 0x13, 0x0, 0x21, 0x84, 0x54, 0x92, 0x24, 0x49, 0x92, 0x92, 0x24, 0x49, 0x52, 0x92, 0x4a, 0x92, 0xa4, 0x92, 0x92, + 0x94, 0x24, 0xa9, 0x24, 0x29, 0x49, 0x4a, 0x52, 0x2a, 0xa9, 0x0, ]); const args = new Uint8Array(); -const { code, memory, registers } = Program.fromSpi(program, args, false); (async () => { - await pvmHostCallExtension.runProgram(code, 5, tryAsGas(1000), registers, memory); + await pvmHostCallExtension.runProgram(program, args, 5, tryAsGas(1000)); })(); diff --git a/packages/core/pvm-host-calls/host-call-handler.ts b/packages/core/pvm-host-calls/host-call-handler.ts index 0bf1d91ef..ba0202d6b 100644 --- a/packages/core/pvm-host-calls/host-call-handler.ts +++ b/packages/core/pvm-host-calls/host-call-handler.ts @@ -1,9 +1,9 @@ import { tryAsU32, type U32 } from "@typeberry/numbers"; -import type { Gas, GasCounter, SmallGas } from "@typeberry/pvm-interpreter/gas.js"; +import type { Gas, IGasCounter, SmallGas } from "@typeberry/pvm-interface"; import { type RegisterIndex, tryAsRegisterIndex } from "@typeberry/pvm-interpreter/registers.js"; import { asOpaqueType, type Opaque } from "@typeberry/utils"; -import type { IHostCallMemory } from "./host-call-memory.js"; -import type { IHostCallRegisters } from "./host-call-registers.js"; +import type { HostCallMemory } from "./host-call-memory.js"; +import type { HostCallRegisters } from "./host-call-registers.js"; /** Strictly-typed host call index. */ export type HostCallIndex = Opaque; @@ -34,9 +34,9 @@ export interface HostCallHandler { /** * The gas cost of invocation of that host call. * - * NOTE: `((reg: IHostCallRegisters) => Gas)` function is for compatibility reasons: pre GP 0.7.2 + * NOTE: `((reg: HostCallRegisters) => Gas)` function is for compatibility reasons: pre GP 0.7.2 */ - readonly basicGasCost: SmallGas | ((reg: IHostCallRegisters) => Gas); + readonly basicGasCost: SmallGas | ((reg: HostCallRegisters) => Gas); /** Currently executing service id. */ readonly currentServiceId: U32; @@ -49,5 +49,5 @@ export interface HostCallHandler { * * NOTE the call is ALLOWED and expected to modify registers and memory. */ - execute(gas: GasCounter, regs: IHostCallRegisters, memory: IHostCallMemory): Promise; + execute(gas: IGasCounter, regs: HostCallRegisters, memory: HostCallMemory): Promise; } diff --git a/packages/core/pvm-host-calls/host-call-memory.test.ts b/packages/core/pvm-host-calls/host-call-memory.test.ts index 55803c70b..d906dbe14 100644 --- a/packages/core/pvm-host-calls/host-call-memory.test.ts +++ b/packages/core/pvm-host-calls/host-call-memory.test.ts @@ -1,17 +1,39 @@ import { beforeEach, describe, it } from "node:test"; -import { tryAsU64 } from "@typeberry/numbers"; -import { Memory } from "@typeberry/pvm-interpreter"; -import { OutOfBounds, PageFault } from "@typeberry/pvm-interpreter/memory/errors.js"; -import { MEMORY_SIZE } from "@typeberry/pvm-interpreter/memory/memory-consts.js"; +import { tryAsU32, tryAsU64, type U32 } from "@typeberry/numbers"; +import { + getPageStartAddress, + type IMemory, + MAX_MEMORY_INDEX, + MEMORY_SIZE, + type PageFault, +} from "@typeberry/pvm-interface"; import { deepEqual, OK, Result } from "@typeberry/utils"; import { HostCallMemory } from "./host-call-memory.js"; +class FakeMemory implements IMemory { + store(address: U32, bytes: Uint8Array): Result { + const pageStart = getPageStartAddress(tryAsU32((address + bytes.length) % 2 ** 32)); + return Result.error( + { address: tryAsU32(pageStart) }, + () => `Page fault: attempted to access reserved page ${pageStart}`, + ); + } + + read(address: U32, result: Uint8Array): Result { + const pageStart = getPageStartAddress(tryAsU32((address + result.length) % 2 ** 32)); + return Result.error( + { address: tryAsU32(pageStart) }, + () => `Page fault: attempted to access reserved page ${pageStart}`, + ); + } +} + describe("HostCallMemory", () => { - let memory: Memory; + let memory: FakeMemory; let hostCallMemory: HostCallMemory; beforeEach(() => { - memory = new Memory(); + memory = new FakeMemory(); hostCallMemory = new HostCallMemory(memory); }); @@ -33,7 +55,7 @@ describe("HostCallMemory", () => { deepEqual( result, - Result.error(PageFault.fromPageNumber(0, true), () => "Page fault: attempted to access reserved page 0"), + Result.error({ address: tryAsU32(0) }, () => "Page fault: attempted to access reserved page 0"), ); }); @@ -45,25 +67,19 @@ describe("HostCallMemory", () => { deepEqual( result, - Result.error( - new OutOfBounds(), - () => "Memory access out of bounds: address 4294967294 + length 3 exceeds memory size", - ), + Result.error({ address: tryAsU32(0) }, () => "Page fault: attempted to access reserved page 0"), ); }); - it("should throw when address exceeds MAX_MEMORY_INDEX", () => { - const address = tryAsU64(MEMORY_SIZE); + it("should wrap address when exceeds MAX_MEMORY_INDEX and throw", () => { + const address = tryAsU64(MAX_MEMORY_INDEX + 1); const bytes = new Uint8Array([1, 2, 3]); const res = hostCallMemory.storeFrom(address, bytes); deepEqual( res, - Result.error( - new OutOfBounds(), - () => "Memory access out of bounds: address 4294967296 + length 3 exceeds memory size", - ), + Result.error({ address: tryAsU32(0) }, () => "Page fault: attempted to access reserved page 0"), ); }); }); @@ -86,7 +102,7 @@ describe("HostCallMemory", () => { deepEqual( result, - Result.error(PageFault.fromPageNumber(0, true), () => "Page fault: attempted to access reserved page 0"), + Result.error({ address: tryAsU32(0) }, () => "Page fault: attempted to access reserved page 0"), ); }); @@ -98,25 +114,19 @@ describe("HostCallMemory", () => { deepEqual( res, - Result.error( - new OutOfBounds(), - () => "Memory access out of bounds: address 4294967294 + length 3 exceeds memory size", - ), + Result.error({ address: tryAsU32(0) }, () => "Page fault: attempted to access reserved page 0"), ); }); - it("should throw when address exceeds MAX_MEMORY_INDEX", () => { - const address = tryAsU64(MEMORY_SIZE); + it("should wrap address when exceeds MAX_MEMORY_INDEX and throw", () => { + const address = tryAsU64(MAX_MEMORY_INDEX + 1); const result = new Uint8Array([1, 2, 3]); const res = hostCallMemory.loadInto(result, address); deepEqual( res, - Result.error( - new OutOfBounds(), - () => "Memory access out of bounds: address 4294967296 + length 3 exceeds memory size", - ), + Result.error({ address: tryAsU32(0) }, () => "Page fault: attempted to access reserved page 0"), ); }); }); diff --git a/packages/core/pvm-host-calls/host-call-memory.ts b/packages/core/pvm-host-calls/host-call-memory.ts index 50dbde622..cf7e54e97 100644 --- a/packages/core/pvm-host-calls/host-call-memory.ts +++ b/packages/core/pvm-host-calls/host-call-memory.ts @@ -1,44 +1,43 @@ -import { tryAsU64, type U64 } from "@typeberry/numbers"; -import { type Memory, tryAsMemoryIndex } from "@typeberry/pvm-interpreter"; -import { OutOfBounds, type PageFault } from "@typeberry/pvm-interpreter/memory/errors.js"; -import { MEMORY_SIZE } from "@typeberry/pvm-interpreter/memory/memory-consts.js"; +import { tryAsU32, type U64 } from "@typeberry/numbers"; +import type { IMemory, PageFault } from "@typeberry/pvm-interface"; import { OK, Result } from "@typeberry/utils"; -export interface IHostCallMemory { - storeFrom(address: U64, bytes: Uint8Array): Result; - loadInto(result: Uint8Array, startAddress: U64): Result; -} - -export class HostCallMemory implements IHostCallMemory { - constructor(private readonly memory: Memory) {} +export class HostCallMemory { + constructor(private readonly memory: IMemory) {} - storeFrom(address: U64, bytes: Uint8Array): Result { + /** + * Save some bytes into memory under given address. + * + * NOTE: Given address is U64 (pure register value), + * but we use only lower 32-bits. + */ + storeFrom(regAddress: U64, bytes: Uint8Array): Result { if (bytes.length === 0) { return Result.ok(OK); } - if (address + tryAsU64(bytes.length) > MEMORY_SIZE) { - return Result.error( - new OutOfBounds(), - () => `Memory access out of bounds: address ${address} + length ${bytes.length} exceeds memory size`, - ); - } - - return this.memory.storeFrom(tryAsMemoryIndex(Number(address)), bytes); + // NOTE: We always take lower 32 bits from register value. + // + // https://graypaper.fluffylabs.dev/#/ab2cdbd/25ed0025ed00?v=0.7.2 + const address = tryAsU32(Number(regAddress & 0xffff_ffffn)); + return this.memory.store(address, bytes); } - loadInto(result: Uint8Array, startAddress: U64): Result { - if (result.length === 0) { + /** + * Read some bytes from memory under given address. + * + * NOTE: Given address is U64 (pure register value), + * but we use only lower 32-bits. + */ + loadInto(output: Uint8Array, regAddress: U64): Result { + if (output.length === 0) { return Result.ok(OK); } - if (startAddress + tryAsU64(result.length) > MEMORY_SIZE) { - return Result.error( - new OutOfBounds(), - () => `Memory access out of bounds: address ${startAddress} + length ${result.length} exceeds memory size`, - ); - } - - return this.memory.loadInto(result, tryAsMemoryIndex(Number(startAddress))); + // https://graypaper.fluffylabs.dev/#/ab2cdbd/25ed0025ed00?v=0.7.2 + // + // NOTE we are taking the the lower U32 part of the register, hence it's safe. + const address = tryAsU32(Number(regAddress & 0xffff_ffffn)); + return this.memory.read(address, output); } } diff --git a/packages/core/pvm-host-calls/host-call-registers.test.ts b/packages/core/pvm-host-calls/host-call-registers.test.ts index 8bf79dce0..7b0ae56ce 100644 --- a/packages/core/pvm-host-calls/host-call-registers.test.ts +++ b/packages/core/pvm-host-calls/host-call-registers.test.ts @@ -2,25 +2,26 @@ import assert from "node:assert"; import { describe, it } from "node:test"; import { tryAsU64 } from "@typeberry/numbers"; -import { Registers } from "@typeberry/pvm-interpreter"; import { HostCallRegisters } from "./host-call-registers.js"; describe("HostCallRegisters", () => { - describe("get", () => { + describe("getAllEncoded", () => { it("reads a u64 value from the underlying registers", () => { - const registers = new Registers(); - registers.setU64(0, tryAsU64(0xffff_ffff_ffff_fffdn)); - const hostCallRegisters = new HostCallRegisters(registers); + const regs = new BigUint64Array(13); + regs[0] = 0xffff_ffff_ffff_fffdn; + const hostCallRegisters = new HostCallRegisters(new Uint8Array(regs.buffer)); assert.strictEqual(hostCallRegisters.get(0), tryAsU64(0xffff_ffff_ffff_fffdn)); }); }); - describe("set", () => { + describe("setAllEncoded", () => { it("writes a u64 value to the underlying registers", () => { - const registers = new Registers(); - const hostCallRegisters = new HostCallRegisters(registers); + const regs = new BigUint64Array(13); + const hostCallRegisters = new HostCallRegisters(new Uint8Array(regs.buffer)); hostCallRegisters.set(0, tryAsU64(0xffff_ffff_ffff_fffdn)); - assert.strictEqual(registers.getU64(0), tryAsU64(0xffff_ffff_ffff_fffdn)); + const view = new DataView(hostCallRegisters.getEncoded().buffer); + assert.strictEqual(view.getBigUint64(0, true), tryAsU64(0xffff_ffff_ffff_fffdn)); + assert.strictEqual(regs[0], tryAsU64(0xffff_ffff_ffff_fffdn)); }); }); }); diff --git a/packages/core/pvm-host-calls/host-call-registers.ts b/packages/core/pvm-host-calls/host-call-registers.ts index d56cbf1b5..579973eb8 100644 --- a/packages/core/pvm-host-calls/host-call-registers.ts +++ b/packages/core/pvm-host-calls/host-call-registers.ts @@ -1,19 +1,25 @@ import { tryAsU64, type U64 } from "@typeberry/numbers"; -import type { Registers } from "@typeberry/pvm-interpreter"; +import { REGISTER_BYTE_SIZE } from "@typeberry/pvm-interface"; -export interface IHostCallRegisters { - get(registerIndex: number): U64; - set(registerIndex: number, value: U64): void; -} +export class HostCallRegisters { + private readonly registers: DataView; -export class HostCallRegisters implements IHostCallRegisters { - constructor(private readonly registers: Registers) {} + constructor(private readonly bytes: Uint8Array) { + this.registers = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength); + } + /** Get U64 register value. */ get(registerIndex: number): U64 { - return tryAsU64(this.registers.getU64(registerIndex)); + return tryAsU64(this.registers.getBigUint64(registerIndex * REGISTER_BYTE_SIZE, true)); } + /** Set U64 register value. */ set(registerIndex: number, value: U64) { - this.registers.setU64(registerIndex, value); + this.registers.setBigUint64(registerIndex * REGISTER_BYTE_SIZE, value, true); + } + + /** Get all registers encoded into little-endian bytes. */ + getEncoded(): Uint8Array { + return this.bytes; } } diff --git a/packages/core/pvm-host-calls/host-calls-manager.ts b/packages/core/pvm-host-calls/host-calls-manager.ts index 8eb211a17..f2a6cef75 100644 --- a/packages/core/pvm-host-calls/host-calls-manager.ts +++ b/packages/core/pvm-host-calls/host-calls-manager.ts @@ -1,6 +1,6 @@ import { Logger } from "@typeberry/logger"; import { tryAsU32 } from "@typeberry/numbers"; -import { type Gas, tryAsSmallGas } from "@typeberry/pvm-interpreter/gas.js"; +import { type Gas, tryAsSmallGas } from "@typeberry/pvm-interface"; import { check } from "@typeberry/utils"; import { type HostCallHandler, @@ -8,7 +8,7 @@ import { type PvmExecution, tryAsHostCallIndex, } from "./host-call-handler.js"; -import type { IHostCallRegisters } from "./host-call-registers.js"; +import type { HostCallRegisters } from "./host-call-registers.js"; const logger = Logger.new(import.meta.filename, "host-calls-pvm"); @@ -41,7 +41,7 @@ export class HostCallsManager { context: string, hostCallIndex: HostCallIndex, hostCallHandler: HostCallHandler, - registers: IHostCallRegisters, + registers: HostCallRegisters, gas: Gas, ) { const { currentServiceId } = hostCallHandler; diff --git a/packages/core/pvm-host-calls/host-calls.ts b/packages/core/pvm-host-calls/host-calls.ts index 863a210a0..1317941f0 100644 --- a/packages/core/pvm-host-calls/host-calls.ts +++ b/packages/core/pvm-host-calls/host-calls.ts @@ -1,8 +1,4 @@ -import type { Interpreter, Memory } from "@typeberry/pvm-interpreter"; -import type { Gas } from "@typeberry/pvm-interpreter/gas.js"; -import { tryAsMemoryIndex } from "@typeberry/pvm-interpreter/memory/memory-index.js"; -import type { Registers } from "@typeberry/pvm-interpreter/registers.js"; -import { Status } from "@typeberry/pvm-interpreter/status.js"; +import { type Gas, type IPvmInterpreter, Status } from "@typeberry/pvm-interface"; import { assertNever, check, safeAllocUint8Array } from "@typeberry/utils"; import { PvmExecution, tryAsHostCallIndex } from "./host-call-handler.js"; import { HostCallMemory } from "./host-call-memory.js"; @@ -44,21 +40,22 @@ export class HostCalls { private hostCalls: HostCallsManager, ) {} - private getReturnValue(status: Status, pvmInstance: Interpreter): ReturnValue { - const gasConsumed = pvmInstance.getGasConsumed(); + private getReturnValue(status: Status, pvmInstance: IPvmInterpreter): ReturnValue { + const gasConsumed = pvmInstance.gas.used(); if (status === Status.OOG) { return ReturnValue.fromStatus(gasConsumed, status); } if (status === Status.HALT) { - const memory = pvmInstance.getMemory(); - const regs = pvmInstance.getRegisters(); - const maybeAddress = regs.getLowerU32(7); - const maybeLength = regs.getLowerU32(8); + const regs = new HostCallRegisters(pvmInstance.registers.getAllEncoded()); + const memory = new HostCallMemory(pvmInstance.memory); + const address = regs.get(7); + // NOTE we are taking the the lower U32 part of the register, hence it's safe. + const length = Number(regs.get(8) & 0xffff_ffffn); - const result = safeAllocUint8Array(maybeLength); - const startAddress = tryAsMemoryIndex(maybeAddress); - const loadResult = memory.loadInto(result, startAddress); + const result = safeAllocUint8Array(length); + + const loadResult = memory.loadInto(result, address); if (loadResult.isError) { return ReturnValue.fromMemorySlice(gasConsumed, new Uint8Array()); @@ -70,7 +67,7 @@ export class HostCalls { return ReturnValue.fromStatus(gasConsumed, Status.PANIC); } - private async execute(pvmInstance: Interpreter) { + private async execute(pvmInstance: IPvmInterpreter) { pvmInstance.runProgram(); for (;;) { let status = pvmInstance.getStatus(); @@ -82,9 +79,9 @@ export class HostCalls { "We know that the exit param is not null, because the status is 'Status.HOST' `; const hostCallIndex = pvmInstance.getExitParam() ?? -1; - const gas = pvmInstance.getGasCounter(); - const regs = new HostCallRegisters(pvmInstance.getRegisters()); - const memory = new HostCallMemory(pvmInstance.getMemory()); + const gas = pvmInstance.gas; + const regs = new HostCallRegisters(pvmInstance.registers.getAllEncoded()); + const memory = new HostCallMemory(pvmInstance.memory); const index = tryAsHostCallIndex(hostCallIndex); const hostCall = this.hostCalls.get(index); @@ -97,7 +94,7 @@ export class HostCalls { const pcLog = `[PC: ${pvmInstance.getPC()}]`; if (underflow) { this.hostCalls.traceHostCall(`${pcLog} OOG`, index, hostCall, regs, gas.get()); - return ReturnValue.fromStatus(pvmInstance.getGasConsumed(), Status.OOG); + return ReturnValue.fromStatus(gas.used(), Status.OOG); } this.hostCalls.traceHostCall(`${pcLog} Invoking`, index, hostCall, regs, gasBefore); const result = await hostCall.execute(gas, regs, memory); @@ -108,6 +105,7 @@ export class HostCalls { regs, gas.get(), ); + pvmInstance.registers.setAllEncoded(regs.getEncoded()); if (result === PvmExecution.Halt) { status = Status.HALT; @@ -134,15 +132,9 @@ export class HostCalls { } } - async runProgram( - rawProgram: Uint8Array, - initialPc: number, - initialGas: Gas, - maybeRegisters?: Registers, - maybeMemory?: Memory, - ): Promise { + async runProgram(program: Uint8Array, args: Uint8Array, initialPc: number, initialGas: Gas): Promise { const pvmInstance = await this.pvmInstanceManager.getInstance(); - pvmInstance.reset(rawProgram, initialPc, initialGas, maybeRegisters, maybeMemory); + pvmInstance.resetJam(program, args, initialPc, initialGas); try { return await this.execute(pvmInstance); } finally { diff --git a/packages/core/pvm-host-calls/index.ts b/packages/core/pvm-host-calls/index.ts index 564a83493..ea208a1da 100644 --- a/packages/core/pvm-host-calls/index.ts +++ b/packages/core/pvm-host-calls/index.ts @@ -1,6 +1,6 @@ export { type HostCallHandler, PvmExecution, traceRegisters, tryAsHostCallIndex } from "./host-call-handler.js"; -export { HostCallMemory, type IHostCallMemory } from "./host-call-memory.js"; -export { HostCallRegisters, type IHostCallRegisters } from "./host-call-registers.js"; +export { HostCallMemory } from "./host-call-memory.js"; +export { HostCallRegisters } from "./host-call-registers.js"; export { HostCalls as PvmHostCallExtension } from "./host-calls.js"; export { HostCallsManager as HostCalls } from "./host-calls-manager.js"; export { InterpreterInstanceManager as PvmInstanceManager } from "./interpreter-instance-manager.js"; diff --git a/packages/core/pvm-host-calls/interpreter-instance-manager.ts b/packages/core/pvm-host-calls/interpreter-instance-manager.ts index 852dc3e62..b55b73a69 100644 --- a/packages/core/pvm-host-calls/interpreter-instance-manager.ts +++ b/packages/core/pvm-host-calls/interpreter-instance-manager.ts @@ -1,22 +1,37 @@ +import { PvmBackend } from "@typeberry/config"; +import type { IPvmInterpreter } from "@typeberry/pvm-interface"; import { Interpreter } from "@typeberry/pvm-interpreter"; +import { AnanasInterpreter } from "@typeberry/pvm-interpreter-ananas"; +import { assertNever } from "@typeberry/utils"; -type ResolveFn = (pvm: Interpreter) => void; +type ResolveFn = (pvm: IPvmInterpreter) => void; +// TODO [MaSo] Delete this & also make host calls independent from intepreters. export class InterpreterInstanceManager { - private instances: Interpreter[] = []; private waitingQueue: ResolveFn[] = []; - constructor(noOfPvmInstances: number) { - for (let i = 0; i < noOfPvmInstances; i++) { - this.instances.push( - new Interpreter({ - useSbrkGas: false, - }), - ); + private constructor(private readonly instances: IPvmInterpreter[]) {} + + static async new(interpreter: PvmBackend): Promise { + const instances: IPvmInterpreter[] = []; + switch (interpreter) { + case PvmBackend.BuiltIn: + instances.push( + new Interpreter({ + useSbrkGas: false, + }), + ); + break; + case PvmBackend.Ananas: + instances.push(await AnanasInterpreter.new()); + break; + default: + assertNever(interpreter); } + return new InterpreterInstanceManager(instances); } - async getInstance(): Promise { + async getInstance(): Promise { const instance = this.instances.pop(); if (instance !== undefined) { return Promise.resolve(instance); @@ -26,7 +41,7 @@ export class InterpreterInstanceManager { }); } - releaseInstance(pvm: Interpreter) { + releaseInstance(pvm: IPvmInterpreter) { const waiting = this.waitingQueue.shift(); if (waiting !== undefined) { return waiting(pvm); diff --git a/packages/core/pvm-host-calls/package.json b/packages/core/pvm-host-calls/package.json index 06d11f25e..d45f5ce34 100644 --- a/packages/core/pvm-host-calls/package.json +++ b/packages/core/pvm-host-calls/package.json @@ -4,9 +4,12 @@ "description": "PVM host calls", "main": "index.ts", "dependencies": { + "@typeberry/config": "^0.2.0", "@typeberry/logger": "*", "@typeberry/numbers": "*", + "@typeberry/pvm-interface": "*", "@typeberry/pvm-interpreter": "*", + "@typeberry/pvm-interpreter-ananas": "*", "@typeberry/pvm-program": "*", "@typeberry/pvm-spi-decoder": "*", "@typeberry/utils": "*" diff --git a/packages/core/pvm-interface/gas.ts b/packages/core/pvm-interface/gas.ts new file mode 100644 index 000000000..8da1fb467 --- /dev/null +++ b/packages/core/pvm-interface/gas.ts @@ -0,0 +1,57 @@ +import { tryAsU32, tryAsU64, type U32, type U64 } from "@typeberry/numbers"; +import { asOpaqueType, type Opaque } from "@typeberry/utils"; + +/** A U64 version of `Gas`. */ +export type BigGas = Opaque; +/** A U32 version of `Gas`. */ +export type SmallGas = Opaque; +/** Gas measuring type. Can be either U64 or U32 for performance reasons. */ +export type Gas = BigGas | SmallGas; + +/** Attempt to convert given number into U32 gas representation. */ +export const tryAsSmallGas = (v: number): SmallGas => asOpaqueType(tryAsU32(v)); + +/** Attempt to convert given number into U64 gas representation. */ +export const tryAsBigGas = (v: number | bigint): BigGas => asOpaqueType(tryAsU64(v)); + +/** Attempt to convert given number into gas. */ +export const tryAsGas = (v: number | bigint): Gas => + typeof v === "number" && v < 2 ** 32 ? tryAsSmallGas(v) : tryAsBigGas(v); + +/** An abstraction over gas counter. + * + * It can be optimized to use numbers instead of bigint in case of small gas. + */ +export interface IGasCounter { + /** + * Set during initialization of GasCounter. + * + * NOTE: Needed to calculate `used()` gas. + */ + initialGas: Gas; + + /** Return remaining gas. */ + get(): Gas; + + /** + * Overwrite remaining gas. + * + * NOTE: Could cause `used()` gas calculation to be incorrect. + * + * @see + * Prefer sub method instead. + */ + set(g: Gas): void; + + /** Returns true if there was an underflow. */ + sub(g: Gas): boolean; + + /** + * Calculates used gas since creation of GasCounter. + * + * The interface does not handle negative or more than `initialGas` values. + * + * NOTE: We can use at most `initialGas` and as little as `0`. + */ + used(): Gas; +} diff --git a/packages/core/pvm-interface/index.ts b/packages/core/pvm-interface/index.ts new file mode 100644 index 000000000..91870781e --- /dev/null +++ b/packages/core/pvm-interface/index.ts @@ -0,0 +1,5 @@ +export * from "./gas.js"; +export * from "./memory.js"; +export * from "./pvm.js"; +export * from "./registers.js"; +export * from "./status.js"; diff --git a/packages/core/pvm-interface/memory.ts b/packages/core/pvm-interface/memory.ts new file mode 100644 index 000000000..c978c2f32 --- /dev/null +++ b/packages/core/pvm-interface/memory.ts @@ -0,0 +1,24 @@ +import { tryAsU32, type U32 } from "@typeberry/numbers"; +import type { OK, Result } from "@typeberry/utils"; + +export const MAX_MEMORY_INDEX = 0xffff_ffff; +export const MEMORY_SIZE = MAX_MEMORY_INDEX + 1; + +const PAGE_SIZE_SHIFT = 12; + +export type PageFault = { + address: U32; +}; + +export function getPageStartAddress(address: U32): U32 { + return tryAsU32(((address >>> PAGE_SIZE_SHIFT) << PAGE_SIZE_SHIFT) >>> 0); +} + +/** Allows store and read segments of memory. */ +export interface IMemory { + /** Store bytes into memory at given address. */ + store(address: U32, bytes: Uint8Array): Result; + + /** Load bytes from memory from given address into given buffer. */ + read(address: U32, result: Uint8Array): Result; +} diff --git a/packages/core/pvm-interface/package.json b/packages/core/pvm-interface/package.json new file mode 100644 index 000000000..45fc54414 --- /dev/null +++ b/packages/core/pvm-interface/package.json @@ -0,0 +1,17 @@ +{ + "name": "@typeberry/pvm-interface", + "version": "0.1.3", + "description": "A PVM interface for external implementations.", + "main": "index.ts", + "dependencies": { + "@typeberry/numbers": "*", + "@typeberry/utils": "*" + }, + "scripts": { + "start": "tsx ./bin.ts", + "test": "tsx --test $(find . -type f -name '*.test.ts' | tr '\\n' ' ')" + }, + "author": "Fluffy Labs", + "license": "MPL-2.0", + "type": "module" +} diff --git a/packages/core/pvm-interface/pvm.ts b/packages/core/pvm-interface/pvm.ts new file mode 100644 index 000000000..1d86ad2cb --- /dev/null +++ b/packages/core/pvm-interface/pvm.ts @@ -0,0 +1,31 @@ +import type { U32 } from "@typeberry/numbers"; +import type { Gas, IGasCounter } from "./gas.js"; +import type { IMemory } from "./memory.js"; +import type { IRegisters } from "./registers.js"; +import type { Status } from "./status.js"; + +export interface IPvmInterpreter { + /** Manipulate gas. */ + readonly gas: IGasCounter; + + /** Manipulate registers. */ + readonly registers: IRegisters; + + /** Manipulate memory. */ + readonly memory: IMemory; + + /** Prepare SPI program to be executed. */ + resetJam(program: Uint8Array, args: Uint8Array, pc: number, gas: Gas): void; + + /** Execute loaded program. */ + runProgram(): void; + + /** Get current Status. */ + getStatus(): Status; + + /** Get current Program Counter. */ + getPC(): number; + + /** Get exit args. Needed in case of HOST or FAULT. */ + getExitParam(): U32 | null; +} diff --git a/packages/core/pvm-interface/registers.ts b/packages/core/pvm-interface/registers.ts new file mode 100644 index 000000000..0e14cfc4d --- /dev/null +++ b/packages/core/pvm-interface/registers.ts @@ -0,0 +1,18 @@ +export const NO_OF_REGISTERS = 13; +export const REGISTER_BYTE_SIZE = 8; + +/** Allow to set and get all registers encoded into little-endian bytes. */ +export interface IRegisters { + /** + * Get all registers encoded into little-endian bytes. + * + * NOTE: Total length of bytes must be NO_OF_REGISTERS * REGISTER_BYTE_SIZE. + */ + getAllEncoded(): Uint8Array; + /** + * Set all registers from little-endian encoded bytes. + * + * NOTE: Total length of bytes must be NO_OF_REGISTERS * REGISTER_BYTE_SIZE. + */ + setAllEncoded(bytes: Uint8Array): void; +} diff --git a/packages/core/pvm-interface/status.ts b/packages/core/pvm-interface/status.ts new file mode 100644 index 000000000..b04862cfd --- /dev/null +++ b/packages/core/pvm-interface/status.ts @@ -0,0 +1,19 @@ +/** + * Result codes for the PVM execution. + * + * https://graypaper.fluffylabs.dev/#/ab2cdbd/2e43002e4300?v=0.7.2 + */ +export enum Status { + /** Continue */ + OK = 255, + /** Finished */ + HALT = 0, + /** Panic */ + PANIC = 1, + /** Page-fault */ + FAULT = 2, + /** Host-call */ + HOST = 3, + /** Out of gas */ + OOG = 4, +} diff --git a/packages/core/pvm-interpreter-ananas/index.ts b/packages/core/pvm-interpreter-ananas/index.ts new file mode 100644 index 000000000..d54467730 --- /dev/null +++ b/packages/core/pvm-interpreter-ananas/index.ts @@ -0,0 +1,181 @@ +import { readFileSync } from "node:fs"; +import { fileURLToPath } from "node:url"; +import { instantiate } from "@fluffylabs/anan-as/raw"; +import { tryAsU32, type U32 } from "@typeberry/numbers"; +import { + type Gas, + getPageStartAddress, + type IGasCounter, + type IMemory, + type IPvmInterpreter, + type IRegisters, + NO_OF_REGISTERS, + type PageFault, + REGISTER_BYTE_SIZE, + Status, + tryAsBigGas, + tryAsGas, +} from "@typeberry/pvm-interface"; +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; + +class AnanasRegisters implements IRegisters { + constructor(private readonly instance: Ananas) {} + + getAllEncoded(): Uint8Array { + return this.instance.getRegisters(); + } + + setAllEncoded(bytes: Uint8Array): void { + check`${bytes.length === NO_OF_REGISTERS * REGISTER_BYTE_SIZE} + Incorrect size of input registers. Got: ${bytes.length}, + need: ${NO_OF_REGISTERS * REGISTER_BYTE_SIZE}`; + this.instance.setRegisters(lowerBytes(bytes)); + } + + getAllU64(): BigUint64Array { + const bytes = this.getAllEncoded(); + return new BigUint64Array(bytes.buffer, bytes.byteOffset); + } +} + +class AnanasMemory implements IMemory { + constructor(private readonly instance: Ananas) {} + + store(address: U32, bytes: Uint8Array): Result { + if (this.instance.setMemory(address, bytes)) { + return Result.ok(OK); + } + return Result.error({ address: getPageStartAddress(address) }, () => "Memory is unwritable!"); + } + + read(address: U32, result: Uint8Array): Result { + if (result.length === 0) { + return Result.ok(OK); + } + const newResult = this.instance.getMemory(address, result.length); + if (newResult === null) { + return Result.error({ address: getPageStartAddress(address) }, () => "Memory is inaccessible!"); + } + result.set(newResult, 0); + return Result.ok(OK); + } +} + +class AnanasGasCounter implements IGasCounter { + initialGas: Gas = tryAsGas(0n); + + constructor(private readonly instance: Ananas) {} + + get(): Gas { + return tryAsGas(this.instance.getGasLeft()); + } + + set(g: Gas): void { + this.instance.setGasLeft(BigInt(g)); + } + + sub(g: Gas): boolean { + const result = this.instance.getGasLeft() - BigInt(g); + if (result >= 0n) { + this.instance.setGasLeft(result); + return false; + } + this.instance.setGasLeft(0n); + return true; + } + + used(): Gas { + const gasConsumed = BigInt(this.initialGas) - BigInt(this.get()); + + if (gasConsumed < 0) { + return this.initialGas; + } + + return tryAsBigGas(gasConsumed); + } +} + +export class AnanasInterpreter implements IPvmInterpreter { + readonly registers: AnanasRegisters; + readonly memory: AnanasMemory; + readonly gas: AnanasGasCounter; + + private constructor(private readonly instance: Ananas) { + this.registers = new AnanasRegisters(instance); + this.memory = new AnanasMemory(instance); + this.gas = new AnanasGasCounter(instance); + } + + static async new() { + const wasmModule = await WebAssembly.compile(wasmBuffer); + const instance = await instantiate(wasmModule, { + env: { + abort: () => { + throw new Error("Abort called from WASM"); + }, + }, + }); + return new AnanasInterpreter(instance); + } + + resetJam(program: Uint8Array, args: Uint8Array, pc: number, gas: Gas): void { + const programArr = lowerBytes(program); + const argsArr = lowerBytes(args); + this.gas.initialGas = gas; + this.instance.resetJAM(programArr, pc, BigInt(gas), argsArr, true); + } + + resetGeneric(program: Uint8Array, _pc: number, gas: Gas): void { + const programArr = lowerBytes(program); + const emptyRegisters = Array(13 * 8).fill(0); + const pageMap = new Uint8Array(); + const chunks = new Uint8Array(); + this.gas.initialGas = gas; + this.instance.resetGenericWithMemory(programArr, emptyRegisters, pageMap, chunks, BigInt(gas), false); + } + + nextStep(): boolean { + return this.instance.nextStep(); + } + + runProgram(): void { + // NOTE Setting max value u32 in nNextSteps making ananas running until finished + // without comming back and forth between JS <-> WASM + while (this.instance.nSteps(INF_STEPS)) {} + } + + getStatus(): Status { + const status = this.instance.getStatus(); + if (status < 0) { + return Status.OK; + } + check`${Status[status] !== undefined} Invalid status returned: ${status}`; + return status; + } + + getPC(): number { + return this.instance.getProgramCounter(); + } + + getExitParam(): U32 | null { + return tryAsU32(this.instance.getExitArg()); + } +} + +/** Convert `Uint8Array` to `number[]` */ +function lowerBytes(data: Uint8Array): number[] { + const r = new Array(data.length); + for (let i = 0; i < data.length; i++) { + r[i] = data[i]; + } + return r; +} diff --git a/packages/core/pvm-interpreter-ananas/package.json b/packages/core/pvm-interpreter-ananas/package.json new file mode 100644 index 000000000..ffdbb25a3 --- /dev/null +++ b/packages/core/pvm-interpreter-ananas/package.json @@ -0,0 +1,21 @@ +{ + "name": "@typeberry/pvm-interpreter-ananas", + "version": "0.1.3", + "description": "Anan-as PVM implementation.", + "main": "index.ts", + "dependencies": { + "@fluffylabs/anan-as": "^1.0.0-a9b8141", + "@typeberry/codec": "*", + "@typeberry/numbers": "*", + "@typeberry/pvm-interface": "*", + "@typeberry/utils": "*", + "assemblyscript-loader": "^0.3.0" + }, + "scripts": { + "start": "tsx ./bin.ts", + "test": "tsx --test $(find . -type f -name '*.test.ts' | tr '\\n' ' ')" + }, + "author": "Fluffy Labs", + "license": "MPL-2.0", + "type": "module" +} diff --git a/packages/core/pvm-interpreter/args-decoder/decoders/nibbles-decoder.ts b/packages/core/pvm-interpreter/args-decoder/decoders/nibbles-decoder.ts index 5a2997668..cba051220 100644 --- a/packages/core/pvm-interpreter/args-decoder/decoders/nibbles-decoder.ts +++ b/packages/core/pvm-interpreter/args-decoder/decoders/nibbles-decoder.ts @@ -1,4 +1,4 @@ -import { NO_OF_REGISTERS } from "../../registers.js"; +import { NO_OF_REGISTERS } from "@typeberry/pvm-interface"; const MAX_REGISTER_INDEX = NO_OF_REGISTERS - 1; const MAX_LENGTH = 4; diff --git a/packages/core/pvm-interpreter/assemblify.ts b/packages/core/pvm-interpreter/assemblify.ts index 398390163..384667bbf 100644 --- a/packages/core/pvm-interpreter/assemblify.ts +++ b/packages/core/pvm-interpreter/assemblify.ts @@ -1,4 +1,4 @@ -import { type SmallGas, tryAsSmallGas } from "./gas.js"; +import { type SmallGas, tryAsSmallGas } from "@typeberry/pvm-interface"; import { Instruction } from "./instruction.js"; import type { Mask } from "./program-decoder/mask.js"; diff --git a/packages/core/pvm-interpreter/bin.ts b/packages/core/pvm-interpreter/bin.ts index ca77e496e..8b5405288 100644 --- a/packages/core/pvm-interpreter/bin.ts +++ b/packages/core/pvm-interpreter/bin.ts @@ -1,4 +1,4 @@ -import { tryAsGas } from "./gas.js"; +import { tryAsGas } from "@typeberry/pvm-interface"; import { Interpreter } from "./index.js"; const program = new Uint8Array([ @@ -7,6 +7,6 @@ const program = new Uint8Array([ ]); const pvm = new Interpreter(); -pvm.reset(program, 0, tryAsGas(1000)); +pvm.resetGeneric(program, 0, tryAsGas(1000)); pvm.printProgram(); pvm.runProgram(); diff --git a/packages/core/pvm-interpreter/gas.test.ts b/packages/core/pvm-interpreter/gas.test.ts index 49031f25f..63aa74618 100644 --- a/packages/core/pvm-interpreter/gas.test.ts +++ b/packages/core/pvm-interpreter/gas.test.ts @@ -1,6 +1,7 @@ import assert from "node:assert"; import { describe, it } from "node:test"; -import { gasCounter, tryAsBigGas } from "./gas.js"; +import { tryAsBigGas, tryAsSmallGas } from "@typeberry/pvm-interface"; +import { gasCounter } from "./gas.js"; describe("GasCounterU64", () => { it("should return false if there is no underflow", () => { @@ -20,4 +21,33 @@ describe("GasCounterU64", () => { assert.strictEqual(underflow, true); assert.strictEqual(gas.get(), tryAsBigGas(0)); }); + + it("should return correct gas consumed", () => { + const gasSubs = [tryAsSmallGas(10), tryAsSmallGas(15), tryAsSmallGas(20)]; + const gas = gasCounter(tryAsBigGas(100)); + + // when + for (const g of gasSubs) { + gas.sub(g); + } + + // then + assert.deepStrictEqual(gas.used(), tryAsBigGas(10 + 15 + 20)); + }); + + it("should return cap to initial gas when consumed gas goes into negative", () => { + const initialGas = tryAsBigGas(100); + const gas = gasCounter(initialGas); + const gasCost = tryAsSmallGas(11); + const maxSteps = 10; + let i = 0; + + // when + while (!gas.sub(gasCost) && i < maxSteps) { + i++; + } + + // then + assert.deepStrictEqual(gas.used(), initialGas); + }); }); diff --git a/packages/core/pvm-interpreter/gas.ts b/packages/core/pvm-interpreter/gas.ts index 1dc6ca940..b2fc2a079 100644 --- a/packages/core/pvm-interpreter/gas.ts +++ b/packages/core/pvm-interpreter/gas.ts @@ -1,45 +1,17 @@ -import { tryAsU32, tryAsU64, type U32, type U64 } from "@typeberry/numbers"; -import { asOpaqueType, type Opaque } from "@typeberry/utils"; - -/** Gas measuring type. Can be either U64 or U32 for performance reasons. */ -export type Gas = BigGas | SmallGas; -/** A U64 version of `Gas`. */ -export type BigGas = Opaque; -/** A U32 version of `Gas`. */ -export type SmallGas = Opaque; - -/** Attempt to convert given number into U32 gas representation. */ -export const tryAsSmallGas = (v: number): SmallGas => asOpaqueType(tryAsU32(v)); - -/** Attempt to convert given number into U64 gas representation. */ -export const tryAsBigGas = (v: number | bigint): BigGas => asOpaqueType(tryAsU64(v)); - -/** Attempt to convert given number into gas. */ -export const tryAsGas = (v: number | bigint): Gas => - typeof v === "number" && v < 2 ** 32 ? tryAsSmallGas(v) : tryAsBigGas(v); +import { tryAsU64, type U64 } from "@typeberry/numbers"; +import { type Gas, type IGasCounter, tryAsGas } from "@typeberry/pvm-interface"; /** Create a new gas counter instance depending on the gas value. */ -export function gasCounter(gas: Gas): GasCounter { +export function gasCounter(gas: Gas): IGasCounter { return new GasCounterU64(tryAsU64(gas)); } -/** An abstraction over gas counter. - * - * It can be optimized to use numbers instead of bigint in case of small gas. - */ -export interface GasCounter { - /** Return remaining gas. */ - get(): Gas; - - /** Overwrite remaining gas. Prefer sub method instead. */ - set(g: Gas): void; - - /** Returns true if there was an underflow. */ - sub(g: Gas): boolean; -} +class GasCounterU64 implements IGasCounter { + initialGas: Gas; -class GasCounterU64 implements GasCounter { - constructor(private gas: U64) {} + constructor(private gas: U64) { + this.initialGas = tryAsGas(gas); + } set(g: Gas) { this.gas = tryAsU64(g); @@ -58,4 +30,15 @@ class GasCounterU64 implements GasCounter { this.gas = tryAsU64(0n); return true; } + + used(): Gas { + const gasConsumed = tryAsU64(this.initialGas) - this.gas; + + // In we have less than zero left we assume that all gas has been consumed. + if (gasConsumed < 0) { + return this.initialGas; + } + + return tryAsGas(gasConsumed); + } } diff --git a/packages/core/pvm-interpreter/index.ts b/packages/core/pvm-interpreter/index.ts index bc2f9f57f..6bdfd9a80 100644 --- a/packages/core/pvm-interpreter/index.ts +++ b/packages/core/pvm-interpreter/index.ts @@ -1,13 +1,4 @@ -export { - type BigGas, - type Gas, - type GasCounter, - gasCounter, - type SmallGas, - tryAsBigGas, - tryAsGas, - tryAsSmallGas, -} from "./gas.js"; +export { gasCounter } from "./gas.js"; export * from "./interpreter.js"; export { Memory, diff --git a/packages/core/pvm-interpreter/instruction-gas-map.ts b/packages/core/pvm-interpreter/instruction-gas-map.ts index 03036eb7d..9a24cc8d0 100644 --- a/packages/core/pvm-interpreter/instruction-gas-map.ts +++ b/packages/core/pvm-interpreter/instruction-gas-map.ts @@ -1,5 +1,5 @@ +import type { SmallGas } from "@typeberry/pvm-interface"; import { byteToOpCodeMap } from "./assemblify.js"; -import type { SmallGas } from "./gas.js"; import { HIGHEST_INSTRUCTION_NUMBER } from "./instruction.js"; export const instructionGasMap = (() => { diff --git a/packages/core/pvm-interpreter/interpreter.ts b/packages/core/pvm-interpreter/interpreter.ts index d8ce22c29..ca560a3e0 100644 --- a/packages/core/pvm-interpreter/interpreter.ts +++ b/packages/core/pvm-interpreter/interpreter.ts @@ -1,12 +1,14 @@ import { Logger } from "@typeberry/logger"; import { tryAsU32, type U32 } from "@typeberry/numbers"; +import { type Gas, type IPvmInterpreter, Status, tryAsGas } from "@typeberry/pvm-interface"; +import { Program } from "@typeberry/pvm-program"; import { ArgsDecoder } from "./args-decoder/args-decoder.js"; import { createResults } from "./args-decoder/args-decoding-results.js"; import { ArgumentType } from "./args-decoder/argument-type.js"; import { instructionArgumentTypeMap } from "./args-decoder/instruction-argument-type-map.js"; import { assemblify } from "./assemblify.js"; import { BasicBlocks } from "./basic-blocks/index.js"; -import { type Gas, type GasCounter, gasCounter, tryAsBigGas, tryAsGas } from "./gas.js"; +import { gasCounter } from "./gas.js"; import { Instruction } from "./instruction.js"; import { instructionGasMap } from "./instruction-gas-map.js"; import { InstructionResult } from "./instruction-result.js"; @@ -49,7 +51,6 @@ import { Mask } from "./program-decoder/mask.js"; import { ProgramDecoder } from "./program-decoder/program-decoder.js"; import { Registers } from "./registers.js"; import { Result } from "./result.js"; -import { Status } from "./status.js"; type InterpreterOptions = { useSbrkGas?: boolean; @@ -57,14 +58,14 @@ type InterpreterOptions = { const logger = Logger.new(import.meta.filename, "pvm"); -export class Interpreter { +export class Interpreter implements IPvmInterpreter { private readonly useSbrkGas: boolean; - private registers = new Registers(); + readonly registers = new Registers(); + readonly memory = new Memory(); + gas = gasCounter(tryAsGas(0)); private code: Uint8Array = new Uint8Array(); private mask = Mask.empty(); private pc = 0; - private gas = gasCounter(tryAsGas(0)); - private initialGas = gasCounter(tryAsGas(0)); private argsDecoder: ArgsDecoder; private threeRegsDispatcher: ThreeRegsDispatcher; private twoRegsOneImmDispatcher: TwoRegsOneImmDispatcher; @@ -74,7 +75,6 @@ export class Interpreter { private oneOffsetDispatcher: OneOffsetDispatcher; private oneRegOneImmDispatcher: OneRegOneImmDispatcher; private instructionResult = new InstructionResult(); - private memory = new Memory(); private twoImmsDispatcher: TwoImmsDispatcher; private oneRegTwoImmsDispatcher: OneRegTwoImmsDispatcher; private noArgsDispatcher: NoArgsDispatcher; @@ -128,7 +128,12 @@ export class Interpreter { this.oneRegOneExtImmDispatcher = new OneRegOneExtImmDispatcher(loadOps); } - reset(rawProgram: Uint8Array, pc: number, gas: Gas, maybeRegisters?: Registers, maybeMemory?: Memory) { + resetJam(program: Uint8Array, args: Uint8Array, pc: number, gas: Gas) { + const p = Program.fromSpi(program, args, true); + this.resetGeneric(p.code, pc, gas, p.registers, p.memory); + } + + resetGeneric(rawProgram: Uint8Array, pc: number, gas: Gas, maybeRegisters?: Registers, maybeMemory?: Memory) { const programDecoder = new ProgramDecoder(rawProgram); this.code = programDecoder.getCode(); this.mask = programDecoder.getMask(); @@ -136,7 +141,6 @@ export class Interpreter { this.pc = pc; this.gas = gasCounter(gas); - this.initialGas = gasCounter(gas); this.status = Status.OK; this.argsDecoder.reset(this.code, this.mask); this.basicBlocks.reset(this.code, this.mask); @@ -278,10 +282,6 @@ export class Interpreter { return this.status; } - getRegisters() { - return this.registers; - } - getPC() { return this.pc; } @@ -290,24 +290,6 @@ export class Interpreter { this.pc = nextPc; } - getGas(): Gas { - return this.gas.get(); - } - - getGasConsumed(): Gas { - const gasConsumed = tryAsBigGas(this.initialGas.get()) - tryAsBigGas(this.gas.get()); - - if (gasConsumed < 0) { - return this.initialGas.get(); - } - - return tryAsBigGas(gasConsumed); - } - - getGasCounter(): GasCounter { - return this.gas; - } - getStatus() { return this.status; } @@ -317,10 +299,6 @@ export class Interpreter { return p !== null ? tryAsU32(p) : p; } - getMemory() { - return this.memory; - } - getMemoryPage(pageNumber: number): null | Uint8Array { return this.memory.getPageDump(tryAsPageNumber(pageNumber)); } diff --git a/packages/core/pvm-interpreter/memory/errors.ts b/packages/core/pvm-interpreter/memory/errors.ts index 2f232c2c7..30a6de494 100644 --- a/packages/core/pvm-interpreter/memory/errors.ts +++ b/packages/core/pvm-interpreter/memory/errors.ts @@ -1,24 +1,25 @@ -import { MEMORY_SIZE } from "./memory-consts.js"; -import { type MemoryIndex, tryAsMemoryIndex } from "./memory-index.js"; +import { tryAsU32, type U32 } from "@typeberry/numbers"; +import { type PageFault as InterpreterPageFault, MEMORY_SIZE } from "@typeberry/pvm-interface"; +import { tryAsMemoryIndex } from "./memory-index.js"; import { getStartPageIndex, getStartPageIndexFromPageNumber } from "./memory-utils.js"; import { tryAsPageNumber } from "./pages/page-utils.js"; -export class PageFault { +export class PageFault implements InterpreterPageFault { private constructor( - public address: MemoryIndex, + public address: U32, public isAccessFault = true, ) {} static fromPageNumber(maybePageNumber: number, isAccessFault = false) { const pageNumber = tryAsPageNumber(maybePageNumber); const startPageIndex = getStartPageIndexFromPageNumber(pageNumber); - return new PageFault(startPageIndex, isAccessFault); + return new PageFault(tryAsU32(startPageIndex), isAccessFault); } static fromMemoryIndex(maybeMemoryIndex: number, isAccessFault = false) { const memoryIndex = tryAsMemoryIndex(maybeMemoryIndex % MEMORY_SIZE); const startPageIndex = getStartPageIndex(memoryIndex); - return new PageFault(startPageIndex, isAccessFault); + return new PageFault(tryAsU32(startPageIndex), isAccessFault); } } diff --git a/packages/core/pvm-interpreter/memory/memory-builder.test.ts b/packages/core/pvm-interpreter/memory/memory-builder.test.ts index e06db96fc..3d960269c 100644 --- a/packages/core/pvm-interpreter/memory/memory-builder.test.ts +++ b/packages/core/pvm-interpreter/memory/memory-builder.test.ts @@ -1,8 +1,9 @@ import assert from "node:assert"; import { describe, it } from "node:test"; +import { MEMORY_SIZE } from "@typeberry/pvm-interface"; import { IncorrectSbrkIndex } from "./errors.js"; import { MemoryBuilder } from "./memory-builder.js"; -import { MEMORY_SIZE, PAGE_SIZE, RESERVED_NUMBER_OF_PAGES } from "./memory-consts.js"; +import { PAGE_SIZE, RESERVED_NUMBER_OF_PAGES } from "./memory-consts.js"; import { tryAsMemoryIndex, tryAsSbrkIndex } from "./memory-index.js"; import { ReadablePage, WriteablePage } from "./pages/index.js"; import { tryAsPageNumber } from "./pages/page-utils.js"; diff --git a/packages/core/pvm-interpreter/memory/memory-consts.ts b/packages/core/pvm-interpreter/memory/memory-consts.ts index 54b4fa398..f3ad96756 100644 --- a/packages/core/pvm-interpreter/memory/memory-consts.ts +++ b/packages/core/pvm-interpreter/memory/memory-consts.ts @@ -1,7 +1,6 @@ +import { MEMORY_SIZE } from "@typeberry/pvm-interface"; import { check } from "@typeberry/utils"; -export const MAX_MEMORY_INDEX = 0xffff_ffff; -export const MEMORY_SIZE = MAX_MEMORY_INDEX + 1; export const PAGE_SIZE_SHIFT = 12; // PAGE_SIZE has to be a power of 2 export const PAGE_SIZE = 1 << PAGE_SIZE_SHIFT; diff --git a/packages/core/pvm-interpreter/memory/memory-index.ts b/packages/core/pvm-interpreter/memory/memory-index.ts index 31f0b6b22..bf999fa23 100644 --- a/packages/core/pvm-interpreter/memory/memory-index.ts +++ b/packages/core/pvm-interpreter/memory/memory-index.ts @@ -1,7 +1,6 @@ +import { MAX_MEMORY_INDEX } from "@typeberry/pvm-interface"; import { asOpaqueType, check, type Opaque } from "@typeberry/utils"; -import { MAX_MEMORY_INDEX } from "./memory-consts.js"; - export type MemoryIndex = Opaque; export const tryAsMemoryIndex = (index: number): MemoryIndex => { diff --git a/packages/core/pvm-interpreter/memory/memory-range.test.ts b/packages/core/pvm-interpreter/memory/memory-range.test.ts index 18d91a8b9..1e3af2b8c 100644 --- a/packages/core/pvm-interpreter/memory/memory-range.test.ts +++ b/packages/core/pvm-interpreter/memory/memory-range.test.ts @@ -1,6 +1,7 @@ import assert from "node:assert"; import { describe, it } from "node:test"; -import { MEMORY_SIZE, PAGE_SIZE } from "./memory-consts.js"; +import { MEMORY_SIZE } from "@typeberry/pvm-interface"; +import { PAGE_SIZE } from "./memory-consts.js"; import { tryAsMemoryIndex } from "./memory-index.js"; import { MemoryRange } from "./memory-range.js"; diff --git a/packages/core/pvm-interpreter/memory/memory-range.ts b/packages/core/pvm-interpreter/memory/memory-range.ts index 61b3bad5b..4ba38fefe 100644 --- a/packages/core/pvm-interpreter/memory/memory-range.ts +++ b/packages/core/pvm-interpreter/memory/memory-range.ts @@ -1,4 +1,5 @@ -import { MEMORY_SIZE, PAGE_SIZE, RESERVED_NUMBER_OF_PAGES } from "./memory-consts.js"; +import { MEMORY_SIZE } from "@typeberry/pvm-interface"; +import { PAGE_SIZE, RESERVED_NUMBER_OF_PAGES } from "./memory-consts.js"; import { type MemoryIndex, tryAsMemoryIndex } from "./memory-index.js"; /** diff --git a/packages/core/pvm-interpreter/memory/memory-utils.test.ts b/packages/core/pvm-interpreter/memory/memory-utils.test.ts index 54747525d..07a0ce608 100644 --- a/packages/core/pvm-interpreter/memory/memory-utils.test.ts +++ b/packages/core/pvm-interpreter/memory/memory-utils.test.ts @@ -1,7 +1,7 @@ import assert from "node:assert"; import { describe, it } from "node:test"; - -import { MAX_MEMORY_INDEX, MEMORY_SIZE, PAGE_SIZE } from "./memory-consts.js"; +import { MAX_MEMORY_INDEX, MEMORY_SIZE } from "@typeberry/pvm-interface"; +import { PAGE_SIZE } from "./memory-consts.js"; import { tryAsMemoryIndex, tryAsSbrkIndex } from "./memory-index.js"; import { alignToPageSize, getPageNumber, getStartPageIndex, getStartPageIndexFromPageNumber } from "./memory-utils.js"; import { tryAsPageNumber } from "./pages/page-utils.js"; diff --git a/packages/core/pvm-interpreter/memory/memory.test.ts b/packages/core/pvm-interpreter/memory/memory.test.ts index 768c10a3e..1036f1d0d 100644 --- a/packages/core/pvm-interpreter/memory/memory.test.ts +++ b/packages/core/pvm-interpreter/memory/memory.test.ts @@ -1,10 +1,10 @@ import assert from "node:assert"; import { describe, it } from "node:test"; - +import { MAX_MEMORY_INDEX } from "@typeberry/pvm-interface"; import { deepEqual, OK, Result } from "@typeberry/utils"; import { PageFault } from "./errors.js"; import { Memory } from "./memory.js"; -import { MAX_MEMORY_INDEX, MIN_ALLOCATION_LENGTH, PAGE_SIZE, RESERVED_NUMBER_OF_PAGES } from "./memory-consts.js"; +import { MIN_ALLOCATION_LENGTH, PAGE_SIZE, RESERVED_NUMBER_OF_PAGES } from "./memory-consts.js"; import { tryAsMemoryIndex, tryAsSbrkIndex } from "./memory-index.js"; import { ReadablePage, WriteablePage } from "./pages/index.js"; import type { MemoryPage } from "./pages/memory-page.js"; diff --git a/packages/core/pvm-interpreter/memory/memory.ts b/packages/core/pvm-interpreter/memory/memory.ts index 805382bdc..13bb49853 100644 --- a/packages/core/pvm-interpreter/memory/memory.ts +++ b/packages/core/pvm-interpreter/memory/memory.ts @@ -1,9 +1,11 @@ import { BytesBlob } from "@typeberry/bytes"; import { Logger } from "@typeberry/logger"; +import type { U32 } from "@typeberry/numbers"; +import { type IMemory, type PageFault as InretpreterPageFault, MAX_MEMORY_INDEX } from "@typeberry/pvm-interface"; import { OK, Result } from "@typeberry/utils"; import { OutOfMemory, PageFault } from "./errors.js"; -import { MAX_MEMORY_INDEX, PAGE_SIZE, RESERVED_NUMBER_OF_PAGES } from "./memory-consts.js"; -import { type MemoryIndex, type SbrkIndex, tryAsSbrkIndex } from "./memory-index.js"; +import { PAGE_SIZE, RESERVED_NUMBER_OF_PAGES } from "./memory-consts.js"; +import { type MemoryIndex, type SbrkIndex, tryAsMemoryIndex, tryAsSbrkIndex } from "./memory-index.js"; import { MemoryRange, RESERVED_MEMORY_RANGE } from "./memory-range.js"; import { alignToPageSize, getPageNumber } from "./memory-utils.js"; import { PageRange } from "./page-range.js"; @@ -24,7 +26,7 @@ enum AccessType { const logger = Logger.new(import.meta.filename, "pvm:mem"); -export class Memory { +export class Memory implements IMemory { static fromInitialMemory(initialMemoryState: InitialMemoryState) { return new Memory( initialMemoryState?.sbrkIndex, @@ -41,6 +43,14 @@ export class Memory { private memory = new Map(), ) {} + store(address: U32, bytes: Uint8Array): Result { + return this.storeFrom(tryAsMemoryIndex(address), bytes); + } + + read(address: U32, output: Uint8Array): Result { + return this.loadInto(output, tryAsMemoryIndex(address)); + } + reset() { this.sbrkIndex = tryAsSbrkIndex(RESERVED_MEMORY_RANGE.end); this.virtualSbrkIndex = tryAsSbrkIndex(RESERVED_MEMORY_RANGE.end); diff --git a/packages/core/pvm-interpreter/memory/page-range.test.ts b/packages/core/pvm-interpreter/memory/page-range.test.ts index d79f0b4da..20640ed21 100644 --- a/packages/core/pvm-interpreter/memory/page-range.test.ts +++ b/packages/core/pvm-interpreter/memory/page-range.test.ts @@ -1,6 +1,7 @@ import assert from "node:assert"; import { describe, it } from "node:test"; -import { MAX_MEMORY_INDEX, MAX_NUMBER_OF_PAGES, MEMORY_SIZE, PAGE_SIZE } from "./memory-consts.js"; +import { MAX_MEMORY_INDEX, MEMORY_SIZE } from "@typeberry/pvm-interface"; +import { MAX_NUMBER_OF_PAGES, PAGE_SIZE } from "./memory-consts.js"; import { tryAsMemoryIndex } from "./memory-index.js"; import { MemoryRange } from "./memory-range.js"; import { PageRange } from "./page-range.js"; diff --git a/packages/core/pvm-interpreter/ops/memory-ops.test.ts b/packages/core/pvm-interpreter/ops/memory-ops.test.ts index 9adb517df..3f26628f0 100644 --- a/packages/core/pvm-interpreter/ops/memory-ops.test.ts +++ b/packages/core/pvm-interpreter/ops/memory-ops.test.ts @@ -1,8 +1,9 @@ import assert from "node:assert"; import { describe, it } from "node:test"; +import { MAX_MEMORY_INDEX } from "@typeberry/pvm-interface"; import { InstructionResult } from "../instruction-result.js"; import { Memory, MemoryBuilder } from "../memory/index.js"; -import { MAX_MEMORY_INDEX, PAGE_SIZE, RESERVED_NUMBER_OF_PAGES } from "../memory/memory-consts.js"; +import { PAGE_SIZE, RESERVED_NUMBER_OF_PAGES } from "../memory/memory-consts.js"; import { tryAsMemoryIndex, tryAsSbrkIndex } from "../memory/memory-index.js"; import { Registers } from "../registers.js"; import { MemoryOps } from "./memory-ops.js"; diff --git a/packages/core/pvm-interpreter/ops/store-ops.ts b/packages/core/pvm-interpreter/ops/store-ops.ts index b99b60b26..d90342d74 100644 --- a/packages/core/pvm-interpreter/ops/store-ops.ts +++ b/packages/core/pvm-interpreter/ops/store-ops.ts @@ -112,7 +112,7 @@ export class StoreOps { this.instructionResult.status = Result.FAULT_ACCESS; } else { this.instructionResult.status = Result.FAULT; - this.instructionResult.exitParam = getStartPageIndex(storeResult.error.address); + this.instructionResult.exitParam = getStartPageIndex(tryAsMemoryIndex(storeResult.error.address)); } } } diff --git a/packages/core/pvm-interpreter/package.json b/packages/core/pvm-interpreter/package.json index 3d6daa452..dc6e8f8cd 100644 --- a/packages/core/pvm-interpreter/package.json +++ b/packages/core/pvm-interpreter/package.json @@ -8,6 +8,8 @@ "@typeberry/codec": "*", "@typeberry/logger": "*", "@typeberry/numbers": "*", + "@typeberry/pvm-interface": "*", + "@typeberry/pvm-program": "*", "@typeberry/utils": "*" }, "scripts": { diff --git a/packages/core/pvm-interpreter/registers.test.ts b/packages/core/pvm-interpreter/registers.test.ts index 56127bf4f..bc12c1d83 100644 --- a/packages/core/pvm-interpreter/registers.test.ts +++ b/packages/core/pvm-interpreter/registers.test.ts @@ -3,6 +3,7 @@ import { describe, it } from "node:test"; import { Registers } from "./registers.js"; const U32_BYTES = 4; +const U64_BYTES = 8; describe("Registers", () => { describe("loading values", () => { @@ -85,4 +86,58 @@ describe("Registers", () => { assert.deepStrictEqual(regs.getBytesAsLittleEndian(1, U32_BYTES), expectedBytes); }); }); + + describe("Implemented IRegister", () => { + it("should correctly get all registers into bytes encoded", () => { + const regs = new Registers(); + + const num = 0xef_cd_ab_89_67_45_23_01n; + const bytesReg = new Uint8Array([0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef]); + const fill = new Uint8Array(12 * U64_BYTES).fill(0); // we set 1st register so we fill remaining 12 with 0 + const expected = new Uint8Array([...bytesReg, ...fill]); + + regs.setU64(0, num); + + // when + const encodedAllRegisters = regs.getAllEncoded(); + + // then + assert.deepStrictEqual(encodedAllRegisters.length, expected.length); + assert.deepStrictEqual(encodedAllRegisters, expected); + }); + + it("should correctly set all registers from bytes encoded", () => { + const regs = new Registers(); + + const bytesReg = new Uint8Array([0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef]); + const fill = new Uint8Array(12 * U64_BYTES).fill(0); // we set 1st register so we fill remaining 12 with 0 + const bytes = new Uint8Array([...bytesReg, ...fill]); + + const expected = 0xef_cd_ab_89_67_45_23_01n; + + regs.setAllEncoded(bytes); + + const reg = regs.getU64(0); + + assert.deepStrictEqual(reg, expected); + }); + + it("should throw when trying to set all registers from bytes encoded with incorrect size", () => { + const regs = new Registers(); + + const bytesReg = new Uint8Array([0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef]); + const fill = new Uint8Array(12 * U64_BYTES).fill(0); // we set 1st register so we fill remaining 12 with 0 + const bytes = new Uint8Array([...bytesReg, ...fill, 0x00]); + + // too many + assert.throws(() => { + regs.setAllEncoded(bytes); + }); + + // too little + assert.throws(() => { + regs.setAllEncoded(fill); + }); + }); + }); }); diff --git a/packages/core/pvm-interpreter/registers.ts b/packages/core/pvm-interpreter/registers.ts index 30a7de456..d3a64e0c9 100644 --- a/packages/core/pvm-interpreter/registers.ts +++ b/packages/core/pvm-interpreter/registers.ts @@ -1,7 +1,7 @@ +import { type IRegisters, NO_OF_REGISTERS } from "@typeberry/pvm-interface"; import { asOpaqueType, check, type Opaque, safeAllocUint8Array } from "@typeberry/utils"; -export const NO_OF_REGISTERS = 13; -const REGISTER_SIZE_SHIFT = 3; +const REGISTER_SIZE_SHIFT = 3; // x << 3 === x * 8 export type RegisterIndex = Opaque; @@ -10,7 +10,7 @@ export const tryAsRegisterIndex = (index: number): RegisterIndex => { return asOpaqueType(index); }; -export class Registers { +export class Registers implements IRegisters { private asSigned: BigInt64Array; private asUnsigned: BigUint64Array; @@ -20,6 +20,15 @@ export class Registers { this.asUnsigned = new BigUint64Array(bytes.buffer, bytes.byteOffset); } + getAllEncoded(): Uint8Array { + return this.bytes; + } + + setAllEncoded(bytes: Uint8Array): void { + check`${bytes.length === this.bytes.length} Incorrect size of input registers. Got: ${bytes.length}, need: ${this.bytes.length}`; + this.bytes.set(bytes, 0); + } + static fromBytes(bytes: Uint8Array) { check`${bytes.length === NO_OF_REGISTERS << REGISTER_SIZE_SHIFT} Invalid size of registers array.`; return new Registers(bytes); @@ -30,10 +39,6 @@ export class Registers { return this.bytes.subarray(offset, offset + len); } - getAllBytesAsLittleEndian() { - return this.bytes; - } - copyFrom(regs: Registers | BigUint64Array) { const array = regs instanceof BigUint64Array ? regs : regs.asUnsigned; this.asUnsigned.set(array); diff --git a/packages/core/pvm-interpreter/status.ts b/packages/core/pvm-interpreter/status.ts deleted file mode 100644 index e44dda2c5..000000000 --- a/packages/core/pvm-interpreter/status.ts +++ /dev/null @@ -1,13 +0,0 @@ -/** - * Inner status codes for the PVM - * - * https://graypaper.fluffylabs.dev/#/85129da/2cae022cae02?v=0.6.3 - */ -export enum Status { - OK = 255, - HALT = 0, - PANIC = 1, - FAULT = 2, - HOST = 3, - OOG = 4, -} diff --git a/packages/jam/config-node/node-config.ts b/packages/jam/config-node/node-config.ts index ec73bd827..d60ca9cf4 100644 --- a/packages/jam/config-node/node-config.ts +++ b/packages/jam/config-node/node-config.ts @@ -1,6 +1,7 @@ import fs from "node:fs"; import os from "node:os"; import type { JsonObject } from "@typeberry/block-json"; +import { PvmBackend } from "@typeberry/config"; import { configs } from "@typeberry/configs"; import { type FromJson, json, parseFromJson } from "@typeberry/json-parser"; import { Logger } from "@typeberry/logger"; @@ -18,6 +19,7 @@ export const DEFAULT_CONFIG = "default"; export const NODE_DEFAULTS = { name: isBrowser() ? "browser" : os.hostname(), config: DEFAULT_CONFIG, + pvm: PvmBackend.BuiltIn, }; /** Chain spec chooser. */ diff --git a/packages/jam/config/index.ts b/packages/jam/config/index.ts index cb1cfaa47..4887e0122 100644 --- a/packages/jam/config/index.ts +++ b/packages/jam/config/index.ts @@ -1,2 +1,3 @@ export * from "./chain-spec.js"; export * from "./network.js"; +export * from "./pvm-backend.js"; diff --git a/packages/jam/config/pvm-backend.ts b/packages/jam/config/pvm-backend.ts new file mode 100644 index 000000000..8e07a0ed5 --- /dev/null +++ b/packages/jam/config/pvm-backend.ts @@ -0,0 +1,10 @@ +/** Implemented PVM Backends names in THE SAME ORDER as enum. */ +export const PvmBackendNames = ["built-in", "ananas"]; + +/** Implemented PVM Backends to choose from. */ +export enum PvmBackend { + /** Built-in aka. Typeberry 🫐 interpreter. */ + BuiltIn = 0, + /** Ananas 🍍 interpreter. */ + Ananas = 1, +} diff --git a/packages/jam/jam-host-calls/accumulate/assign.test.ts b/packages/jam/jam-host-calls/accumulate/assign.test.ts index 205d8783b..44f6563f6 100644 --- a/packages/jam/jam-host-calls/accumulate/assign.test.ts +++ b/packages/jam/jam-host-calls/accumulate/assign.test.ts @@ -8,8 +8,8 @@ import { tinyChainSpec } from "@typeberry/config"; import { type Blake2bHash, HASH_SIZE } from "@typeberry/hash"; import { tryAsU64 } from "@typeberry/numbers"; import { HostCallMemory, HostCallRegisters, PvmExecution } from "@typeberry/pvm-host-calls"; -import { Registers } from "@typeberry/pvm-interpreter"; -import { gasCounter, tryAsGas } from "@typeberry/pvm-interpreter/gas.js"; +import { tryAsGas } from "@typeberry/pvm-interface"; +import { gasCounter } from "@typeberry/pvm-interpreter/gas.js"; import { MemoryBuilder, tryAsMemoryIndex } from "@typeberry/pvm-interpreter/memory/index.js"; import { tryAsSbrkIndex } from "@typeberry/pvm-interpreter/memory/memory-index.js"; import { PAGE_SIZE } from "@typeberry/pvm-spi-decoder/memory-conts.js"; @@ -18,6 +18,7 @@ import { Result } from "@typeberry/utils"; import { UpdatePrivilegesError } from "../externalities/partial-state.js"; import { PartialStateMock } from "../externalities/partial-state-mock.js"; import { HostCallResult } from "../results.js"; +import { emptyRegistersBuffer } from "../utils.js"; import { Assign } from "./assign.js"; const gas = gasCounter(tryAsGas(0)); @@ -32,7 +33,7 @@ function prepareRegsAndMemory( { skipAuthQueue = false, assigners = null }: { skipAuthQueue?: boolean; assigners?: bigint | number | null } = {}, ) { const memStart = 2 ** 16; - const registers = new HostCallRegisters(new Registers()); + const registers = new HostCallRegisters(emptyRegistersBuffer()); registers.set(CORE_INDEX_REG, tryAsU64(coreIndex)); registers.set(AUTH_QUEUE_START_REG, tryAsU64(memStart)); if (assigners !== null) { @@ -52,10 +53,10 @@ function prepareRegsAndMemory( if (!skipAuthQueue) { builder.setReadablePages(tryAsMemoryIndex(memStart), tryAsMemoryIndex(memStart + PAGE_SIZE), data.raw); } - const memory = builder.finalize(tryAsMemoryIndex(0), tryAsSbrkIndex(0)); + const memory = new HostCallMemory(builder.finalize(tryAsMemoryIndex(0), tryAsSbrkIndex(0))); return { registers, - memory: new HostCallMemory(memory), + memory, }; } diff --git a/packages/jam/jam-host-calls/accumulate/assign.ts b/packages/jam/jam-host-calls/accumulate/assign.ts index e4594229b..9437ddf09 100644 --- a/packages/jam/jam-host-calls/accumulate/assign.ts +++ b/packages/jam/jam-host-calls/accumulate/assign.ts @@ -3,9 +3,9 @@ import { codec, Decoder } from "@typeberry/codec"; import { FixedSizeArray } from "@typeberry/collections"; import type { ChainSpec } from "@typeberry/config"; import { HASH_SIZE } from "@typeberry/hash"; -import type { HostCallHandler, IHostCallMemory, IHostCallRegisters } from "@typeberry/pvm-host-calls"; +import type { HostCallHandler, HostCallMemory, HostCallRegisters } from "@typeberry/pvm-host-calls"; import { PvmExecution, traceRegisters, tryAsHostCallIndex } from "@typeberry/pvm-host-calls"; -import { type GasCounter, tryAsSmallGas } from "@typeberry/pvm-interpreter/gas.js"; +import { type IGasCounter, tryAsSmallGas } from "@typeberry/pvm-interface"; import { AUTHORIZATION_QUEUE_SIZE } from "@typeberry/state"; import { assertNever, safeAllocUint8Array } from "@typeberry/utils"; import { type PartialState, UpdatePrivilegesError } from "../externalities/partial-state.js"; @@ -31,11 +31,7 @@ export class Assign implements HostCallHandler { private readonly chainSpec: ChainSpec, ) {} - async execute( - _gas: GasCounter, - regs: IHostCallRegisters, - memory: IHostCallMemory, - ): Promise { + async execute(_gas: IGasCounter, regs: HostCallRegisters, memory: HostCallMemory): Promise { // c const maybeCoreIndex = regs.get(IN_OUT_REG); // o diff --git a/packages/jam/jam-host-calls/accumulate/bless.test.ts b/packages/jam/jam-host-calls/accumulate/bless.test.ts index d38504fff..013cf1c8a 100644 --- a/packages/jam/jam-host-calls/accumulate/bless.test.ts +++ b/packages/jam/jam-host-calls/accumulate/bless.test.ts @@ -5,8 +5,8 @@ import { codec, Encoder } from "@typeberry/codec"; import { tinyChainSpec } from "@typeberry/config"; import { tryAsU64, type U64 } from "@typeberry/numbers"; import { HostCallMemory, HostCallRegisters, PvmExecution } from "@typeberry/pvm-host-calls"; -import { Registers } from "@typeberry/pvm-interpreter"; -import { gasCounter, tryAsGas } from "@typeberry/pvm-interpreter/gas.js"; +import { tryAsGas } from "@typeberry/pvm-interface"; +import { gasCounter } from "@typeberry/pvm-interpreter/gas.js"; import { MemoryBuilder, tryAsMemoryIndex } from "@typeberry/pvm-interpreter/memory/index.js"; import { PAGE_SIZE } from "@typeberry/pvm-interpreter/memory/memory-consts.js"; import { tryAsSbrkIndex } from "@typeberry/pvm-interpreter/memory/memory-index.js"; @@ -16,6 +16,7 @@ import { Compatibility, GpVersion, Result } from "@typeberry/utils"; import { UpdatePrivilegesError } from "../externalities/partial-state.js"; import { PartialStateMock } from "../externalities/partial-state-mock.js"; import { HostCallResult } from "../results.js"; +import { emptyRegistersBuffer } from "../utils.js"; import { Bless } from "./bless.js"; const gas = gasCounter(tryAsGas(0)); @@ -54,7 +55,7 @@ function prepareRegsAndMemory( ) { const memAuthStart = 2 ** 24; const memStart = 2 ** 16; - const registers = new HostCallRegisters(new Registers()); + const registers = new HostCallRegisters(emptyRegistersBuffer()); registers.set(MANAGER_REG, manager ?? tryAsU64(5)); registers.set(AUTHORIZATION_REG, tryAsU64(memAuthStart)); registers.set(VALIDATOR_REG, validator ?? tryAsU64(20)); @@ -80,10 +81,10 @@ function prepareRegsAndMemory( builder.setReadablePages(tryAsMemoryIndex(memAuthStart), tryAsMemoryIndex(memAuthStart + PAGE_SIZE), dataAuth.raw); } - const memory = builder.finalize(tryAsMemoryIndex(0), tryAsSbrkIndex(0)); + const memory = new HostCallMemory(builder.finalize(tryAsMemoryIndex(0), tryAsSbrkIndex(0))); return { registers, - memory: new HostCallMemory(memory), + memory, }; } describe("HostCalls: Bless", () => { diff --git a/packages/jam/jam-host-calls/accumulate/bless.ts b/packages/jam/jam-host-calls/accumulate/bless.ts index a899d6a95..dce274e4a 100644 --- a/packages/jam/jam-host-calls/accumulate/bless.ts +++ b/packages/jam/jam-host-calls/accumulate/bless.ts @@ -2,9 +2,9 @@ import { type ServiceGas, type ServiceId, tryAsServiceGas, tryAsServiceId } from import { codec, Decoder, tryAsExactBytes } from "@typeberry/codec"; import type { ChainSpec } from "@typeberry/config"; import { tryAsU64 } from "@typeberry/numbers"; -import type { HostCallHandler, IHostCallMemory, IHostCallRegisters } from "@typeberry/pvm-host-calls"; +import type { HostCallHandler, HostCallMemory, HostCallRegisters } from "@typeberry/pvm-host-calls"; import { PvmExecution, traceRegisters, tryAsHostCallIndex } from "@typeberry/pvm-host-calls"; -import { type GasCounter, tryAsSmallGas } from "@typeberry/pvm-interpreter"; +import { type IGasCounter, tryAsSmallGas } from "@typeberry/pvm-interface"; import { tryAsPerCore } from "@typeberry/state"; import { asOpaqueType, assertNever, Compatibility, GpVersion, safeAllocUint8Array } from "@typeberry/utils"; import { type PartialState, UpdatePrivilegesError } from "../externalities/partial-state.js"; @@ -43,11 +43,7 @@ export class Bless implements HostCallHandler { private readonly chainSpec: ChainSpec, ) {} - async execute( - _gas: GasCounter, - regs: IHostCallRegisters, - memory: IHostCallMemory, - ): Promise { + async execute(_gas: IGasCounter, regs: HostCallRegisters, memory: HostCallMemory): Promise { // `m`: manager service (can change privileged services) const manager = getServiceId(regs.get(IN_OUT_REG)); // `a`: manages authorization queue diff --git a/packages/jam/jam-host-calls/accumulate/checkpoint.test.ts b/packages/jam/jam-host-calls/accumulate/checkpoint.test.ts index 45cd95d4e..39b2476fa 100644 --- a/packages/jam/jam-host-calls/accumulate/checkpoint.test.ts +++ b/packages/jam/jam-host-calls/accumulate/checkpoint.test.ts @@ -2,9 +2,10 @@ import assert from "node:assert"; import { describe, it } from "node:test"; import { tryAsServiceId } from "@typeberry/block"; import { HostCallRegisters } from "@typeberry/pvm-host-calls"; -import { gasCounter, tryAsGas } from "@typeberry/pvm-interpreter/gas.js"; -import { Registers } from "@typeberry/pvm-interpreter/registers.js"; +import { tryAsGas } from "@typeberry/pvm-interface"; +import { gasCounter } from "@typeberry/pvm-interpreter/gas.js"; import { PartialStateMock } from "../externalities/partial-state-mock.js"; +import { emptyRegistersBuffer } from "../utils.js"; import { Checkpoint } from "./checkpoint.js"; const REGISTER = 7; @@ -16,7 +17,7 @@ describe("HostCalls: Checkpoint", () => { const checkpoint = new Checkpoint(serviceId, accumulate); const counter = gasCounter(tryAsGas(2n ** 42n - 1n)); - const regs = new HostCallRegisters(new Registers()); + const regs = new HostCallRegisters(emptyRegistersBuffer()); assert.deepStrictEqual(regs.get(REGISTER), 0n); assert.deepStrictEqual(accumulate.checkpointCalled, 0); diff --git a/packages/jam/jam-host-calls/accumulate/checkpoint.ts b/packages/jam/jam-host-calls/accumulate/checkpoint.ts index 96b662c31..a049315ba 100644 --- a/packages/jam/jam-host-calls/accumulate/checkpoint.ts +++ b/packages/jam/jam-host-calls/accumulate/checkpoint.ts @@ -1,7 +1,7 @@ import type { ServiceId } from "@typeberry/block"; -import type { HostCallHandler, IHostCallRegisters } from "@typeberry/pvm-host-calls"; +import type { HostCallHandler, HostCallRegisters } from "@typeberry/pvm-host-calls"; import { type PvmExecution, tryAsHostCallIndex } from "@typeberry/pvm-host-calls"; -import { type GasCounter, tryAsSmallGas } from "@typeberry/pvm-interpreter/gas.js"; +import { type IGasCounter, tryAsSmallGas } from "@typeberry/pvm-interface"; import type { RegisterIndex } from "@typeberry/pvm-interpreter/registers.js"; import type { PartialState } from "../externalities/partial-state.js"; import { GasHostCall } from "../gas.js"; @@ -27,7 +27,7 @@ export class Checkpoint implements HostCallHandler { this.tracedRegisters = this.gasHostCall.tracedRegisters; } - async execute(gas: GasCounter, regs: IHostCallRegisters): Promise { + async execute(gas: IGasCounter, regs: HostCallRegisters): Promise { await this.gasHostCall.execute(gas, regs); this.partialState.checkpoint(); logger.trace`CHECKPOINT()`; diff --git a/packages/jam/jam-host-calls/accumulate/designate.test.ts b/packages/jam/jam-host-calls/accumulate/designate.test.ts index 9fe733d44..e68da84de 100644 --- a/packages/jam/jam-host-calls/accumulate/designate.test.ts +++ b/packages/jam/jam-host-calls/accumulate/designate.test.ts @@ -7,16 +7,17 @@ import { tinyChainSpec } from "@typeberry/config"; import { BANDERSNATCH_KEY_BYTES, BLS_KEY_BYTES, ED25519_KEY_BYTES } from "@typeberry/crypto"; import { tryAsU64 } from "@typeberry/numbers"; import { HostCallMemory, HostCallRegisters, PvmExecution } from "@typeberry/pvm-host-calls"; -import { gasCounter, tryAsGas } from "@typeberry/pvm-interpreter/gas.js"; +import { tryAsGas } from "@typeberry/pvm-interface"; +import { gasCounter } from "@typeberry/pvm-interpreter/gas.js"; import { MemoryBuilder, tryAsMemoryIndex } from "@typeberry/pvm-interpreter/memory/index.js"; import { PAGE_SIZE } from "@typeberry/pvm-interpreter/memory/memory-consts.js"; import { tryAsSbrkIndex } from "@typeberry/pvm-interpreter/memory/memory-index.js"; -import { Registers } from "@typeberry/pvm-interpreter/registers.js"; import { VALIDATOR_META_BYTES, ValidatorData } from "@typeberry/state"; import { Result } from "@typeberry/utils"; import { UnprivilegedError } from "../externalities/partial-state.js"; import { PartialStateMock } from "../externalities/partial-state-mock.js"; import { HostCallResult } from "../results.js"; +import { emptyRegistersBuffer } from "../utils.js"; import { Designate } from "./designate.js"; const gas = gasCounter(tryAsGas(0)); @@ -28,7 +29,7 @@ function prepareRegsAndMemory( { skipValidators = false }: { skipValidators?: boolean } = {}, ) { const memStart = 2 ** 16; - const registers = new HostCallRegisters(new Registers()); + const registers = new HostCallRegisters(emptyRegistersBuffer()); registers.set(VALIDATORS_DATA_START_REG, tryAsU64(memStart)); const builder = new MemoryBuilder(); @@ -51,10 +52,10 @@ function prepareRegsAndMemory( if (!skipValidators) { builder.setReadablePages(tryAsMemoryIndex(memStart), tryAsMemoryIndex(memStart + PAGE_SIZE), data.raw); } - const memory = builder.finalize(tryAsMemoryIndex(0), tryAsSbrkIndex(0)); + const memory = new HostCallMemory(builder.finalize(tryAsMemoryIndex(0), tryAsSbrkIndex(0))); return { registers: registers, - memory: new HostCallMemory(memory), + memory, }; } diff --git a/packages/jam/jam-host-calls/accumulate/designate.ts b/packages/jam/jam-host-calls/accumulate/designate.ts index df16ece91..ebc1523f3 100644 --- a/packages/jam/jam-host-calls/accumulate/designate.ts +++ b/packages/jam/jam-host-calls/accumulate/designate.ts @@ -1,9 +1,9 @@ import { type ServiceId, tryAsPerValidator } from "@typeberry/block"; import { Decoder, tryAsExactBytes } from "@typeberry/codec"; import type { ChainSpec } from "@typeberry/config"; -import type { HostCallHandler, IHostCallMemory, IHostCallRegisters } from "@typeberry/pvm-host-calls"; +import type { HostCallHandler, HostCallMemory, HostCallRegisters } from "@typeberry/pvm-host-calls"; import { PvmExecution, traceRegisters, tryAsHostCallIndex } from "@typeberry/pvm-host-calls"; -import { type GasCounter, tryAsSmallGas } from "@typeberry/pvm-interpreter/gas.js"; +import { type IGasCounter, tryAsSmallGas } from "@typeberry/pvm-interface"; import { ValidatorData } from "@typeberry/state"; import { safeAllocUint8Array } from "@typeberry/utils"; import type { PartialState } from "../externalities/partial-state.js"; @@ -29,11 +29,7 @@ export class Designate implements HostCallHandler { private readonly chainSpec: ChainSpec, ) {} - async execute( - _gas: GasCounter, - regs: IHostCallRegisters, - memory: IHostCallMemory, - ): Promise { + async execute(_gas: IGasCounter, regs: HostCallRegisters, memory: HostCallMemory): Promise { // `o` const validatorsStart = regs.get(IN_OUT_REG); diff --git a/packages/jam/jam-host-calls/accumulate/eject.test.ts b/packages/jam/jam-host-calls/accumulate/eject.test.ts index 4a33aa41b..2cc3350a7 100644 --- a/packages/jam/jam-host-calls/accumulate/eject.test.ts +++ b/packages/jam/jam-host-calls/accumulate/eject.test.ts @@ -5,16 +5,17 @@ import { Bytes } from "@typeberry/bytes"; import { HASH_SIZE } from "@typeberry/hash"; import { tryAsU64 } from "@typeberry/numbers"; import { HostCallMemory, HostCallRegisters, PvmExecution } from "@typeberry/pvm-host-calls"; +import { tryAsGas } from "@typeberry/pvm-interface"; import { MemoryBuilder } from "@typeberry/pvm-interpreter"; -import { gasCounter, tryAsGas } from "@typeberry/pvm-interpreter/gas.js"; +import { gasCounter } from "@typeberry/pvm-interpreter/gas.js"; import { tryAsMemoryIndex } from "@typeberry/pvm-interpreter/memory/index.js"; import { tryAsSbrkIndex } from "@typeberry/pvm-interpreter/memory/memory-index.js"; -import { Registers } from "@typeberry/pvm-interpreter/registers.js"; import { PAGE_SIZE } from "@typeberry/pvm-spi-decoder/memory-conts.js"; import { deepEqual, OK, Result } from "@typeberry/utils"; import { EjectError } from "../externalities/partial-state.js"; import { PartialStateMock } from "../externalities/partial-state-mock.js"; import { HostCallResult } from "../results.js"; +import { emptyRegistersBuffer } from "../utils.js"; import { Eject } from "./eject.js"; const RESULT_REG = 7; @@ -27,7 +28,7 @@ function prepareRegsAndMemory( { skipHash = false }: { skipHash?: boolean } = {}, ) { const hashStart = 2 ** 16; - const registers = new HostCallRegisters(new Registers()); + const registers = new HostCallRegisters(emptyRegistersBuffer()); registers.set(SOURCE_REG, tryAsU64(source)); registers.set(HASH_START_REG, tryAsU64(hashStart)); @@ -36,10 +37,10 @@ function prepareRegsAndMemory( builder.setReadablePages(tryAsMemoryIndex(hashStart), tryAsMemoryIndex(hashStart + PAGE_SIZE), hash.raw); } - const memory = builder.finalize(tryAsMemoryIndex(0), tryAsSbrkIndex(0)); + const memory = new HostCallMemory(builder.finalize(tryAsMemoryIndex(0), tryAsSbrkIndex(0))); return { registers, - memory: new HostCallMemory(memory), + memory, }; } diff --git a/packages/jam/jam-host-calls/accumulate/eject.ts b/packages/jam/jam-host-calls/accumulate/eject.ts index 45db683d0..a73d1958d 100644 --- a/packages/jam/jam-host-calls/accumulate/eject.ts +++ b/packages/jam/jam-host-calls/accumulate/eject.ts @@ -2,9 +2,9 @@ import type { ServiceId } from "@typeberry/block"; import type { PreimageHash } from "@typeberry/block/preimage.js"; import { Bytes } from "@typeberry/bytes"; import { HASH_SIZE } from "@typeberry/hash"; -import type { HostCallHandler, IHostCallMemory, IHostCallRegisters } from "@typeberry/pvm-host-calls"; +import type { HostCallHandler, HostCallMemory, HostCallRegisters } from "@typeberry/pvm-host-calls"; import { PvmExecution, traceRegisters, tryAsHostCallIndex } from "@typeberry/pvm-host-calls"; -import { type GasCounter, tryAsSmallGas } from "@typeberry/pvm-interpreter/gas.js"; +import { type IGasCounter, tryAsSmallGas } from "@typeberry/pvm-interface"; import { assertNever, resultToString } from "@typeberry/utils"; import { EjectError, type PartialState } from "../externalities/partial-state.js"; import { logger } from "../logger.js"; @@ -28,11 +28,7 @@ export class Eject implements HostCallHandler { private readonly partialState: PartialState, ) {} - async execute( - _gas: GasCounter, - regs: IHostCallRegisters, - memory: IHostCallMemory, - ): Promise { + async execute(_gas: IGasCounter, regs: HostCallRegisters, memory: HostCallMemory): Promise { // `d`: account to eject from (source) const serviceId = getServiceId(regs.get(IN_OUT_REG)); // `o`: preimage hash start memory index diff --git a/packages/jam/jam-host-calls/accumulate/forget.test.ts b/packages/jam/jam-host-calls/accumulate/forget.test.ts index eb922f04d..dcd8ea1b0 100644 --- a/packages/jam/jam-host-calls/accumulate/forget.test.ts +++ b/packages/jam/jam-host-calls/accumulate/forget.test.ts @@ -5,8 +5,8 @@ import { Bytes } from "@typeberry/bytes"; import { HASH_SIZE } from "@typeberry/hash"; import { tryAsU64, type U64 } from "@typeberry/numbers"; import { HostCallMemory, HostCallRegisters, PvmExecution } from "@typeberry/pvm-host-calls"; -import { Registers } from "@typeberry/pvm-interpreter"; -import { gasCounter, tryAsGas } from "@typeberry/pvm-interpreter/gas.js"; +import { tryAsGas } from "@typeberry/pvm-interface"; +import { gasCounter } from "@typeberry/pvm-interpreter/gas.js"; import { MemoryBuilder, tryAsMemoryIndex } from "@typeberry/pvm-interpreter/memory/index.js"; import { tryAsSbrkIndex } from "@typeberry/pvm-interpreter/memory/memory-index.js"; import { PAGE_SIZE } from "@typeberry/pvm-spi-decoder/memory-conts.js"; @@ -14,6 +14,7 @@ import { Result } from "@typeberry/utils"; import { ForgetPreimageError } from "../externalities/partial-state.js"; import { PartialStateMock } from "../externalities/partial-state-mock.js"; import { HostCallResult } from "../results.js"; +import { emptyRegistersBuffer } from "../utils.js"; import { Forget } from "./forget.js"; const gas = gasCounter(tryAsGas(0)); @@ -27,7 +28,7 @@ function prepareRegsAndMemory( { skipPreimageHash = false }: { skipPreimageHash?: boolean } = {}, ) { const memStart = 2 ** 16; - const registers = new HostCallRegisters(new Registers()); + const registers = new HostCallRegisters(emptyRegistersBuffer()); registers.set(HASH_START_REG, tryAsU64(memStart)); registers.set(LENGTH_REG, preimageLength); @@ -36,10 +37,10 @@ function prepareRegsAndMemory( if (!skipPreimageHash) { builder.setReadablePages(tryAsMemoryIndex(memStart), tryAsMemoryIndex(memStart + PAGE_SIZE), preimageHash.raw); } - const memory = builder.finalize(tryAsMemoryIndex(0), tryAsSbrkIndex(0)); + const memory = new HostCallMemory(builder.finalize(tryAsMemoryIndex(0), tryAsSbrkIndex(0))); return { registers, - memory: new HostCallMemory(memory), + memory, }; } diff --git a/packages/jam/jam-host-calls/accumulate/forget.ts b/packages/jam/jam-host-calls/accumulate/forget.ts index 0719b123c..fb30de3ab 100644 --- a/packages/jam/jam-host-calls/accumulate/forget.ts +++ b/packages/jam/jam-host-calls/accumulate/forget.ts @@ -1,9 +1,9 @@ import type { ServiceId } from "@typeberry/block"; import { Bytes } from "@typeberry/bytes"; import { HASH_SIZE } from "@typeberry/hash"; -import type { HostCallHandler, IHostCallMemory, IHostCallRegisters } from "@typeberry/pvm-host-calls"; +import type { HostCallHandler, HostCallMemory, HostCallRegisters } from "@typeberry/pvm-host-calls"; import { PvmExecution, traceRegisters, tryAsHostCallIndex } from "@typeberry/pvm-host-calls"; -import { type GasCounter, tryAsSmallGas } from "@typeberry/pvm-interpreter/gas.js"; +import { type IGasCounter, tryAsSmallGas } from "@typeberry/pvm-interface"; import { resultToString } from "@typeberry/utils"; import type { PartialState } from "../externalities/partial-state.js"; import { logger } from "../logger.js"; @@ -26,11 +26,7 @@ export class Forget implements HostCallHandler { private readonly partialState: PartialState, ) {} - async execute( - _gas: GasCounter, - regs: IHostCallRegisters, - memory: IHostCallMemory, - ): Promise { + async execute(_gas: IGasCounter, regs: HostCallRegisters, memory: HostCallMemory): Promise { // `o` const hashStart = regs.get(IN_OUT_REG); // `z` diff --git a/packages/jam/jam-host-calls/accumulate/new.test.ts b/packages/jam/jam-host-calls/accumulate/new.test.ts index e3dcee227..090d5c009 100644 --- a/packages/jam/jam-host-calls/accumulate/new.test.ts +++ b/packages/jam/jam-host-calls/accumulate/new.test.ts @@ -5,8 +5,8 @@ import { Bytes } from "@typeberry/bytes"; import { HASH_SIZE } from "@typeberry/hash"; import { tryAsU64, type U64 } from "@typeberry/numbers"; import { HostCallMemory, HostCallRegisters, PvmExecution } from "@typeberry/pvm-host-calls"; -import { Registers } from "@typeberry/pvm-interpreter"; -import { gasCounter, tryAsGas } from "@typeberry/pvm-interpreter/gas.js"; +import { tryAsGas } from "@typeberry/pvm-interface"; +import { gasCounter } from "@typeberry/pvm-interpreter/gas.js"; import { MemoryBuilder, tryAsMemoryIndex } from "@typeberry/pvm-interpreter/memory/index.js"; import { tryAsSbrkIndex } from "@typeberry/pvm-interpreter/memory/memory-index.js"; import { PAGE_SIZE } from "@typeberry/pvm-spi-decoder/memory-conts.js"; @@ -14,6 +14,7 @@ import { Compatibility, GpVersion, Result } from "@typeberry/utils"; import { NewServiceError } from "../externalities/partial-state.js"; import { PartialStateMock } from "../externalities/partial-state-mock.js"; import { HostCallResult } from "../results.js"; +import { emptyRegistersBuffer } from "../utils.js"; import { New } from "./new.js"; const gas = gasCounter(tryAsGas(0)); @@ -36,7 +37,7 @@ function prepareRegsAndMemory( { skipCodeHash = false }: { skipCodeHash?: boolean } = {}, ) { const memStart = 2 ** 16; - const registers = new HostCallRegisters(new Registers()); + const registers = new HostCallRegisters(emptyRegistersBuffer()); registers.set(CODE_HASH_START_REG, tryAsU64(memStart)); registers.set(CODE_LENGTH_REG, tryAsU64(codeLength)); registers.set(GAS_REG, gas); @@ -49,10 +50,10 @@ function prepareRegsAndMemory( if (!skipCodeHash) { builder.setReadablePages(tryAsMemoryIndex(memStart), tryAsMemoryIndex(memStart + PAGE_SIZE), codeHash.raw); } - const memory = builder.finalize(tryAsMemoryIndex(0), tryAsSbrkIndex(0)); + const memory = new HostCallMemory(builder.finalize(tryAsMemoryIndex(0), tryAsSbrkIndex(0))); return { registers, - memory: new HostCallMemory(memory), + memory, }; } diff --git a/packages/jam/jam-host-calls/accumulate/new.ts b/packages/jam/jam-host-calls/accumulate/new.ts index a60d11918..f596db1d4 100644 --- a/packages/jam/jam-host-calls/accumulate/new.ts +++ b/packages/jam/jam-host-calls/accumulate/new.ts @@ -2,9 +2,9 @@ import { type ServiceId, tryAsServiceGas } from "@typeberry/block"; import { Bytes } from "@typeberry/bytes"; import { HASH_SIZE } from "@typeberry/hash"; import { tryAsU64 } from "@typeberry/numbers"; -import type { HostCallHandler, IHostCallMemory, IHostCallRegisters } from "@typeberry/pvm-host-calls"; +import type { HostCallHandler, HostCallMemory, HostCallRegisters } from "@typeberry/pvm-host-calls"; import { PvmExecution, traceRegisters, tryAsHostCallIndex } from "@typeberry/pvm-host-calls"; -import { type GasCounter, tryAsSmallGas } from "@typeberry/pvm-interpreter/gas.js"; +import { type IGasCounter, tryAsSmallGas } from "@typeberry/pvm-interface"; import { assertNever, Compatibility, GpVersion, resultToString } from "@typeberry/utils"; import { NewServiceError, type PartialState } from "../externalities/partial-state.js"; import { logger } from "../logger.js"; @@ -29,11 +29,7 @@ export class New implements HostCallHandler { private readonly partialState: PartialState, ) {} - async execute( - _gas: GasCounter, - regs: IHostCallRegisters, - memory: IHostCallMemory, - ): Promise { + async execute(_gas: IGasCounter, regs: HostCallRegisters, memory: HostCallMemory): Promise { // `o` const codeHashStart = regs.get(IN_OUT_REG); // `l` diff --git a/packages/jam/jam-host-calls/accumulate/provide.test.ts b/packages/jam/jam-host-calls/accumulate/provide.test.ts index b6c387e0d..5633904e4 100644 --- a/packages/jam/jam-host-calls/accumulate/provide.test.ts +++ b/packages/jam/jam-host-calls/accumulate/provide.test.ts @@ -4,19 +4,14 @@ import { type ServiceId, tryAsServiceId } from "@typeberry/block"; import { BytesBlob } from "@typeberry/bytes"; import { tryAsU64 } from "@typeberry/numbers"; import { HostCallMemory, HostCallRegisters, PvmExecution } from "@typeberry/pvm-host-calls"; -import { - gasCounter, - MemoryBuilder, - Registers, - tryAsGas, - tryAsMemoryIndex, - tryAsSbrkIndex, -} from "@typeberry/pvm-interpreter"; +import { tryAsGas } from "@typeberry/pvm-interface"; +import { gasCounter, MemoryBuilder, tryAsMemoryIndex, tryAsSbrkIndex } from "@typeberry/pvm-interpreter"; import { PAGE_SIZE } from "@typeberry/pvm-interpreter/memory/memory-consts.js"; import { deepEqual, Result } from "@typeberry/utils"; import { ProvidePreimageError } from "../externalities/partial-state.js"; import { PartialStateMock } from "../externalities/partial-state-mock.js"; import { HostCallResult } from "../results.js"; +import { emptyRegistersBuffer } from "../utils.js"; import { Provide } from "./provide.js"; const gas = gasCounter(tryAsGas(0)); @@ -30,7 +25,7 @@ function prepareRegsAndMemory( { registerMemory = true }: { registerMemory?: boolean } = {}, ) { const preimageStart = 2 ** 16; - const registers = new HostCallRegisters(new Registers()); + const registers = new HostCallRegisters(emptyRegistersBuffer()); registers.set(RESULT_REG, tryAsU64(service)); registers.set(PREIMAGE_START_REG, tryAsU64(preimageStart)); registers.set(LENGTH_REG, tryAsU64(preimage.length)); @@ -44,10 +39,10 @@ function prepareRegsAndMemory( ); } - const memory = builder.finalize(tryAsMemoryIndex(0), tryAsSbrkIndex(0)); + const memory = new HostCallMemory(builder.finalize(tryAsMemoryIndex(0), tryAsSbrkIndex(0))); return { registers, - memory: new HostCallMemory(memory), + memory, }; } diff --git a/packages/jam/jam-host-calls/accumulate/provide.ts b/packages/jam/jam-host-calls/accumulate/provide.ts index 182f59ba5..0d7937161 100644 --- a/packages/jam/jam-host-calls/accumulate/provide.ts +++ b/packages/jam/jam-host-calls/accumulate/provide.ts @@ -2,13 +2,13 @@ import type { ServiceId } from "@typeberry/block"; import { BytesBlob } from "@typeberry/bytes"; import { type HostCallHandler, - type IHostCallMemory, - type IHostCallRegisters, + type HostCallMemory, + type HostCallRegisters, PvmExecution, traceRegisters, tryAsHostCallIndex, } from "@typeberry/pvm-host-calls"; -import { type GasCounter, tryAsSmallGas } from "@typeberry/pvm-interpreter"; +import { type IGasCounter, tryAsSmallGas } from "@typeberry/pvm-interface"; import { assertNever, resultToString, safeAllocUint8Array } from "@typeberry/utils"; import { type PartialState, ProvidePreimageError } from "../externalities/partial-state.js"; import { logger } from "../logger.js"; @@ -32,7 +32,7 @@ export class Provide implements HostCallHandler { private readonly partialState: PartialState, ) {} - async execute(_gas: GasCounter, regs: IHostCallRegisters, memory: IHostCallMemory) { + async execute(_gas: IGasCounter, regs: HostCallRegisters, memory: HostCallMemory) { // `s` const serviceId = getServiceIdOrCurrent(IN_OUT_REG, regs, this.currentServiceId); diff --git a/packages/jam/jam-host-calls/accumulate/query.test.ts b/packages/jam/jam-host-calls/accumulate/query.test.ts index 153498010..041555823 100644 --- a/packages/jam/jam-host-calls/accumulate/query.test.ts +++ b/packages/jam/jam-host-calls/accumulate/query.test.ts @@ -5,18 +5,13 @@ import { Bytes, type BytesBlob } from "@typeberry/bytes"; import { HASH_SIZE } from "@typeberry/hash"; import { tryAsU32, tryAsU64, type U32 } from "@typeberry/numbers"; import { HostCallMemory, HostCallRegisters, PvmExecution } from "@typeberry/pvm-host-calls"; -import { - gasCounter, - MemoryBuilder, - Registers, - tryAsGas, - tryAsMemoryIndex, - tryAsSbrkIndex, -} from "@typeberry/pvm-interpreter"; +import { tryAsGas } from "@typeberry/pvm-interface"; +import { gasCounter, MemoryBuilder, tryAsMemoryIndex, tryAsSbrkIndex } from "@typeberry/pvm-interpreter"; import { PAGE_SIZE } from "@typeberry/pvm-spi-decoder/memory-conts.js"; import { type PreimageStatus, PreimageStatusKind } from "../externalities/partial-state.js"; import { PartialStateMock } from "../externalities/partial-state-mock.js"; import { HostCallResult } from "../results.js"; +import { emptyRegistersBuffer } from "../utils.js"; import { Query } from "./query.js"; const gas = gasCounter(tryAsGas(0)); @@ -32,7 +27,7 @@ function prepareRegsAndMemory( data: BytesBlob, { registerMemory = true }: { registerMemory?: boolean } = {}, ) { - const registers = new HostCallRegisters(new Registers()); + const registers = new HostCallRegisters(emptyRegistersBuffer()); registers.set(HASH_START_REG, tryAsU64(hashStart)); registers.set(LENGTH_REG, tryAsU64(length)); @@ -41,10 +36,10 @@ function prepareRegsAndMemory( builder.setReadablePages(tryAsMemoryIndex(hashStart), tryAsMemoryIndex(hashStart + PAGE_SIZE), data.raw); } - const memory = builder.finalize(tryAsMemoryIndex(0), tryAsSbrkIndex(0)); + const memory = new HostCallMemory(builder.finalize(tryAsMemoryIndex(0), tryAsSbrkIndex(0))); return { registers, - memory: new HostCallMemory(memory), + memory, }; } diff --git a/packages/jam/jam-host-calls/accumulate/query.ts b/packages/jam/jam-host-calls/accumulate/query.ts index f56e49b36..1dea6476f 100644 --- a/packages/jam/jam-host-calls/accumulate/query.ts +++ b/packages/jam/jam-host-calls/accumulate/query.ts @@ -4,13 +4,13 @@ import { HASH_SIZE } from "@typeberry/hash"; import { tryAsU64 } from "@typeberry/numbers"; import { type HostCallHandler, - type IHostCallMemory, - type IHostCallRegisters, + type HostCallMemory, + type HostCallRegisters, PvmExecution, traceRegisters, tryAsHostCallIndex, } from "@typeberry/pvm-host-calls"; -import { type GasCounter, tryAsSmallGas } from "@typeberry/pvm-interpreter"; +import { type IGasCounter, tryAsSmallGas } from "@typeberry/pvm-interface"; import { type PartialState, PreimageStatusKind } from "../externalities/partial-state.js"; import { logger } from "../logger.js"; import { HostCallResult } from "../results.js"; @@ -34,11 +34,7 @@ export class Query implements HostCallHandler { private readonly partialState: PartialState, ) {} - async execute( - _gas: GasCounter, - regs: IHostCallRegisters, - memory: IHostCallMemory, - ): Promise { + async execute(_gas: IGasCounter, regs: HostCallRegisters, memory: HostCallMemory): Promise { // `o` const hashStart = regs.get(IN_OUT_REG_1); // `z` diff --git a/packages/jam/jam-host-calls/accumulate/solicit.test.ts b/packages/jam/jam-host-calls/accumulate/solicit.test.ts index a88abbd42..e5a997829 100644 --- a/packages/jam/jam-host-calls/accumulate/solicit.test.ts +++ b/packages/jam/jam-host-calls/accumulate/solicit.test.ts @@ -5,8 +5,8 @@ import { Bytes } from "@typeberry/bytes"; import { HASH_SIZE } from "@typeberry/hash"; import { tryAsU64, type U64 } from "@typeberry/numbers"; import { HostCallMemory, HostCallRegisters, PvmExecution } from "@typeberry/pvm-host-calls"; -import { Registers } from "@typeberry/pvm-interpreter"; -import { gasCounter, tryAsGas } from "@typeberry/pvm-interpreter/gas.js"; +import { tryAsGas } from "@typeberry/pvm-interface"; +import { gasCounter } from "@typeberry/pvm-interpreter/gas.js"; import { MemoryBuilder, tryAsMemoryIndex } from "@typeberry/pvm-interpreter/memory/index.js"; import { tryAsSbrkIndex } from "@typeberry/pvm-interpreter/memory/memory-index.js"; import { PAGE_SIZE } from "@typeberry/pvm-spi-decoder/memory-conts.js"; @@ -14,6 +14,7 @@ import { Result } from "@typeberry/utils"; import { RequestPreimageError } from "../externalities/partial-state.js"; import { PartialStateMock } from "../externalities/partial-state-mock.js"; import { HostCallResult } from "../results.js"; +import { emptyRegistersBuffer } from "../utils.js"; import { Solicit } from "./solicit.js"; const gas = gasCounter(tryAsGas(0)); @@ -27,7 +28,7 @@ function prepareRegsAndMemory( { skipPreimageHash = false }: { skipPreimageHash?: boolean } = {}, ) { const memStart = 2 ** 16; - const registers = new HostCallRegisters(new Registers()); + const registers = new HostCallRegisters(emptyRegistersBuffer()); registers.set(HASH_START_REG, tryAsU64(memStart)); registers.set(LENGTH_REG, preimageLength); @@ -36,10 +37,10 @@ function prepareRegsAndMemory( if (!skipPreimageHash) { builder.setReadablePages(tryAsMemoryIndex(memStart), tryAsMemoryIndex(memStart + PAGE_SIZE), preimageHash.raw); } - const memory = builder.finalize(tryAsMemoryIndex(0), tryAsSbrkIndex(0)); + const memory = new HostCallMemory(builder.finalize(tryAsMemoryIndex(0), tryAsSbrkIndex(0))); return { registers, - memory: new HostCallMemory(memory), + memory, }; } diff --git a/packages/jam/jam-host-calls/accumulate/solicit.ts b/packages/jam/jam-host-calls/accumulate/solicit.ts index 4edba67b4..752088550 100644 --- a/packages/jam/jam-host-calls/accumulate/solicit.ts +++ b/packages/jam/jam-host-calls/accumulate/solicit.ts @@ -1,9 +1,9 @@ import type { ServiceId } from "@typeberry/block"; import { Bytes } from "@typeberry/bytes"; import { HASH_SIZE } from "@typeberry/hash"; -import type { HostCallHandler, IHostCallMemory, IHostCallRegisters } from "@typeberry/pvm-host-calls"; +import type { HostCallHandler, HostCallMemory, HostCallRegisters } from "@typeberry/pvm-host-calls"; import { PvmExecution, traceRegisters, tryAsHostCallIndex } from "@typeberry/pvm-host-calls"; -import { type GasCounter, tryAsSmallGas } from "@typeberry/pvm-interpreter/gas.js"; +import { type IGasCounter, tryAsSmallGas } from "@typeberry/pvm-interface"; import { assertNever, resultToString } from "@typeberry/utils"; import { type PartialState, RequestPreimageError } from "../externalities/partial-state.js"; import { logger } from "../logger.js"; @@ -26,11 +26,7 @@ export class Solicit implements HostCallHandler { private readonly partialState: PartialState, ) {} - async execute( - _gas: GasCounter, - regs: IHostCallRegisters, - memory: IHostCallMemory, - ): Promise { + async execute(_gas: IGasCounter, regs: HostCallRegisters, memory: HostCallMemory): Promise { // `o` const hashStart = regs.get(IN_OUT_REG); // `z` diff --git a/packages/jam/jam-host-calls/accumulate/transfer.test.ts b/packages/jam/jam-host-calls/accumulate/transfer.test.ts index b3ffcb99c..78847d0b0 100644 --- a/packages/jam/jam-host-calls/accumulate/transfer.test.ts +++ b/packages/jam/jam-host-calls/accumulate/transfer.test.ts @@ -4,16 +4,17 @@ import { type ServiceId, tryAsServiceId } from "@typeberry/block"; import { Bytes } from "@typeberry/bytes"; import { tryAsU64, type U64 } from "@typeberry/numbers"; import { HostCallMemory, HostCallRegisters, PvmExecution } from "@typeberry/pvm-host-calls"; +import { tryAsGas } from "@typeberry/pvm-interface"; import { MemoryBuilder } from "@typeberry/pvm-interpreter"; -import { gasCounter, tryAsGas } from "@typeberry/pvm-interpreter/gas.js"; +import { gasCounter } from "@typeberry/pvm-interpreter/gas.js"; import { tryAsMemoryIndex } from "@typeberry/pvm-interpreter/memory/index.js"; import { tryAsSbrkIndex } from "@typeberry/pvm-interpreter/memory/memory-index.js"; -import { Registers } from "@typeberry/pvm-interpreter/registers.js"; import { PAGE_SIZE } from "@typeberry/pvm-spi-decoder/memory-conts.js"; import { Compatibility, GpVersion, Result } from "@typeberry/utils"; import { TRANSFER_MEMO_BYTES, TransferError } from "../externalities/partial-state.js"; import { PartialStateMock } from "../externalities/partial-state-mock.js"; import { HostCallResult } from "../results.js"; +import { emptyRegistersBuffer } from "../utils.js"; import { Transfer } from "./transfer.js"; const RESULT_REG = 7; @@ -30,7 +31,7 @@ function prepareRegsAndMemory( { skipMemo = false }: { skipMemo?: boolean } = {}, ) { const memStart = 2 ** 16; - const registers = new HostCallRegisters(new Registers()); + const registers = new HostCallRegisters(emptyRegistersBuffer()); registers.set(DESTINATION_REG, tryAsU64(destination)); registers.set(AMOUNT_REG, amount); registers.set(ON_TRANSFER_GAS_REG, gas); @@ -41,10 +42,10 @@ function prepareRegsAndMemory( builder.setReadablePages(tryAsMemoryIndex(memStart), tryAsMemoryIndex(memStart + PAGE_SIZE), memo.raw); } - const memory = builder.finalize(tryAsMemoryIndex(0), tryAsSbrkIndex(0)); + const memory = new HostCallMemory(builder.finalize(tryAsMemoryIndex(0), tryAsSbrkIndex(0))); return { registers, - memory: new HostCallMemory(memory), + memory, }; } diff --git a/packages/jam/jam-host-calls/accumulate/transfer.ts b/packages/jam/jam-host-calls/accumulate/transfer.ts index 214c52f19..d28518d14 100644 --- a/packages/jam/jam-host-calls/accumulate/transfer.ts +++ b/packages/jam/jam-host-calls/accumulate/transfer.ts @@ -1,8 +1,8 @@ import { type ServiceId, tryAsServiceGas } from "@typeberry/block"; import { Bytes } from "@typeberry/bytes"; -import type { HostCallHandler, IHostCallMemory, IHostCallRegisters } from "@typeberry/pvm-host-calls"; +import type { HostCallHandler, HostCallMemory, HostCallRegisters } from "@typeberry/pvm-host-calls"; import { PvmExecution, traceRegisters, tryAsHostCallIndex } from "@typeberry/pvm-host-calls"; -import { type GasCounter, tryAsGas, tryAsSmallGas } from "@typeberry/pvm-interpreter/gas.js"; +import { type IGasCounter, tryAsGas, tryAsSmallGas } from "@typeberry/pvm-interface"; import { assertNever, Compatibility, GpVersion, resultToString } from "@typeberry/utils"; import { type PartialState, TRANSFER_MEMO_BYTES, TransferError } from "../externalities/partial-state.js"; import { logger } from "../logger.js"; @@ -40,7 +40,7 @@ export class Transfer implements HostCallHandler { */ basicGasCost = Compatibility.isGreaterOrEqual(GpVersion.V0_7_2) ? tryAsSmallGas(10) - : (regs: IHostCallRegisters) => tryAsGas(10n + regs.get(TRANSFER_GAS_FEE_REG)); + : (regs: HostCallRegisters) => tryAsGas(10n + regs.get(TRANSFER_GAS_FEE_REG)); tracedRegisters = traceRegisters(IN_OUT_REG, AMOUNT_REG, TRANSFER_GAS_FEE_REG, MEMO_START_REG); @@ -49,7 +49,7 @@ export class Transfer implements HostCallHandler { private readonly partialState: PartialState, ) {} - async execute(gas: GasCounter, regs: IHostCallRegisters, memory: IHostCallMemory): Promise { + async execute(gas: IGasCounter, regs: HostCallRegisters, memory: HostCallMemory): Promise { // `d`: destination const destination = getServiceId(regs.get(IN_OUT_REG)); // `a`: amount diff --git a/packages/jam/jam-host-calls/accumulate/upgrade.test.ts b/packages/jam/jam-host-calls/accumulate/upgrade.test.ts index 68e4eec53..61a477a5e 100644 --- a/packages/jam/jam-host-calls/accumulate/upgrade.test.ts +++ b/packages/jam/jam-host-calls/accumulate/upgrade.test.ts @@ -5,13 +5,14 @@ import { Bytes } from "@typeberry/bytes"; import { HASH_SIZE } from "@typeberry/hash"; import { tryAsU64, type U64 } from "@typeberry/numbers"; import { HostCallMemory, HostCallRegisters, PvmExecution } from "@typeberry/pvm-host-calls"; -import { Registers } from "@typeberry/pvm-interpreter"; -import { gasCounter, tryAsGas } from "@typeberry/pvm-interpreter/gas.js"; +import { tryAsGas } from "@typeberry/pvm-interface"; +import { gasCounter } from "@typeberry/pvm-interpreter/gas.js"; import { MemoryBuilder, tryAsMemoryIndex } from "@typeberry/pvm-interpreter/memory/index.js"; import { tryAsSbrkIndex } from "@typeberry/pvm-interpreter/memory/memory-index.js"; import { PAGE_SIZE } from "@typeberry/pvm-spi-decoder/memory-conts.js"; import { PartialStateMock } from "../externalities/partial-state-mock.js"; import { HostCallResult } from "../results.js"; +import { emptyRegistersBuffer } from "../utils.js"; import { Upgrade } from "./upgrade.js"; const gas = gasCounter(tryAsGas(0)); @@ -27,7 +28,7 @@ function prepareRegsAndMemory( { skipCodeHash = false }: { skipCodeHash?: boolean } = {}, ) { const memStart = 2 ** 16; - const registers = new HostCallRegisters(new Registers()); + const registers = new HostCallRegisters(emptyRegistersBuffer()); registers.set(CODE_HASH_START_REG, tryAsU64(memStart)); registers.set(GAS_REG, gas); registers.set(BALANCE_REG, balance); @@ -37,10 +38,10 @@ function prepareRegsAndMemory( if (!skipCodeHash) { builder.setReadablePages(tryAsMemoryIndex(memStart), tryAsMemoryIndex(memStart + PAGE_SIZE), codeHash.raw); } - const memory = builder.finalize(tryAsMemoryIndex(0), tryAsSbrkIndex(0)); + const memory = new HostCallMemory(builder.finalize(tryAsMemoryIndex(0), tryAsSbrkIndex(0))); return { registers, - memory: new HostCallMemory(memory), + memory, }; } diff --git a/packages/jam/jam-host-calls/accumulate/upgrade.ts b/packages/jam/jam-host-calls/accumulate/upgrade.ts index c6539d070..3be6c7ff4 100644 --- a/packages/jam/jam-host-calls/accumulate/upgrade.ts +++ b/packages/jam/jam-host-calls/accumulate/upgrade.ts @@ -1,9 +1,9 @@ import type { ServiceId } from "@typeberry/block"; import { Bytes } from "@typeberry/bytes"; import { HASH_SIZE } from "@typeberry/hash"; -import type { HostCallHandler, IHostCallMemory, IHostCallRegisters } from "@typeberry/pvm-host-calls"; +import type { HostCallHandler, HostCallMemory, HostCallRegisters } from "@typeberry/pvm-host-calls"; import { PvmExecution, traceRegisters, tryAsHostCallIndex } from "@typeberry/pvm-host-calls"; -import { type GasCounter, tryAsSmallGas } from "@typeberry/pvm-interpreter/gas.js"; +import { type IGasCounter, tryAsSmallGas } from "@typeberry/pvm-interface"; import type { PartialState } from "../externalities/partial-state.js"; import { logger } from "../logger.js"; import { HostCallResult } from "../results.js"; @@ -27,11 +27,7 @@ export class Upgrade implements HostCallHandler { private readonly partialState: PartialState, ) {} - async execute( - _gas: GasCounter, - regs: IHostCallRegisters, - memory: IHostCallMemory, - ): Promise { + async execute(_gas: IGasCounter, regs: HostCallRegisters, memory: HostCallMemory): Promise { // `o` const codeHashStart = regs.get(IN_OUT_REG); // `g` diff --git a/packages/jam/jam-host-calls/accumulate/yield.test.ts b/packages/jam/jam-host-calls/accumulate/yield.test.ts index b95cc7166..0903d1340 100644 --- a/packages/jam/jam-host-calls/accumulate/yield.test.ts +++ b/packages/jam/jam-host-calls/accumulate/yield.test.ts @@ -5,17 +5,12 @@ import { Bytes, type BytesBlob } from "@typeberry/bytes"; import { HASH_SIZE } from "@typeberry/hash"; import { tryAsU32, tryAsU64, type U32 } from "@typeberry/numbers"; import { HostCallMemory, HostCallRegisters, PvmExecution } from "@typeberry/pvm-host-calls"; -import { - gasCounter, - MemoryBuilder, - Registers, - tryAsGas, - tryAsMemoryIndex, - tryAsSbrkIndex, -} from "@typeberry/pvm-interpreter"; +import { tryAsGas } from "@typeberry/pvm-interface"; +import { gasCounter, MemoryBuilder, tryAsMemoryIndex, tryAsSbrkIndex } from "@typeberry/pvm-interpreter"; import { PAGE_SIZE } from "@typeberry/pvm-spi-decoder/memory-conts.js"; import { PartialStateMock } from "../externalities/partial-state-mock.js"; import { HostCallResult } from "../results.js"; +import { emptyRegistersBuffer } from "../utils.js"; import { Yield } from "./yield.js"; const gas = gasCounter(tryAsGas(0)); @@ -27,7 +22,7 @@ function prepareRegsAndMemory( data: BytesBlob, { registerMemory = true }: { registerMemory?: boolean } = {}, ) { - const registers = new HostCallRegisters(new Registers()); + const registers = new HostCallRegisters(emptyRegistersBuffer()); registers.set(HASH_START_REG, tryAsU64(hashStart)); const builder = new MemoryBuilder(); @@ -35,10 +30,10 @@ function prepareRegsAndMemory( builder.setReadablePages(tryAsMemoryIndex(hashStart), tryAsMemoryIndex(hashStart + PAGE_SIZE), data.raw); } - const memory = builder.finalize(tryAsMemoryIndex(0), tryAsSbrkIndex(0)); + const memory = new HostCallMemory(builder.finalize(tryAsMemoryIndex(0), tryAsSbrkIndex(0))); return { registers, - memory: new HostCallMemory(memory), + memory, }; } diff --git a/packages/jam/jam-host-calls/accumulate/yield.ts b/packages/jam/jam-host-calls/accumulate/yield.ts index 8cb63c764..0c6f8f2bf 100644 --- a/packages/jam/jam-host-calls/accumulate/yield.ts +++ b/packages/jam/jam-host-calls/accumulate/yield.ts @@ -3,13 +3,13 @@ import { Bytes } from "@typeberry/bytes"; import { HASH_SIZE } from "@typeberry/hash"; import { type HostCallHandler, - type IHostCallMemory, - type IHostCallRegisters, + type HostCallMemory, + type HostCallRegisters, PvmExecution, traceRegisters, tryAsHostCallIndex, } from "@typeberry/pvm-host-calls"; -import { type GasCounter, tryAsSmallGas } from "@typeberry/pvm-interpreter"; +import { type IGasCounter, tryAsSmallGas } from "@typeberry/pvm-interface"; import type { PartialState } from "../externalities/partial-state.js"; import { logger } from "../logger.js"; import { HostCallResult } from "../results.js"; @@ -31,11 +31,7 @@ export class Yield implements HostCallHandler { private readonly partialState: PartialState, ) {} - async execute( - _gas: GasCounter, - regs: IHostCallRegisters, - memory: IHostCallMemory, - ): Promise { + async execute(_gas: IGasCounter, regs: HostCallRegisters, memory: HostCallMemory): Promise { // `o` const hashStart = regs.get(IN_OUT_REG); diff --git a/packages/jam/jam-host-calls/externalities/refine-externalities.test.ts b/packages/jam/jam-host-calls/externalities/refine-externalities.test.ts index be4d797f7..58744303a 100644 --- a/packages/jam/jam-host-calls/externalities/refine-externalities.test.ts +++ b/packages/jam/jam-host-calls/externalities/refine-externalities.test.ts @@ -3,13 +3,12 @@ import type { BytesBlob } from "@typeberry/bytes"; import { MultiMap } from "@typeberry/collections"; import type { Blake2bHash } from "@typeberry/hash"; import type { U64 } from "@typeberry/numbers"; -import type { IHostCallMemory } from "@typeberry/pvm-host-calls"; -import type { BigGas, Registers } from "@typeberry/pvm-interpreter"; +import type { HostCallMemory, HostCallRegisters } from "@typeberry/pvm-host-calls"; +import { type BigGas, Status } from "@typeberry/pvm-interface"; import { ProgramDecoder, type ProgramDecoderError, } from "@typeberry/pvm-interpreter/program-decoder/program-decoder.js"; -import { Status } from "@typeberry/pvm-interpreter/status.js"; import { type OK, Result } from "@typeberry/utils"; import { type MachineId, @@ -104,7 +103,7 @@ export class TestRefineExt implements RefineExternalities { destinationStart: U64, sourceStart: U64, length: U64, - destination: IHostCallMemory, + destination: HostCallMemory, ): Promise> { const val = this.machinePeekData.get(machineIndex, destinationStart, sourceStart, length, destination); if (val === undefined) { @@ -120,7 +119,7 @@ export class TestRefineExt implements RefineExternalities { sourceStart: U64, destinationStart: U64, length: U64, - source: IHostCallMemory, + source: HostCallMemory, ): Promise> { const val = this.machinePokeData.get(machineIndex, sourceStart, destinationStart, length, source); if (val === undefined) { @@ -134,7 +133,7 @@ export class TestRefineExt implements RefineExternalities { async machineInvoke( machineIndex: MachineId, gas: BigGas, - registers: Registers, + registers: HostCallRegisters, ): Promise> { const machine = this.machineInvokeData.get(machineIndex); if (machine === undefined) { diff --git a/packages/jam/jam-host-calls/externalities/refine-externalities.ts b/packages/jam/jam-host-calls/externalities/refine-externalities.ts index 9997ed731..c2e62a78b 100644 --- a/packages/jam/jam-host-calls/externalities/refine-externalities.ts +++ b/packages/jam/jam-host-calls/externalities/refine-externalities.ts @@ -2,10 +2,9 @@ import type { Segment, SegmentIndex, ServiceId } from "@typeberry/block"; import type { BytesBlob } from "@typeberry/bytes"; import type { Blake2bHash } from "@typeberry/hash"; import { tryAsU64, type U64 } from "@typeberry/numbers"; -import type { IHostCallMemory } from "@typeberry/pvm-host-calls"; -import type { BigGas, Registers } from "@typeberry/pvm-interpreter"; +import type { HostCallMemory, HostCallRegisters } from "@typeberry/pvm-host-calls"; +import { type BigGas, Status } from "@typeberry/pvm-interface"; import type { ProgramDecoderError } from "@typeberry/pvm-interpreter/program-decoder/program-decoder.js"; -import { Status } from "@typeberry/pvm-interpreter/status.js"; import { asOpaqueType, type OK, type Opaque, type Result } from "@typeberry/utils"; /** @@ -23,7 +22,7 @@ export type MachineId = Opaque; export const tryAsMachineId = (v: number | bigint): MachineId => asOpaqueType(tryAsU64(v)); export class MachineInstance { - async run(gas: BigGas, registers: Registers): Promise { + async run(gas: BigGas, registers: HostCallRegisters): Promise { return { result: { status: Status.OK, @@ -51,7 +50,7 @@ export type MachineStatus = export type MachineResult = { result: MachineStatus; gas: BigGas; - registers: Registers; + registers: HostCallRegisters; }; /** Types of possbile operations to request by Pages host call. */ @@ -123,7 +122,7 @@ export interface RefineExternalities { destinationStart: U64, sourceStart: U64, length: U64, - destination: IHostCallMemory, + destination: HostCallMemory, ): Promise>; /** Write a fragment of memory into `machineIndex` from given source memory. */ @@ -132,7 +131,7 @@ export interface RefineExternalities { sourceStart: U64, destinationStart: U64, length: U64, - source: IHostCallMemory, + source: HostCallMemory, ): Promise>; /** Start an inner PVM instance with given entry point and starting code. */ @@ -142,7 +141,7 @@ export interface RefineExternalities { machineInvoke( machineIndex: MachineId, gas: BigGas, - registers: Registers, + registers: HostCallRegisters, ): Promise>; /** diff --git a/packages/jam/jam-host-calls/fetch.test.ts b/packages/jam/jam-host-calls/fetch.test.ts index 3f27300bb..ee4b465fd 100644 --- a/packages/jam/jam-host-calls/fetch.test.ts +++ b/packages/jam/jam-host-calls/fetch.test.ts @@ -5,18 +5,13 @@ import { tryAsServiceId } from "@typeberry/block"; import { BytesBlob } from "@typeberry/bytes"; import { tryAsU64, type U64 } from "@typeberry/numbers"; import { HostCallMemory, HostCallRegisters, PvmExecution } from "@typeberry/pvm-host-calls"; -import { - gasCounter, - MemoryBuilder, - Registers, - tryAsGas, - tryAsMemoryIndex, - tryAsSbrkIndex, -} from "@typeberry/pvm-interpreter"; +import { tryAsGas } from "@typeberry/pvm-interface"; +import { gasCounter, MemoryBuilder, tryAsMemoryIndex, tryAsSbrkIndex } from "@typeberry/pvm-interpreter"; import { PAGE_SIZE } from "@typeberry/pvm-interpreter/memory/memory-consts.js"; import { Compatibility, GpVersion } from "@typeberry/utils"; import { Fetch, FetchKind, type IFetchExternalities } from "./fetch.js"; import { HostCallResult } from "./results.js"; +import { emptyRegistersBuffer } from "./utils.js"; describe("Fetch", () => { const IN_OUT_REG = 7; @@ -32,7 +27,7 @@ describe("Fetch", () => { const badOffset = tryAsU64(0xfffff); - const registers = new HostCallRegisters(new Registers()); + const registers = new HostCallRegisters(emptyRegistersBuffer()); registers.set(IN_OUT_REG, badOffset); registers.set(8, tryAsU64(0)); registers.set(9, tryAsU64(blob.length)); @@ -490,7 +485,7 @@ describe("Fetch", () => { const memOffset = tryAsU64(pageStart + 1234); const blobLength = tryAsU64(blob.length); - const registers = new HostCallRegisters(new Registers()); + const registers = new HostCallRegisters(emptyRegistersBuffer()); registers.set(IN_OUT_REG, memOffset); registers.set(8, tryAsU64(offset)); registers.set(9, tryAsU64(length)); diff --git a/packages/jam/jam-host-calls/fetch.ts b/packages/jam/jam-host-calls/fetch.ts index 0ffbb46c1..af2023031 100644 --- a/packages/jam/jam-host-calls/fetch.ts +++ b/packages/jam/jam-host-calls/fetch.ts @@ -1,9 +1,9 @@ import type { ServiceId } from "@typeberry/block"; import type { BytesBlob } from "@typeberry/bytes"; import { minU64, tryAsU64, type U32, type U64 } from "@typeberry/numbers"; -import type { HostCallHandler, IHostCallMemory, IHostCallRegisters } from "@typeberry/pvm-host-calls"; +import type { HostCallHandler, HostCallMemory, HostCallRegisters } from "@typeberry/pvm-host-calls"; import { PvmExecution, traceRegisters, tryAsHostCallIndex } from "@typeberry/pvm-host-calls"; -import { type GasCounter, tryAsSmallGas } from "@typeberry/pvm-interpreter/gas.js"; +import { type IGasCounter, tryAsSmallGas } from "@typeberry/pvm-interface"; import { Compatibility, GpVersion } from "@typeberry/utils"; import { logger } from "./logger.js"; import { HostCallResult } from "./results.js"; @@ -253,11 +253,7 @@ export class Fetch implements HostCallHandler { private readonly fetch: IFetchExternalities, ) {} - async execute( - _gas: GasCounter, - regs: IHostCallRegisters, - memory: IHostCallMemory, - ): Promise { + async execute(_gas: IGasCounter, regs: HostCallRegisters, memory: HostCallMemory): Promise { const fetchKindU64 = regs.get(10); const kind = clampU64ToU32(fetchKindU64); const value = this.getValue(kind, regs); @@ -285,7 +281,7 @@ export class Fetch implements HostCallHandler { regs.set(IN_OUT_REG, value === null ? HostCallResult.NONE : valueLength); } - private getValue(kind: U32, regs: IHostCallRegisters): BytesBlob | null { + private getValue(kind: U32, regs: HostCallRegisters): BytesBlob | null { if (kind === FetchKind.Constants) { return this.fetch.constants(); } diff --git a/packages/jam/jam-host-calls/gas.test.ts b/packages/jam/jam-host-calls/gas.test.ts index 4aa408be7..d48133eb4 100644 --- a/packages/jam/jam-host-calls/gas.test.ts +++ b/packages/jam/jam-host-calls/gas.test.ts @@ -2,9 +2,10 @@ import assert from "node:assert"; import { describe, it } from "node:test"; import { tryAsServiceId } from "@typeberry/block"; import { HostCallRegisters } from "@typeberry/pvm-host-calls"; -import { gasCounter, tryAsGas } from "@typeberry/pvm-interpreter/gas.js"; -import { Registers } from "@typeberry/pvm-interpreter/registers.js"; +import { tryAsGas } from "@typeberry/pvm-interface"; +import { gasCounter } from "@typeberry/pvm-interpreter"; import { GasHostCall } from "./gas.js"; +import { emptyRegistersBuffer } from "./utils.js"; const REGISTER = 7; @@ -14,7 +15,7 @@ describe("HostCalls: Gas", () => { const gas = new GasHostCall(currentServiceId); const counter = gasCounter(tryAsGas(10_000)); - const regs = new HostCallRegisters(new Registers()); + const regs = new HostCallRegisters(emptyRegistersBuffer()); assert.deepStrictEqual(regs.get(REGISTER), 0n); @@ -30,7 +31,7 @@ describe("HostCalls: Gas", () => { const gas = new GasHostCall(currentServiceId); const counter = gasCounter(tryAsGas(2n ** 64n - 1n)); - const regs = new HostCallRegisters(new Registers()); + const regs = new HostCallRegisters(emptyRegistersBuffer()); assert.deepStrictEqual(regs.get(REGISTER), 0n); diff --git a/packages/jam/jam-host-calls/gas.ts b/packages/jam/jam-host-calls/gas.ts index e9c8a9826..102401175 100644 --- a/packages/jam/jam-host-calls/gas.ts +++ b/packages/jam/jam-host-calls/gas.ts @@ -1,8 +1,8 @@ import type { ServiceId } from "@typeberry/block"; import { tryAsU64 } from "@typeberry/numbers"; -import type { HostCallHandler, IHostCallRegisters } from "@typeberry/pvm-host-calls"; +import type { HostCallHandler, HostCallRegisters } from "@typeberry/pvm-host-calls"; import { type PvmExecution, traceRegisters, tryAsHostCallIndex } from "@typeberry/pvm-host-calls"; -import { type GasCounter, tryAsSmallGas } from "@typeberry/pvm-interpreter/gas.js"; +import { type IGasCounter, tryAsSmallGas } from "@typeberry/pvm-interface"; import { logger } from "./logger.js"; /** @@ -19,7 +19,7 @@ export class GasHostCall implements HostCallHandler { constructor(public readonly currentServiceId: ServiceId) {} - execute(gas: GasCounter, regs: IHostCallRegisters): Promise { + execute(gas: IGasCounter, regs: HostCallRegisters): Promise { const gasValue = gas.get(); logger.trace`GAS <- ${gasValue}`; regs.set(7, tryAsU64(gasValue)); diff --git a/packages/jam/jam-host-calls/info.test.ts b/packages/jam/jam-host-calls/info.test.ts index 58a05c3cb..2a70b7fec 100644 --- a/packages/jam/jam-host-calls/info.test.ts +++ b/packages/jam/jam-host-calls/info.test.ts @@ -5,8 +5,8 @@ import { Bytes, BytesBlob } from "@typeberry/bytes"; import { Decoder, tryAsExactBytes } from "@typeberry/codec"; import { tryAsU32, tryAsU64 } from "@typeberry/numbers"; import { HostCallMemory, HostCallRegisters, PvmExecution } from "@typeberry/pvm-host-calls"; -import { Registers } from "@typeberry/pvm-interpreter"; -import { gasCounter, tryAsGas } from "@typeberry/pvm-interpreter/gas.js"; +import { tryAsGas } from "@typeberry/pvm-interface"; +import { gasCounter } from "@typeberry/pvm-interpreter"; import { MemoryBuilder, tryAsMemoryIndex } from "@typeberry/pvm-interpreter/memory/index.js"; import { tryAsSbrkIndex } from "@typeberry/pvm-interpreter/memory/memory-index.js"; import { PAGE_SIZE } from "@typeberry/pvm-spi-decoder/memory-conts.js"; @@ -14,6 +14,7 @@ import { ServiceAccountInfo } from "@typeberry/state"; import { TestAccounts } from "./externalities/test-accounts.js"; import { codecServiceAccountInfoWithThresholdBalance, Info, LEN_REG } from "./info.js"; import { HostCallResult } from "./results.js"; +import { emptyRegistersBuffer } from "./utils.js"; const SERVICE_ID_REG = 7; const RESULT_REG = SERVICE_ID_REG; @@ -26,23 +27,23 @@ const serviceAccountInfoSize = tryAsExactBytes(codecServiceAccountInfoWithThresh function prepareRegsAndMemory(serviceId: ServiceId, accountInfoLength = serviceAccountInfoSize) { const pageStart = 2 ** 16; const memStart = pageStart + PAGE_SIZE - accountInfoLength - 1; - const registers = new HostCallRegisters(new Registers()); + const registers = new HostCallRegisters(emptyRegistersBuffer()); registers.set(SERVICE_ID_REG, tryAsU64(serviceId)); registers.set(DEST_START_REG, tryAsU64(memStart)); registers.set(LEN_REG, tryAsU64(serviceAccountInfoSize)); const builder = new MemoryBuilder(); builder.setWriteablePages(tryAsMemoryIndex(pageStart), tryAsMemoryIndex(pageStart + PAGE_SIZE)); - const memory = builder.finalize(tryAsMemoryIndex(0), tryAsSbrkIndex(0)); + const memory = new HostCallMemory(builder.finalize(tryAsMemoryIndex(0), tryAsSbrkIndex(0))); const readRaw = () => { const result = new Uint8Array(Number(registers.get(LEN_REG))); - assert.strictEqual(memory.loadInto(result, tryAsMemoryIndex(memStart)).isOk, true); + assert.strictEqual(memory.loadInto(result, tryAsU64(memStart)).isOk, true); return BytesBlob.blobFrom(result); }; return { registers, - memory: new HostCallMemory(memory), + memory, readRaw, readInfo: () => { const data = readRaw(); diff --git a/packages/jam/jam-host-calls/info.ts b/packages/jam/jam-host-calls/info.ts index e28243ae8..f4745b27d 100644 --- a/packages/jam/jam-host-calls/info.ts +++ b/packages/jam/jam-host-calls/info.ts @@ -3,9 +3,9 @@ import { BytesBlob } from "@typeberry/bytes"; import { codec, Encoder } from "@typeberry/codec"; import { HASH_SIZE } from "@typeberry/hash"; import { minU64, tryAsU64 } from "@typeberry/numbers"; -import type { HostCallHandler, IHostCallMemory, IHostCallRegisters } from "@typeberry/pvm-host-calls"; +import type { HostCallHandler, HostCallMemory, HostCallRegisters } from "@typeberry/pvm-host-calls"; import { PvmExecution, traceRegisters, tryAsHostCallIndex } from "@typeberry/pvm-host-calls"; -import { type GasCounter, tryAsSmallGas } from "@typeberry/pvm-interpreter/gas.js"; +import { type IGasCounter, tryAsSmallGas } from "@typeberry/pvm-interface"; import { ServiceAccountInfo } from "@typeberry/state"; import { Compatibility, GpVersion, TestSuite } from "@typeberry/utils"; import { logger } from "./logger.js"; @@ -53,11 +53,7 @@ export class Info implements HostCallHandler { private readonly account: AccountsInfo, ) {} - async execute( - _gas: GasCounter, - regs: IHostCallRegisters, - memory: IHostCallMemory, - ): Promise { + async execute(_gas: IGasCounter, regs: HostCallRegisters, memory: HostCallMemory): Promise { // t const serviceId = getServiceIdOrCurrent(IN_OUT_REG, regs, this.currentServiceId); // o diff --git a/packages/jam/jam-host-calls/log.ts b/packages/jam/jam-host-calls/log.ts index 231772c6c..1f9e86ddb 100644 --- a/packages/jam/jam-host-calls/log.ts +++ b/packages/jam/jam-host-calls/log.ts @@ -1,7 +1,7 @@ import type { ServiceId } from "@typeberry/block"; -import type { HostCallHandler, IHostCallMemory, IHostCallRegisters } from "@typeberry/pvm-host-calls"; +import type { HostCallHandler, HostCallMemory, HostCallRegisters } from "@typeberry/pvm-host-calls"; import { type PvmExecution, traceRegisters, tryAsHostCallIndex } from "@typeberry/pvm-host-calls"; -import { type GasCounter, tryAsSmallGas } from "@typeberry/pvm-interpreter/gas.js"; +import { type IGasCounter, tryAsSmallGas } from "@typeberry/pvm-interface"; import { safeAllocUint8Array } from "@typeberry/utils"; import { logger } from "./logger.js"; import { clampU64ToU32 } from "./utils.js"; @@ -21,7 +21,7 @@ export class LogHostCall implements HostCallHandler { constructor(public readonly currentServiceId: ServiceId) {} - execute(_gas: GasCounter, regs: IHostCallRegisters, memory: IHostCallMemory): Promise { + execute(_gas: IGasCounter, regs: HostCallRegisters, memory: HostCallMemory): Promise { const lvl = regs.get(7); const targetStart = regs.get(8); const targetLength = regs.get(9); diff --git a/packages/jam/jam-host-calls/lookup.test.ts b/packages/jam/jam-host-calls/lookup.test.ts index bd4825425..9fc17206b 100644 --- a/packages/jam/jam-host-calls/lookup.test.ts +++ b/packages/jam/jam-host-calls/lookup.test.ts @@ -5,14 +5,15 @@ import { Bytes, BytesBlob } from "@typeberry/bytes"; import { Blake2b, type Blake2bHash } from "@typeberry/hash"; import { tryAsU64 } from "@typeberry/numbers"; import { HostCallMemory, HostCallRegisters, PvmExecution } from "@typeberry/pvm-host-calls"; -import { Registers } from "@typeberry/pvm-interpreter"; -import { gasCounter, tryAsGas } from "@typeberry/pvm-interpreter/gas.js"; +import { tryAsGas } from "@typeberry/pvm-interface"; +import { gasCounter } from "@typeberry/pvm-interpreter"; import { MemoryBuilder, tryAsMemoryIndex } from "@typeberry/pvm-interpreter/memory/index.js"; import { tryAsSbrkIndex } from "@typeberry/pvm-interpreter/memory/memory-index.js"; import { PAGE_SIZE } from "@typeberry/pvm-spi-decoder/memory-conts.js"; import { TestAccounts } from "./externalities/test-accounts.js"; import { Lookup } from "./lookup.js"; import { HostCallResult } from "./results.js"; +import { emptyRegistersBuffer } from "./utils.js"; let blake2b: Blake2b; let HASH: Blake2bHash; @@ -45,7 +46,7 @@ function prepareRegsAndMemory( preimageLength = 0, }: { skipKey?: boolean; skipValue?: boolean; preimageOffset?: number; preimageLength?: number } = {}, ) { - const registers = new HostCallRegisters(new Registers()); + const registers = new HostCallRegisters(emptyRegistersBuffer()); registers.set(SERVICE_ID_REG, tryAsU64(serviceId)); registers.set(HASH_ADDRESS_REG, tryAsU64(PREIMAGE_HASH_ADDRESS)); registers.set(DEST_ADDRESS_REG, tryAsU64(DESTINATION_MEM_ADDRESS)); @@ -66,10 +67,10 @@ function prepareRegsAndMemory( tryAsMemoryIndex(DESTINATION_MEM_ADDRESS + PAGE_SIZE), ); } - const memory = builder.finalize(tryAsMemoryIndex(0), tryAsSbrkIndex(0)); + const memory = new HostCallMemory(builder.finalize(tryAsMemoryIndex(0), tryAsSbrkIndex(0))); return { registers, - memory: new HostCallMemory(memory), + memory, }; } diff --git a/packages/jam/jam-host-calls/lookup.ts b/packages/jam/jam-host-calls/lookup.ts index ca56c8486..2c385173c 100644 --- a/packages/jam/jam-host-calls/lookup.ts +++ b/packages/jam/jam-host-calls/lookup.ts @@ -2,9 +2,9 @@ import type { ServiceId } from "@typeberry/block"; import { Bytes, type BytesBlob } from "@typeberry/bytes"; import { type Blake2bHash, HASH_SIZE } from "@typeberry/hash"; import { minU64, tryAsU64 } from "@typeberry/numbers"; -import type { HostCallHandler, IHostCallMemory, IHostCallRegisters } from "@typeberry/pvm-host-calls"; +import type { HostCallHandler, HostCallMemory, HostCallRegisters } from "@typeberry/pvm-host-calls"; import { PvmExecution, traceRegisters, tryAsHostCallIndex } from "@typeberry/pvm-host-calls"; -import { type GasCounter, tryAsSmallGas } from "@typeberry/pvm-interpreter/gas.js"; +import { type IGasCounter, tryAsSmallGas } from "@typeberry/pvm-interface"; import { safeAllocUint8Array } from "@typeberry/utils"; import { logger } from "./logger.js"; import { HostCallResult } from "./results.js"; @@ -34,11 +34,7 @@ export class Lookup implements HostCallHandler { private readonly account: AccountsLookup, ) {} - async execute( - _gas: GasCounter, - regs: IHostCallRegisters, - memory: IHostCallMemory, - ): Promise { + async execute(_gas: IGasCounter, regs: HostCallRegisters, memory: HostCallMemory): Promise { // a const serviceId = getServiceIdOrCurrent(IN_OUT_REG, regs, this.currentServiceId); diff --git a/packages/jam/jam-host-calls/missing.ts b/packages/jam/jam-host-calls/missing.ts index 58862f6ec..8f9177b5d 100644 --- a/packages/jam/jam-host-calls/missing.ts +++ b/packages/jam/jam-host-calls/missing.ts @@ -1,12 +1,12 @@ import { type HostCallHandler, - type IHostCallMemory, - type IHostCallRegisters, + type HostCallMemory, + type HostCallRegisters, type PvmExecution, traceRegisters, tryAsHostCallIndex, } from "@typeberry/pvm-host-calls"; -import { type GasCounter, tryAsSmallGas } from "@typeberry/pvm-interpreter"; +import { type IGasCounter, tryAsSmallGas } from "@typeberry/pvm-interface"; import { HostCallResult } from "./results.js"; import { CURRENT_SERVICE_ID } from "./utils.js"; @@ -16,7 +16,7 @@ export class Missing implements HostCallHandler { currentServiceId = CURRENT_SERVICE_ID; tracedRegisters = traceRegisters(7); - execute(_gas: GasCounter, regs: IHostCallRegisters, _memory: IHostCallMemory): Promise { + execute(_gas: IGasCounter, regs: HostCallRegisters, _memory: HostCallMemory): Promise { regs.set(7, HostCallResult.WHAT); return Promise.resolve(undefined); } diff --git a/packages/jam/jam-host-calls/package.json b/packages/jam/jam-host-calls/package.json index a369cbfcb..1099ec986 100644 --- a/packages/jam/jam-host-calls/package.json +++ b/packages/jam/jam-host-calls/package.json @@ -14,6 +14,7 @@ "@typeberry/logger": "*", "@typeberry/numbers": "*", "@typeberry/pvm-host-calls": "*", + "@typeberry/pvm-interface": "*", "@typeberry/pvm-interpreter": "*", "@typeberry/state": "*", "@typeberry/utils": "*" diff --git a/packages/jam/jam-host-calls/read.test.ts b/packages/jam/jam-host-calls/read.test.ts index 059a5ddab..8ba697b48 100644 --- a/packages/jam/jam-host-calls/read.test.ts +++ b/packages/jam/jam-host-calls/read.test.ts @@ -4,8 +4,8 @@ import { type ServiceId, tryAsServiceId } from "@typeberry/block"; import { BytesBlob } from "@typeberry/bytes"; import { tryAsU64 } from "@typeberry/numbers"; import { HostCallMemory, HostCallRegisters, PvmExecution } from "@typeberry/pvm-host-calls"; -import { Registers } from "@typeberry/pvm-interpreter"; -import { gasCounter, tryAsGas } from "@typeberry/pvm-interpreter/gas.js"; +import { tryAsGas } from "@typeberry/pvm-interface"; +import { gasCounter } from "@typeberry/pvm-interpreter"; import { MemoryBuilder, tryAsMemoryIndex } from "@typeberry/pvm-interpreter/memory/index.js"; import { tryAsSbrkIndex } from "@typeberry/pvm-interpreter/memory/memory-index.js"; import { PAGE_SIZE } from "@typeberry/pvm-spi-decoder/memory-conts.js"; @@ -13,6 +13,7 @@ import { asOpaqueType, OK, Result } from "@typeberry/utils"; import { TestAccounts } from "./externalities/test-accounts.js"; import { Read } from "./read.js"; import { HostCallResult } from "./results.js"; +import { emptyRegistersBuffer } from "./utils.js"; const gas = gasCounter(tryAsGas(0)); const SERVICE_ID_REG = 7; @@ -42,7 +43,7 @@ function prepareRegsAndMemory( ) { const keyAddress = 2 ** 20; const memStart = 2 ** 16; - const registers = new HostCallRegisters(new Registers()); + const registers = new HostCallRegisters(emptyRegistersBuffer()); if (serviceId !== undefined) { registers.set(SERVICE_ID_REG, tryAsU64(serviceId)); } else { @@ -61,13 +62,13 @@ function prepareRegsAndMemory( if (!skipValue) { builder.setWriteablePages(tryAsMemoryIndex(memStart), tryAsMemoryIndex(memStart + PAGE_SIZE)); } - const memory = builder.finalize(tryAsMemoryIndex(0), tryAsSbrkIndex(0)); + const memory = new HostCallMemory(builder.finalize(tryAsMemoryIndex(0), tryAsSbrkIndex(0))); return { registers, - memory: new HostCallMemory(memory), + memory, readResult: () => { const result = new Uint8Array(valueLength - valueOffset); - assert.deepStrictEqual(memory.loadInto(result, tryAsMemoryIndex(memStart)), Result.ok(OK)); + assert.deepStrictEqual(memory.loadInto(result, tryAsU64(memStart)), Result.ok(OK)); return BytesBlob.blobFrom(result); }, }; diff --git a/packages/jam/jam-host-calls/read.ts b/packages/jam/jam-host-calls/read.ts index 98b4ca1f7..8bf624da0 100644 --- a/packages/jam/jam-host-calls/read.ts +++ b/packages/jam/jam-host-calls/read.ts @@ -1,9 +1,9 @@ import type { ServiceId } from "@typeberry/block"; import { BytesBlob } from "@typeberry/bytes"; import { minU64, tryAsU64 } from "@typeberry/numbers"; -import type { HostCallHandler, IHostCallMemory, IHostCallRegisters } from "@typeberry/pvm-host-calls"; +import type { HostCallHandler, HostCallMemory, HostCallRegisters } from "@typeberry/pvm-host-calls"; import { PvmExecution, traceRegisters, tryAsHostCallIndex } from "@typeberry/pvm-host-calls"; -import { type GasCounter, tryAsSmallGas } from "@typeberry/pvm-interpreter/gas.js"; +import { type IGasCounter, tryAsSmallGas } from "@typeberry/pvm-interface"; import { safeAllocUint8Array } from "@typeberry/utils"; import { logger } from "./logger.js"; import { HostCallResult } from "./results.js"; @@ -32,11 +32,7 @@ export class Read implements HostCallHandler { private readonly account: AccountsRead, ) {} - async execute( - _gas: GasCounter, - regs: IHostCallRegisters, - memory: IHostCallMemory, - ): Promise { + async execute(_gas: IGasCounter, regs: HostCallRegisters, memory: HostCallMemory): Promise { // a const serviceId = getServiceIdOrCurrent(IN_OUT_REG, regs, this.currentServiceId); diff --git a/packages/jam/jam-host-calls/refine/export.test.ts b/packages/jam/jam-host-calls/refine/export.test.ts index f960f8bb3..a2325d089 100644 --- a/packages/jam/jam-host-calls/refine/export.test.ts +++ b/packages/jam/jam-host-calls/refine/export.test.ts @@ -4,8 +4,8 @@ import { SEGMENT_BYTES, type Segment, tryAsSegmentIndex, tryAsServiceId } from " import { Bytes } from "@typeberry/bytes"; import { tryAsU64 } from "@typeberry/numbers"; import { HostCallMemory, HostCallRegisters, PvmExecution } from "@typeberry/pvm-host-calls"; -import { Registers } from "@typeberry/pvm-interpreter"; -import { gasCounter, tryAsGas } from "@typeberry/pvm-interpreter/gas.js"; +import { tryAsGas } from "@typeberry/pvm-interface"; +import { gasCounter } from "@typeberry/pvm-interpreter/gas.js"; import { MemoryBuilder, tryAsMemoryIndex } from "@typeberry/pvm-interpreter/memory/index.js"; import { tryAsSbrkIndex } from "@typeberry/pvm-interpreter/memory/memory-index.js"; import { PAGE_SIZE } from "@typeberry/pvm-spi-decoder/memory-conts.js"; @@ -13,6 +13,7 @@ import { Result } from "@typeberry/utils"; import { SegmentExportError } from "../externalities/refine-externalities.js"; import { TestRefineExt } from "../externalities/refine-externalities.test.js"; import { HostCallResult } from "../results.js"; +import { emptyRegistersBuffer } from "../utils.js"; import { Export } from "./export.js"; const gas = gasCounter(tryAsGas(0)); @@ -26,7 +27,7 @@ function prepareRegsAndMemory( { skipSegment = false }: { skipSegment?: boolean } = {}, ) { const memStart = 2 ** 23; - const registers = new HostCallRegisters(new Registers()); + const registers = new HostCallRegisters(emptyRegistersBuffer()); registers.set(SEGMENT_START_REG, tryAsU64(memStart)); registers.set(SEGMENT_LENGTH_REG, tryAsU64(segmentLength)); @@ -34,10 +35,10 @@ function prepareRegsAndMemory( if (!skipSegment) { builder.setReadablePages(tryAsMemoryIndex(memStart), tryAsMemoryIndex(memStart + 2 * PAGE_SIZE), segment.raw); } - const memory = builder.finalize(tryAsMemoryIndex(0), tryAsSbrkIndex(0)); + const memory = new HostCallMemory(builder.finalize(tryAsMemoryIndex(0), tryAsSbrkIndex(0))); return { registers, - memory: new HostCallMemory(memory), + memory, }; } diff --git a/packages/jam/jam-host-calls/refine/export.ts b/packages/jam/jam-host-calls/refine/export.ts index ce2a01454..669db94d3 100644 --- a/packages/jam/jam-host-calls/refine/export.ts +++ b/packages/jam/jam-host-calls/refine/export.ts @@ -3,13 +3,13 @@ import { Bytes } from "@typeberry/bytes"; import { minU64, tryAsU64 } from "@typeberry/numbers"; import { type HostCallHandler, - type IHostCallMemory, - type IHostCallRegisters, + type HostCallMemory, + type HostCallRegisters, PvmExecution, traceRegisters, tryAsHostCallIndex, } from "@typeberry/pvm-host-calls"; -import { type GasCounter, tryAsSmallGas } from "@typeberry/pvm-interpreter"; +import { type IGasCounter, tryAsSmallGas } from "@typeberry/pvm-interface"; import { resultToString } from "@typeberry/utils"; import type { RefineExternalities } from "../externalities/refine-externalities.js"; import { logger } from "../logger.js"; @@ -31,11 +31,7 @@ export class Export implements HostCallHandler { constructor(private readonly refine: RefineExternalities) {} - async execute( - _gas: GasCounter, - regs: IHostCallRegisters, - memory: IHostCallMemory, - ): Promise { + async execute(_gas: IGasCounter, regs: HostCallRegisters, memory: HostCallMemory): Promise { // `p`: segment start address const segmentStart = regs.get(IN_OUT_REG); // `z`: segment bounded length diff --git a/packages/jam/jam-host-calls/refine/expunge.test.ts b/packages/jam/jam-host-calls/refine/expunge.test.ts index 6828b1d2d..a40fe3e3a 100644 --- a/packages/jam/jam-host-calls/refine/expunge.test.ts +++ b/packages/jam/jam-host-calls/refine/expunge.test.ts @@ -2,7 +2,8 @@ import assert from "node:assert"; import { describe, it } from "node:test"; import { tryAsServiceId } from "@typeberry/block"; import { HostCallMemory, HostCallRegisters } from "@typeberry/pvm-host-calls"; -import { gasCounter, MemoryBuilder, Registers, tryAsGas } from "@typeberry/pvm-interpreter"; +import { tryAsGas } from "@typeberry/pvm-interface"; +import { gasCounter, MemoryBuilder } from "@typeberry/pvm-interpreter"; import { tryAsMemoryIndex, tryAsSbrkIndex } from "@typeberry/pvm-interpreter/memory/memory-index.js"; import { Result } from "@typeberry/utils"; import { @@ -14,21 +15,22 @@ import { } from "../externalities/refine-externalities.js"; import { TestRefineExt } from "../externalities/refine-externalities.test.js"; import { HostCallResult } from "../results.js"; +import { emptyRegistersBuffer } from "../utils.js"; import { Expunge } from "./expunge.js"; const gas = gasCounter(tryAsGas(0)); const RESULT_REG = 7; function prepareRegsAndMemory(machineId: MachineId) { - const registers = new HostCallRegisters(new Registers()); + const registers = new HostCallRegisters(emptyRegistersBuffer()); registers.set(7, machineId); const builder = new MemoryBuilder(); - const memory = builder.finalize(tryAsMemoryIndex(0), tryAsSbrkIndex(0)); + const memory = new HostCallMemory(builder.finalize(tryAsMemoryIndex(0), tryAsSbrkIndex(0))); return { registers, - memory: new HostCallMemory(memory), + memory, }; } diff --git a/packages/jam/jam-host-calls/refine/expunge.ts b/packages/jam/jam-host-calls/refine/expunge.ts index be3259c0a..592b532f7 100644 --- a/packages/jam/jam-host-calls/refine/expunge.ts +++ b/packages/jam/jam-host-calls/refine/expunge.ts @@ -1,11 +1,11 @@ import { type HostCallHandler, - type IHostCallRegisters, + type HostCallRegisters, type PvmExecution, traceRegisters, tryAsHostCallIndex, } from "@typeberry/pvm-host-calls"; -import { type GasCounter, tryAsSmallGas } from "@typeberry/pvm-interpreter"; +import { type IGasCounter, tryAsSmallGas } from "@typeberry/pvm-interface"; import { resultToString } from "@typeberry/utils"; import { type RefineExternalities, tryAsMachineId } from "../externalities/refine-externalities.js"; import { logger } from "../logger.js"; @@ -27,7 +27,7 @@ export class Expunge implements HostCallHandler { constructor(private readonly refine: RefineExternalities) {} - async execute(_gas: GasCounter, regs: IHostCallRegisters): Promise { + async execute(_gas: IGasCounter, regs: HostCallRegisters): Promise { // `n`: machine index const machineIndex = tryAsMachineId(regs.get(IN_OUT_REG)); diff --git a/packages/jam/jam-host-calls/refine/historical-lookup.test.ts b/packages/jam/jam-host-calls/refine/historical-lookup.test.ts index 168381502..09339da34 100644 --- a/packages/jam/jam-host-calls/refine/historical-lookup.test.ts +++ b/packages/jam/jam-host-calls/refine/historical-lookup.test.ts @@ -5,13 +5,14 @@ import { Bytes, BytesBlob } from "@typeberry/bytes"; import type { Blake2bHash } from "@typeberry/hash"; import { tryAsU64 } from "@typeberry/numbers"; import { HostCallMemory, HostCallRegisters, PvmExecution } from "@typeberry/pvm-host-calls"; -import { Registers } from "@typeberry/pvm-interpreter"; -import { gasCounter, tryAsGas } from "@typeberry/pvm-interpreter/gas.js"; +import { tryAsGas } from "@typeberry/pvm-interface"; +import { gasCounter } from "@typeberry/pvm-interpreter/gas.js"; import { MemoryBuilder, tryAsMemoryIndex } from "@typeberry/pvm-interpreter/memory/index.js"; import { tryAsSbrkIndex } from "@typeberry/pvm-interpreter/memory/memory-index.js"; import { PAGE_SIZE } from "@typeberry/pvm-spi-decoder/memory-conts.js"; import { TestRefineExt } from "../externalities/refine-externalities.test.js"; import { HostCallResult } from "../results.js"; +import { emptyRegistersBuffer } from "../utils.js"; import { HistoricalLookup } from "./historical-lookup.js"; const gas = gasCounter(tryAsGas(0)); @@ -31,7 +32,7 @@ function prepareRegsAndMemory( ) { const hashAddress = 2 ** 16; const memStart = 2 ** 20; - const registers = new HostCallRegisters(new Registers()); + const registers = new HostCallRegisters(emptyRegistersBuffer()); registers.set(SERVICE_ID_REG, tryAsU64(serviceId)); registers.set(HASH_START_REG, tryAsU64(hashAddress)); registers.set(DEST_START_REG, tryAsU64(memStart)); @@ -45,13 +46,13 @@ function prepareRegsAndMemory( if (writableMemory) { builder.setWriteablePages(tryAsMemoryIndex(memStart), tryAsMemoryIndex(memStart + PAGE_SIZE)); } - const memory = builder.finalize(tryAsMemoryIndex(0), tryAsSbrkIndex(0)); + const memory = new HostCallMemory(builder.finalize(tryAsMemoryIndex(0), tryAsSbrkIndex(0))); return { registers, - memory: new HostCallMemory(memory), + memory, readResult: () => { const result = new Uint8Array(destinationLength); - assert.strictEqual(memory.loadInto(result, tryAsMemoryIndex(memStart)).isOk, true); + assert.strictEqual(memory.loadInto(result, tryAsU64(memStart)).isOk, true); return BytesBlob.blobFrom(result); }, }; diff --git a/packages/jam/jam-host-calls/refine/historical-lookup.ts b/packages/jam/jam-host-calls/refine/historical-lookup.ts index 2d18ada54..165b97c33 100644 --- a/packages/jam/jam-host-calls/refine/historical-lookup.ts +++ b/packages/jam/jam-host-calls/refine/historical-lookup.ts @@ -3,13 +3,13 @@ import { HASH_SIZE } from "@typeberry/hash"; import { minU64, tryAsU64 } from "@typeberry/numbers"; import { type HostCallHandler, - type IHostCallMemory, - type IHostCallRegisters, + type HostCallMemory, + type HostCallRegisters, PvmExecution, traceRegisters, tryAsHostCallIndex, } from "@typeberry/pvm-host-calls"; -import { type GasCounter, tryAsSmallGas } from "@typeberry/pvm-interpreter"; +import { type IGasCounter, tryAsSmallGas } from "@typeberry/pvm-interface"; import type { RefineExternalities } from "../externalities/refine-externalities.js"; import { logger } from "../logger.js"; import { HostCallResult } from "../results.js"; @@ -30,11 +30,7 @@ export class HistoricalLookup implements HostCallHandler { constructor(private readonly refine: RefineExternalities) {} - async execute( - _gas: GasCounter, - regs: IHostCallRegisters, - memory: IHostCallMemory, - ): Promise { + async execute(_gas: IGasCounter, regs: HostCallRegisters, memory: HostCallMemory): Promise { // a const serviceId = getServiceIdOrCurrent(IN_OUT_REG, regs, this.currentServiceId); // h diff --git a/packages/jam/jam-host-calls/refine/invoke.test.ts b/packages/jam/jam-host-calls/refine/invoke.test.ts index d87ada5a6..8b600387d 100644 --- a/packages/jam/jam-host-calls/refine/invoke.test.ts +++ b/packages/jam/jam-host-calls/refine/invoke.test.ts @@ -4,10 +4,10 @@ import { tryAsServiceId } from "@typeberry/block"; import { Bytes, type BytesBlob } from "@typeberry/bytes"; import { tryAsU64, type U64 } from "@typeberry/numbers"; import { HostCallMemory, HostCallRegisters, PvmExecution } from "@typeberry/pvm-host-calls"; -import { gasCounter, MemoryBuilder, Registers, tryAsGas, tryAsMemoryIndex } from "@typeberry/pvm-interpreter"; +import { Status, tryAsGas } from "@typeberry/pvm-interface"; +import { gasCounter, MemoryBuilder } from "@typeberry/pvm-interpreter"; import { RESERVED_NUMBER_OF_PAGES } from "@typeberry/pvm-interpreter/memory/memory-consts.js"; -import { tryAsSbrkIndex } from "@typeberry/pvm-interpreter/memory/memory-index.js"; -import { Status } from "@typeberry/pvm-interpreter/status.js"; +import { tryAsMemoryIndex, tryAsSbrkIndex } from "@typeberry/pvm-interpreter/memory/memory-index.js"; import { PAGE_SIZE } from "@typeberry/pvm-spi-decoder/memory-conts.js"; import { type MachineId, @@ -17,6 +17,7 @@ import { } from "../externalities/refine-externalities.js"; import { TestRefineExt } from "../externalities/refine-externalities.test.js"; import { HostCallResult } from "../results.js"; +import { emptyRegistersBuffer } from "../utils.js"; import { Invoke } from "./invoke.js"; const gas = gasCounter(tryAsGas(0)); @@ -33,15 +34,15 @@ function prepareRegsAndMemory( data: BytesBlob, { registerMemory = true }: { registerMemory?: boolean } = {}, ) { - const registers = new HostCallRegisters(new Registers()); + const registers = new HostCallRegisters(emptyRegistersBuffer()); registers.set(MACHINE_INDEX_REG, machineIndex); registers.set(DEST_REG, tryAsU64(destinationStart)); - const memory = prepareMemory(data, destinationStart, PAGE_SIZE, { registerMemory }); + const memory = new HostCallMemory(prepareMemory(data, destinationStart, PAGE_SIZE, { registerMemory })); return { registers, - memory: new HostCallMemory(memory), + memory, }; } diff --git a/packages/jam/jam-host-calls/refine/invoke.ts b/packages/jam/jam-host-calls/refine/invoke.ts index db44612df..a7be054cc 100644 --- a/packages/jam/jam-host-calls/refine/invoke.ts +++ b/packages/jam/jam-host-calls/refine/invoke.ts @@ -3,15 +3,20 @@ import { codec, Decoder, Encoder, tryAsExactBytes } from "@typeberry/codec"; import { tryAsU64 } from "@typeberry/numbers"; import { type HostCallHandler, - type IHostCallMemory, - type IHostCallRegisters, + type HostCallMemory, + HostCallRegisters, PvmExecution, traceRegisters, tryAsHostCallIndex, } from "@typeberry/pvm-host-calls"; -import { type GasCounter, Registers, tryAsBigGas, tryAsSmallGas } from "@typeberry/pvm-interpreter"; -import { NO_OF_REGISTERS } from "@typeberry/pvm-interpreter/registers.js"; -import { Status } from "@typeberry/pvm-interpreter/status.js"; +import { + type IGasCounter, + NO_OF_REGISTERS, + REGISTER_BYTE_SIZE, + Status, + tryAsBigGas, + tryAsSmallGas, +} from "@typeberry/pvm-interface"; import { check, resultToString } from "@typeberry/utils"; import { type RefineExternalities, tryAsMachineId } from "../externalities/refine-externalities.js"; import { logger } from "../logger.js"; @@ -22,7 +27,7 @@ const IN_OUT_REG_1 = 7; const IN_OUT_REG_2 = 8; const gasAndRegistersCodec = codec.object({ gas: codec.i64, - registers: codec.bytes(NO_OF_REGISTERS * 8), + registers: codec.bytes(NO_OF_REGISTERS * REGISTER_BYTE_SIZE), }); const GAS_REGISTERS_SIZE = tryAsExactBytes(gasAndRegistersCodec.sizeHint); @@ -39,11 +44,7 @@ export class Invoke implements HostCallHandler { constructor(private readonly refine: RefineExternalities) {} - async execute( - _gas: GasCounter, - regs: IHostCallRegisters, - memory: IHostCallMemory, - ): Promise { + async execute(_gas: IGasCounter, regs: HostCallRegisters, memory: HostCallMemory): Promise { // `n`: machine index const machineIndex = tryAsMachineId(regs.get(IN_OUT_REG_1)); // `o`: destination memory start (local) @@ -70,7 +71,7 @@ export class Invoke implements HostCallHandler { // `g` const gasCost = tryAsBigGas(gasRegisters.gas); // `w` - const registers = Registers.fromBytes(gasRegisters.registers.raw); + const registers = new HostCallRegisters(gasRegisters.registers.raw); // try run the machine const state = await this.refine.machineInvoke(machineIndex, gasCost, registers); @@ -86,7 +87,7 @@ export class Invoke implements HostCallHandler { // save the result to the destination memory const resultData = Encoder.encodeObject(gasAndRegistersCodec, { gas: machineState.gas, - registers: Bytes.fromBlob(machineState.registers.getAllBytesAsLittleEndian(), NO_OF_REGISTERS * 8), + registers: Bytes.fromBlob(machineState.registers.getEncoded(), NO_OF_REGISTERS * REGISTER_BYTE_SIZE), }); // this fault does not need to be handled, because we've ensured it's diff --git a/packages/jam/jam-host-calls/refine/machine.test.ts b/packages/jam/jam-host-calls/refine/machine.test.ts index 2f81e2b23..ac342f23e 100644 --- a/packages/jam/jam-host-calls/refine/machine.test.ts +++ b/packages/jam/jam-host-calls/refine/machine.test.ts @@ -4,12 +4,14 @@ import { tryAsServiceId } from "@typeberry/block"; import { BytesBlob } from "@typeberry/bytes"; import { tryAsU64 } from "@typeberry/numbers"; import { HostCallMemory, HostCallRegisters, PvmExecution } from "@typeberry/pvm-host-calls"; -import { gasCounter, MemoryBuilder, Registers, tryAsGas, tryAsMemoryIndex } from "@typeberry/pvm-interpreter"; +import { tryAsGas } from "@typeberry/pvm-interface"; +import { gasCounter, MemoryBuilder, tryAsMemoryIndex } from "@typeberry/pvm-interpreter"; import { tryAsSbrkIndex } from "@typeberry/pvm-interpreter/memory/memory-index.js"; import { PAGE_SIZE } from "@typeberry/pvm-spi-decoder/memory-conts.js"; import { type ProgramCounter, tryAsMachineId, tryAsProgramCounter } from "../externalities/refine-externalities.js"; import { TestRefineExt } from "../externalities/refine-externalities.test.js"; import { HostCallResult } from "../results.js"; +import { emptyRegistersBuffer } from "../utils.js"; import { Machine } from "./machine.js"; const gas = gasCounter(tryAsGas(0)); @@ -20,7 +22,7 @@ const PC_REG = 9; function prepareRegsAndMemory(code: BytesBlob, pc: ProgramCounter, { skipCode = false }: { skipCode?: boolean } = {}) { const memStart = 2 ** 20; - const registers = new HostCallRegisters(new Registers()); + const registers = new HostCallRegisters(emptyRegistersBuffer()); registers.set(CODE_START_REG, tryAsU64(memStart)); registers.set(CODE_LEN_REG, tryAsU64(code.length)); registers.set(PC_REG, pc); @@ -29,10 +31,10 @@ function prepareRegsAndMemory(code: BytesBlob, pc: ProgramCounter, { skipCode = if (!skipCode) { builder.setReadablePages(tryAsMemoryIndex(memStart), tryAsMemoryIndex(memStart + PAGE_SIZE), code.raw); } - const memory = builder.finalize(tryAsMemoryIndex(0), tryAsSbrkIndex(0)); + const memory = new HostCallMemory(builder.finalize(tryAsMemoryIndex(0), tryAsSbrkIndex(0))); return { registers, - memory: new HostCallMemory(memory), + memory, }; } diff --git a/packages/jam/jam-host-calls/refine/machine.ts b/packages/jam/jam-host-calls/refine/machine.ts index 6d3740274..b2f9b3d67 100644 --- a/packages/jam/jam-host-calls/refine/machine.ts +++ b/packages/jam/jam-host-calls/refine/machine.ts @@ -1,13 +1,13 @@ import { BytesBlob } from "@typeberry/bytes"; import { type HostCallHandler, - type IHostCallMemory, - type IHostCallRegisters, + type HostCallMemory, + type HostCallRegisters, PvmExecution, traceRegisters, tryAsHostCallIndex, } from "@typeberry/pvm-host-calls"; -import { type GasCounter, tryAsSmallGas } from "@typeberry/pvm-interpreter"; +import { type IGasCounter, tryAsSmallGas } from "@typeberry/pvm-interface"; import { resultToString, safeAllocUint8Array } from "@typeberry/utils"; import { type RefineExternalities, tryAsProgramCounter } from "../externalities/refine-externalities.js"; import { logger } from "../logger.js"; @@ -29,11 +29,7 @@ export class Machine implements HostCallHandler { constructor(private readonly refine: RefineExternalities) {} - async execute( - _gas: GasCounter, - regs: IHostCallRegisters, - memory: IHostCallMemory, - ): Promise { + async execute(_gas: IGasCounter, regs: HostCallRegisters, memory: HostCallMemory): Promise { // `p_o`: memory index where there program code starts const codeStart = regs.get(IN_OUT_REG); // `p_z`: length of the program code diff --git a/packages/jam/jam-host-calls/refine/pages.test.ts b/packages/jam/jam-host-calls/refine/pages.test.ts index df5908a8c..c0263bd94 100644 --- a/packages/jam/jam-host-calls/refine/pages.test.ts +++ b/packages/jam/jam-host-calls/refine/pages.test.ts @@ -2,15 +2,9 @@ import assert from "node:assert"; import { describe, it } from "node:test"; import { tryAsServiceId } from "@typeberry/block"; import { tryAsU64, type U64 } from "@typeberry/numbers"; -import { HostCallRegisters } from "@typeberry/pvm-host-calls"; -import { - gasCounter, - MemoryBuilder, - Registers, - tryAsGas, - tryAsMemoryIndex, - tryAsSbrkIndex, -} from "@typeberry/pvm-interpreter"; +import { HostCallMemory, HostCallRegisters } from "@typeberry/pvm-host-calls"; +import { tryAsGas } from "@typeberry/pvm-interface"; +import { gasCounter, MemoryBuilder, tryAsMemoryIndex, tryAsSbrkIndex } from "@typeberry/pvm-interpreter"; import { OK, Result } from "@typeberry/utils"; import { type MachineId, @@ -20,20 +14,21 @@ import { } from "../externalities/refine-externalities.js"; import { TestRefineExt } from "../externalities/refine-externalities.test.js"; import { HostCallResult } from "../results.js"; +import { emptyRegistersBuffer } from "../utils.js"; import { Pages } from "./pages.js"; const gas = gasCounter(tryAsGas(0)); const RESULT_REG = 7; function prepareRegsAndMemory(machineId: MachineId, pageStart: U64, pageCount: U64, requestType: U64) { - const registers = new HostCallRegisters(new Registers()); + const registers = new HostCallRegisters(emptyRegistersBuffer()); registers.set(7, machineId); registers.set(8, pageStart); registers.set(9, pageCount); registers.set(10, requestType); const builder = new MemoryBuilder(); - const memory = builder.finalize(tryAsMemoryIndex(0), tryAsSbrkIndex(0)); + const memory = new HostCallMemory(builder.finalize(tryAsMemoryIndex(0), tryAsSbrkIndex(0))); return { registers, diff --git a/packages/jam/jam-host-calls/refine/pages.ts b/packages/jam/jam-host-calls/refine/pages.ts index 82d70e7ff..6163741bb 100644 --- a/packages/jam/jam-host-calls/refine/pages.ts +++ b/packages/jam/jam-host-calls/refine/pages.ts @@ -1,11 +1,11 @@ import { type HostCallHandler, - type IHostCallRegisters, + type HostCallRegisters, type PvmExecution, traceRegisters, tryAsHostCallIndex, } from "@typeberry/pvm-host-calls"; -import { type GasCounter, tryAsSmallGas } from "@typeberry/pvm-interpreter"; +import { type IGasCounter, tryAsSmallGas } from "@typeberry/pvm-interface"; import { assertNever, resultToString } from "@typeberry/utils"; import { PagesError, @@ -33,7 +33,7 @@ export class Pages implements HostCallHandler { constructor(private readonly refine: RefineExternalities) {} - async execute(_gas: GasCounter, regs: IHostCallRegisters): Promise { + async execute(_gas: IGasCounter, regs: HostCallRegisters): Promise { // `n`: machine index const machineIndex = tryAsMachineId(regs.get(IN_OUT_REG)); // `p`: start page diff --git a/packages/jam/jam-host-calls/refine/peek.test.ts b/packages/jam/jam-host-calls/refine/peek.test.ts index 093b649b3..0e014d585 100644 --- a/packages/jam/jam-host-calls/refine/peek.test.ts +++ b/packages/jam/jam-host-calls/refine/peek.test.ts @@ -3,36 +3,31 @@ import { describe, it } from "node:test"; import { tryAsServiceId } from "@typeberry/block"; import { tryAsU64 } from "@typeberry/numbers"; import { HostCallMemory, HostCallRegisters, PvmExecution } from "@typeberry/pvm-host-calls"; -import { - gasCounter, - MemoryBuilder, - Registers, - tryAsGas, - tryAsMemoryIndex, - tryAsSbrkIndex, -} from "@typeberry/pvm-interpreter"; +import { tryAsGas } from "@typeberry/pvm-interface"; +import { gasCounter, MemoryBuilder, tryAsMemoryIndex, tryAsSbrkIndex } from "@typeberry/pvm-interpreter"; import { OK, Result } from "@typeberry/utils"; import { type MachineId, PeekPokeError, tryAsMachineId } from "../externalities/refine-externalities.js"; import { TestRefineExt } from "../externalities/refine-externalities.test.js"; import { HostCallResult } from "../results.js"; +import { emptyRegistersBuffer } from "../utils.js"; import { Peek } from "./peek.js"; const gas = gasCounter(tryAsGas(0)); const RESULT_REG = 7; function prepareRegsAndMemory(machineId: MachineId, destinationStart: number, sourceStart: number, length: number) { - const registers = new HostCallRegisters(new Registers()); + const registers = new HostCallRegisters(emptyRegistersBuffer()); registers.set(7, machineId); registers.set(8, tryAsU64(destinationStart)); registers.set(9, tryAsU64(sourceStart)); registers.set(10, tryAsU64(length)); const builder = new MemoryBuilder(); - const memory = builder.finalize(tryAsMemoryIndex(0), tryAsSbrkIndex(0)); + const memory = new HostCallMemory(builder.finalize(tryAsMemoryIndex(0), tryAsSbrkIndex(0))); return { registers, - memory: new HostCallMemory(memory), + memory, }; } diff --git a/packages/jam/jam-host-calls/refine/peek.ts b/packages/jam/jam-host-calls/refine/peek.ts index 193339ff5..ed12a9d21 100644 --- a/packages/jam/jam-host-calls/refine/peek.ts +++ b/packages/jam/jam-host-calls/refine/peek.ts @@ -1,12 +1,12 @@ import { type HostCallHandler, - type IHostCallMemory, - type IHostCallRegisters, + type HostCallMemory, + type HostCallRegisters, PvmExecution, traceRegisters, tryAsHostCallIndex, } from "@typeberry/pvm-host-calls"; -import { type GasCounter, tryAsSmallGas } from "@typeberry/pvm-interpreter"; +import { type IGasCounter, tryAsSmallGas } from "@typeberry/pvm-interface"; import { assertNever, resultToString } from "@typeberry/utils"; import { PeekPokeError, type RefineExternalities, tryAsMachineId } from "../externalities/refine-externalities.js"; import { logger } from "../logger.js"; @@ -28,11 +28,7 @@ export class Peek implements HostCallHandler { constructor(private readonly refine: RefineExternalities) {} - async execute( - _gas: GasCounter, - regs: IHostCallRegisters, - memory: IHostCallMemory, - ): Promise { + async execute(_gas: IGasCounter, regs: HostCallRegisters, memory: HostCallMemory): Promise { // `n`: machine index const machineIndex = tryAsMachineId(regs.get(IN_OUT_REG)); // `o`: destination memory start (local) diff --git a/packages/jam/jam-host-calls/refine/poke.test.ts b/packages/jam/jam-host-calls/refine/poke.test.ts index 44056dda3..bf6548e02 100644 --- a/packages/jam/jam-host-calls/refine/poke.test.ts +++ b/packages/jam/jam-host-calls/refine/poke.test.ts @@ -3,36 +3,31 @@ import { describe, it } from "node:test"; import { tryAsServiceId } from "@typeberry/block"; import { tryAsU64 } from "@typeberry/numbers"; import { HostCallMemory, HostCallRegisters, PvmExecution } from "@typeberry/pvm-host-calls"; -import { - gasCounter, - MemoryBuilder, - Registers, - tryAsGas, - tryAsMemoryIndex, - tryAsSbrkIndex, -} from "@typeberry/pvm-interpreter"; +import { tryAsGas } from "@typeberry/pvm-interface"; +import { gasCounter, MemoryBuilder, tryAsMemoryIndex, tryAsSbrkIndex } from "@typeberry/pvm-interpreter"; import { OK, Result } from "@typeberry/utils"; import { type MachineId, PeekPokeError, tryAsMachineId } from "../externalities/refine-externalities.js"; import { TestRefineExt } from "../externalities/refine-externalities.test.js"; import { HostCallResult } from "../results.js"; +import { emptyRegistersBuffer } from "../utils.js"; import { Poke } from "./poke.js"; const gas = gasCounter(tryAsGas(0)); const RESULT_REG = 7; function prepareRegsAndMemory(machineId: MachineId, sourceStart: number, destinationStart: number, length: number) { - const registers = new HostCallRegisters(new Registers()); + const registers = new HostCallRegisters(emptyRegistersBuffer()); registers.set(7, machineId); registers.set(8, tryAsU64(sourceStart)); registers.set(9, tryAsU64(destinationStart)); registers.set(10, tryAsU64(length)); const builder = new MemoryBuilder(); - const memory = builder.finalize(tryAsMemoryIndex(0), tryAsSbrkIndex(0)); + const memory = new HostCallMemory(builder.finalize(tryAsMemoryIndex(0), tryAsSbrkIndex(0))); return { registers, - memory: new HostCallMemory(memory), + memory, }; } diff --git a/packages/jam/jam-host-calls/refine/poke.ts b/packages/jam/jam-host-calls/refine/poke.ts index 9fe5a21e9..0dfaf2d5b 100644 --- a/packages/jam/jam-host-calls/refine/poke.ts +++ b/packages/jam/jam-host-calls/refine/poke.ts @@ -1,12 +1,12 @@ import { type HostCallHandler, - type IHostCallMemory, - type IHostCallRegisters, + type HostCallMemory, + type HostCallRegisters, PvmExecution, traceRegisters, tryAsHostCallIndex, } from "@typeberry/pvm-host-calls"; -import { type GasCounter, tryAsSmallGas } from "@typeberry/pvm-interpreter"; +import { type IGasCounter, tryAsSmallGas } from "@typeberry/pvm-interface"; import { assertNever, resultToString } from "@typeberry/utils"; import { PeekPokeError, type RefineExternalities, tryAsMachineId } from "../externalities/refine-externalities.js"; import { logger } from "../logger.js"; @@ -28,11 +28,7 @@ export class Poke implements HostCallHandler { constructor(private readonly refine: RefineExternalities) {} - async execute( - _gas: GasCounter, - regs: IHostCallRegisters, - memory: IHostCallMemory, - ): Promise { + async execute(_gas: IGasCounter, regs: HostCallRegisters, memory: HostCallMemory): Promise { // `n`: machine index const machineIndex = tryAsMachineId(regs.get(IN_OUT_REG)); // `s`: source memory start (nested vm) diff --git a/packages/jam/jam-host-calls/utils.ts b/packages/jam/jam-host-calls/utils.ts index e35108b6c..e964f6970 100644 --- a/packages/jam/jam-host-calls/utils.ts +++ b/packages/jam/jam-host-calls/utils.ts @@ -1,7 +1,8 @@ import { type ServiceId, tryAsServiceId } from "@typeberry/block"; import { tryAsU32, tryAsU64, type U32, type U64, u32AsLeBytes, u64IntoParts } from "@typeberry/numbers"; -import type { IHostCallRegisters } from "@typeberry/pvm-host-calls"; -import { check } from "@typeberry/utils"; +import type { HostCallRegisters } from "@typeberry/pvm-host-calls"; +import { NO_OF_REGISTERS, REGISTER_BYTE_SIZE } from "@typeberry/pvm-interface"; +import { check, safeAllocUint8Array } from "@typeberry/utils"; const MAX_U32 = tryAsU32(2 ** 32 - 1); const MAX_U32_BIG_INT = tryAsU64(MAX_U32); @@ -10,7 +11,7 @@ export const CURRENT_SERVICE_ID = tryAsServiceId(2 ** 32 - 1); export function getServiceIdOrCurrent( regNumber: number, - regs: IHostCallRegisters, + regs: HostCallRegisters, currentServiceId: ServiceId, ): ServiceId | null { const regValue = regs.get(regNumber); @@ -40,3 +41,7 @@ export function writeServiceIdAsLeBytes(serviceId: ServiceId, destination: Uint8 export function clampU64ToU32(value: U64): U32 { return value > MAX_U32_BIG_INT ? MAX_U32 : tryAsU32(Number(value)); } + +export function emptyRegistersBuffer(): Uint8Array { + return safeAllocUint8Array(NO_OF_REGISTERS * REGISTER_BYTE_SIZE); +} diff --git a/packages/jam/jam-host-calls/write.test.ts b/packages/jam/jam-host-calls/write.test.ts index 16575cc35..43280382d 100644 --- a/packages/jam/jam-host-calls/write.test.ts +++ b/packages/jam/jam-host-calls/write.test.ts @@ -4,8 +4,8 @@ import { type ServiceId, tryAsServiceGas, tryAsServiceId, tryAsTimeSlot } from " import { Bytes, BytesBlob } from "@typeberry/bytes"; import { tryAsU32, tryAsU64 } from "@typeberry/numbers"; import { HostCallMemory, HostCallRegisters, PvmExecution } from "@typeberry/pvm-host-calls"; -import { Registers } from "@typeberry/pvm-interpreter"; -import { gasCounter, tryAsGas } from "@typeberry/pvm-interpreter/gas.js"; +import { tryAsGas } from "@typeberry/pvm-interface"; +import { gasCounter } from "@typeberry/pvm-interpreter"; import { MemoryBuilder, tryAsMemoryIndex } from "@typeberry/pvm-interpreter/memory/index.js"; import { tryAsSbrkIndex } from "@typeberry/pvm-interpreter/memory/memory-index.js"; import { PAGE_SIZE } from "@typeberry/pvm-spi-decoder/memory-conts.js"; @@ -13,6 +13,7 @@ import { ServiceAccountInfo } from "@typeberry/state"; import { asOpaqueType } from "@typeberry/utils"; import { TestAccounts } from "./externalities/test-accounts.js"; import { HostCallResult } from "./results.js"; +import { emptyRegistersBuffer } from "./utils.js"; import { Write } from "./write.js"; const gas = gasCounter(tryAsGas(0)); @@ -52,7 +53,7 @@ function prepareRegsAndMemory( ) { const keyAddress = 2 ** 18; const memStart = 2 ** 16; - const registers = new HostCallRegisters(new Registers()); + const registers = new HostCallRegisters(emptyRegistersBuffer()); registers.set(KEY_START_REG, tryAsU64(keyAddress)); registers.set(KEY_LEN_REG, tryAsU64(key.length)); registers.set(DEST_START_REG, tryAsU64(memStart)); @@ -65,10 +66,10 @@ function prepareRegsAndMemory( if (!skipValue && dataInMemory.length > 0) { builder.setReadablePages(tryAsMemoryIndex(memStart), tryAsMemoryIndex(memStart + PAGE_SIZE), dataInMemory.raw); } - const memory = builder.finalize(tryAsMemoryIndex(0), tryAsSbrkIndex(0)); + const memory = new HostCallMemory(builder.finalize(tryAsMemoryIndex(0), tryAsSbrkIndex(0))); return { registers, - memory: new HostCallMemory(memory), + memory, }; } diff --git a/packages/jam/jam-host-calls/write.ts b/packages/jam/jam-host-calls/write.ts index 2df4cf52c..e8c475893 100644 --- a/packages/jam/jam-host-calls/write.ts +++ b/packages/jam/jam-host-calls/write.ts @@ -1,9 +1,9 @@ import type { ServiceId } from "@typeberry/block"; import { BytesBlob } from "@typeberry/bytes"; import { tryAsU64 } from "@typeberry/numbers"; -import type { HostCallHandler, IHostCallMemory, IHostCallRegisters } from "@typeberry/pvm-host-calls"; +import type { HostCallHandler, HostCallMemory, HostCallRegisters } from "@typeberry/pvm-host-calls"; import { PvmExecution, traceRegisters, tryAsHostCallIndex } from "@typeberry/pvm-host-calls"; -import { type GasCounter, tryAsSmallGas } from "@typeberry/pvm-interpreter/gas.js"; +import { type IGasCounter, tryAsSmallGas } from "@typeberry/pvm-interface"; import type { StorageKey } from "@typeberry/state"; import { asOpaqueType, type Result, resultToString, safeAllocUint8Array } from "@typeberry/utils"; import { logger } from "./logger.js"; @@ -41,11 +41,7 @@ export class Write implements HostCallHandler { private readonly account: AccountsWrite, ) {} - async execute( - _gas: GasCounter, - regs: IHostCallRegisters, - memory: IHostCallMemory, - ): Promise { + async execute(_gas: IGasCounter, regs: HostCallRegisters, memory: HostCallMemory): Promise { // k_0 const storageKeyStartAddress = regs.get(IN_OUT_REG); // k_z diff --git a/packages/jam/node/jam-config.ts b/packages/jam/node/jam-config.ts index 524ca9e07..836f014d0 100644 --- a/packages/jam/node/jam-config.ts +++ b/packages/jam/node/jam-config.ts @@ -5,7 +5,7 @@ import { tryAsValidatorIndex, type ValidatorIndex, } from "@typeberry/block"; -import type { Bootnode } from "@typeberry/config"; +import type { Bootnode, PvmBackend } from "@typeberry/config"; import type { NodeConfiguration } from "@typeberry/config-node"; import type { Ed25519SecretSeed, KeySeed } from "@typeberry/crypto/key-derivation.js"; @@ -23,6 +23,7 @@ export class JamConfig { isAuthoring, nodeName, nodeConfig, + pvmBackend, devConfig = null, networkConfig = null, ancestry = [], @@ -30,11 +31,12 @@ export class JamConfig { isAuthoring?: boolean; nodeName: string; nodeConfig: NodeConfiguration; + pvmBackend: PvmBackend; devConfig?: DevConfig | null; networkConfig?: NetworkConfig | null; ancestry?: [HeaderHash, TimeSlot][]; }) { - return new JamConfig(isAuthoring ?? false, nodeName, nodeConfig, devConfig, networkConfig, ancestry); + return new JamConfig(isAuthoring ?? false, nodeName, nodeConfig, pvmBackend, devConfig, networkConfig, ancestry); } private constructor( @@ -44,6 +46,8 @@ export class JamConfig { public readonly nodeName: string, /** Node starting configuration. */ public readonly node: NodeConfiguration, + /** PVM execution engine. */ + public readonly pvmBackend: PvmBackend, /** Developer specific configuration. */ public readonly dev: DevConfig | null, /** Networking options. */ diff --git a/packages/jam/node/main-fuzz.ts b/packages/jam/node/main-fuzz.ts index 06f0155ad..e02fbbdc5 100644 --- a/packages/jam/node/main-fuzz.ts +++ b/packages/jam/node/main-fuzz.ts @@ -31,6 +31,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}.`; const { jamNodeConfig: config } = fuzzConfig; diff --git a/packages/jam/node/main-importer.ts b/packages/jam/node/main-importer.ts index 9aae545c9..93ddf97ec 100644 --- a/packages/jam/node/main-importer.ts +++ b/packages/jam/node/main-importer.ts @@ -17,6 +17,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}.`; const chainSpec = getChainSpec(config.node.flavor); const blake2b = await Blake2b.createHasher(); const omitSealVerification = false; @@ -34,6 +35,7 @@ export async function mainImporter(config: JamConfig, withRelPath: (v: string) = chainSpec, blake2b, workerParams: { + pvm: config.pvmBackend, omitSealVerification, }, }) @@ -42,6 +44,7 @@ export async function mainImporter(config: JamConfig, withRelPath: (v: string) = blake2b, dbPath, workerParams: { + pvm: config.pvmBackend, omitSealVerification, }, }); diff --git a/packages/jam/node/main.ts b/packages/jam/node/main.ts index 6b29b7a76..3ed96d018 100755 --- a/packages/jam/node/main.ts +++ b/packages/jam/node/main.ts @@ -33,6 +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}.`; const chainSpec = getChainSpec(config.node.flavor); const blake2b = await Blake2b.createHasher(); if (config.node.databaseBasePath === undefined) { @@ -50,6 +51,7 @@ export async function main(config: JamConfig, withRelPath: (v: string) => string const importerConfig = LmdbWorkerConfig.new({ ...baseConfig, workerParams: ImporterConfig.create({ + pvm: config.pvmBackend, omitSealVerification: config.node.authorship.omitSealVerification, }), }); diff --git a/packages/jam/transition/accumulate/accumulate.test.ts b/packages/jam/transition/accumulate/accumulate.test.ts index f7c4705d9..4f74961d4 100644 --- a/packages/jam/transition/accumulate/accumulate.test.ts +++ b/packages/jam/transition/accumulate/accumulate.test.ts @@ -19,7 +19,7 @@ import { WorkPackageSpec, WorkReport } from "@typeberry/block/work-report.js"; import { WorkExecResult, WorkExecResultKind, WorkRefineLoad, WorkResult } from "@typeberry/block/work-result.js"; import { Bytes, BytesBlob } from "@typeberry/bytes"; import { asKnownSize, FixedSizeArray, HashDictionary, HashSet } from "@typeberry/collections"; -import { type ChainSpec, tinyChainSpec } from "@typeberry/config"; +import { type ChainSpec, PvmBackend, PvmBackendNames, tinyChainSpec } from "@typeberry/config"; import { Blake2b, HASH_SIZE, type OpaqueHash } from "@typeberry/hash"; import { tryAsU16, tryAsU32, tryAsU64 } from "@typeberry/numbers"; import { @@ -46,271 +46,273 @@ type TestServiceInfo = { lastAccumulation?: TimeSlot; }; -describe("accumulate", () => { - // based on tiny/enqueue_and_unlock_chain_wraps-5.json - it("should do correct state transition", async () => { - const entropy = hashFromString("0xae85d6635e9ae539d0846b911ec86a27fe000f619b78bcac8a74b77e36f6dbcf"); +[PvmBackend.BuiltIn, PvmBackend.Ananas].forEach((pvm) => { + describe(`accumulate: ${PvmBackendNames[pvm]}`, () => { + // based on tiny/enqueue_and_unlock_chain_wraps-5.json + it("should do correct state transition", async () => { + const entropy = hashFromString("0xae85d6635e9ae539d0846b911ec86a27fe000f619b78bcac8a74b77e36f6dbcf"); - const input: AccumulateInput = { - reports: [ - createWorkReport(hashFromString("0xdf49de52326d7d3c99391cdd32b2ca7c06398e798b520970347160ecf8d9ce32")), - createWorkReport(hashFromString("0xd3d0ac423a2e9451db2e88bd75cc143b19424747fbcf2696792987436e8722a6")), - ], - slot: tryAsTimeSlot(47), - entropy, - }; + const input: AccumulateInput = { + reports: [ + createWorkReport(hashFromString("0xdf49de52326d7d3c99391cdd32b2ca7c06398e798b520970347160ecf8d9ce32")), + createWorkReport(hashFromString("0xd3d0ac423a2e9451db2e88bd75cc143b19424747fbcf2696792987436e8722a6")), + ], + slot: tryAsTimeSlot(47), + entropy, + }; - const services = createServices([ - [ - tryAsServiceId(1729), - hashFromString("0x3ecc56accce719e5214e8dbb034f49d5cf0c6942da3e5f3f047d1693cc60c74a"), - preimageBlob, - { lastAccumulation: undefined }, - ], - ]); - const state = InMemoryState.partial(tinyChainSpec, { - timeslot: tryAsTimeSlot(46), - services, - privilegedServices: createPrivilegedServices(tinyChainSpec), - recentlyAccumulated: tryAsPerEpochBlock( + const services = createServices([ [ - [], - [], - [], - [], - [], - ["0x04f5f2b6509d847d49487d5db1275e8105225018952c7aec9d2d90f039c50e45"], - ["0x506524cb141b83715705c05983d17ebc7760c64d2ce81f15b6bc2dd1eeb55551"], - ["0x7bbdd7961afb2db642f7cdb87021fb053c67d2ac79d69a87fc9fbcf6786e30b9"], - [], - ["0x07b08ccece1b01a9202152a6fa99d23cf0160da721374ccaabd40885d8fc15d3"], - [], - ["0x9d7588b469d529d9a058c12bef3ce96106babf54aa3ba251066832cd160aa2c6"], - ].map((x) => HashSet.from(x.map((x) => hashFromString(x)))), - tinyChainSpec, - ), - accumulationQueue: tryAsPerEpochBlock( - [ - [], - [ - createNotAccumulatedWorkReport( - hashFromString("0xa7a66c1635f45e0413cec365f60fb46fa0489c4d2df75f6fd9c00d1da3729222"), - [hashFromString("0x38b7a75bd6d296f01fc6b4b6569c38980af6ba2ea34e22d429cccfb6f5b7432a")], - [hashFromString("0x38b7a75bd6d296f01fc6b4b6569c38980af6ba2ea34e22d429cccfb6f5b7432a")], - tryAsServiceId(1729), - ), - ], - [], - [], - [], - [], - [ - createNotAccumulatedWorkReport( - hashFromString("0x38b7a75bd6d296f01fc6b4b6569c38980af6ba2ea34e22d429cccfb6f5b7432a"), - [hashFromString("0x17e5809e4d5e9daf50361fb6557a00814301159fc81a39cd4d2687e110e0b7be")], - [hashFromString("0x17e5809e4d5e9daf50361fb6557a00814301159fc81a39cd4d2687e110e0b7be")], - tryAsServiceId(1729), - ), - createNotAccumulatedWorkReport( - hashFromString("0x17e5809e4d5e9daf50361fb6557a00814301159fc81a39cd4d2687e110e0b7be"), - [hashFromString("0x36a748779db31316ac26eebc08dadbeda8a8d4794892f6e340e32ab69e0d4d80")], - [hashFromString("0x36a748779db31316ac26eebc08dadbeda8a8d4794892f6e340e32ab69e0d4d80")], - tryAsServiceId(1729), - ), - ], - [], - [], - [], + tryAsServiceId(1729), + hashFromString("0x3ecc56accce719e5214e8dbb034f49d5cf0c6942da3e5f3f047d1693cc60c74a"), + preimageBlob, + { lastAccumulation: undefined }, + ], + ]); + const state = InMemoryState.partial(tinyChainSpec, { + timeslot: tryAsTimeSlot(46), + services, + privilegedServices: createPrivilegedServices(tinyChainSpec), + recentlyAccumulated: tryAsPerEpochBlock( [ - createNotAccumulatedWorkReport( - hashFromString("0x36a748779db31316ac26eebc08dadbeda8a8d4794892f6e340e32ab69e0d4d80"), - [hashFromString("0xd3d0ac423a2e9451db2e88bd75cc143b19424747fbcf2696792987436e8722a6")], - [hashFromString("0xd3d0ac423a2e9451db2e88bd75cc143b19424747fbcf2696792987436e8722a6")], - tryAsServiceId(1729), - ), - ], + [], + [], + [], + [], + [], + ["0x04f5f2b6509d847d49487d5db1275e8105225018952c7aec9d2d90f039c50e45"], + ["0x506524cb141b83715705c05983d17ebc7760c64d2ce81f15b6bc2dd1eeb55551"], + ["0x7bbdd7961afb2db642f7cdb87021fb053c67d2ac79d69a87fc9fbcf6786e30b9"], + [], + ["0x07b08ccece1b01a9202152a6fa99d23cf0160da721374ccaabd40885d8fc15d3"], + [], + ["0x9d7588b469d529d9a058c12bef3ce96106babf54aa3ba251066832cd160aa2c6"], + ].map((x) => HashSet.from(x.map((x) => hashFromString(x)))), + tinyChainSpec, + ), + accumulationQueue: tryAsPerEpochBlock( [ - createNotAccumulatedWorkReport( - hashFromString("0x18176cfbff4a8b40f2d9cba3c2ddc4a5b75e9152c8c41452cda90bebc0632013"), - [hashFromString("0xa7a66c1635f45e0413cec365f60fb46fa0489c4d2df75f6fd9c00d1da3729222")], - [hashFromString("0xa7a66c1635f45e0413cec365f60fb46fa0489c4d2df75f6fd9c00d1da3729222")], - tryAsServiceId(1729), - ), - createNotAccumulatedWorkReport( - hashFromString("0xf5983aaa6fe1e7428902ace29d14be81a664a65f6dfca1138ccb99136547324e"), - [hashFromString("0x18176cfbff4a8b40f2d9cba3c2ddc4a5b75e9152c8c41452cda90bebc0632013")], - [hashFromString("0x18176cfbff4a8b40f2d9cba3c2ddc4a5b75e9152c8c41452cda90bebc0632013")], - tryAsServiceId(1729), - ), + [], + [ + createNotAccumulatedWorkReport( + hashFromString("0xa7a66c1635f45e0413cec365f60fb46fa0489c4d2df75f6fd9c00d1da3729222"), + [hashFromString("0x38b7a75bd6d296f01fc6b4b6569c38980af6ba2ea34e22d429cccfb6f5b7432a")], + [hashFromString("0x38b7a75bd6d296f01fc6b4b6569c38980af6ba2ea34e22d429cccfb6f5b7432a")], + tryAsServiceId(1729), + ), + ], + [], + [], + [], + [], + [ + createNotAccumulatedWorkReport( + hashFromString("0x38b7a75bd6d296f01fc6b4b6569c38980af6ba2ea34e22d429cccfb6f5b7432a"), + [hashFromString("0x17e5809e4d5e9daf50361fb6557a00814301159fc81a39cd4d2687e110e0b7be")], + [hashFromString("0x17e5809e4d5e9daf50361fb6557a00814301159fc81a39cd4d2687e110e0b7be")], + tryAsServiceId(1729), + ), + createNotAccumulatedWorkReport( + hashFromString("0x17e5809e4d5e9daf50361fb6557a00814301159fc81a39cd4d2687e110e0b7be"), + [hashFromString("0x36a748779db31316ac26eebc08dadbeda8a8d4794892f6e340e32ab69e0d4d80")], + [hashFromString("0x36a748779db31316ac26eebc08dadbeda8a8d4794892f6e340e32ab69e0d4d80")], + tryAsServiceId(1729), + ), + ], + [], + [], + [], + [ + createNotAccumulatedWorkReport( + hashFromString("0x36a748779db31316ac26eebc08dadbeda8a8d4794892f6e340e32ab69e0d4d80"), + [hashFromString("0xd3d0ac423a2e9451db2e88bd75cc143b19424747fbcf2696792987436e8722a6")], + [hashFromString("0xd3d0ac423a2e9451db2e88bd75cc143b19424747fbcf2696792987436e8722a6")], + tryAsServiceId(1729), + ), + ], + [ + createNotAccumulatedWorkReport( + hashFromString("0x18176cfbff4a8b40f2d9cba3c2ddc4a5b75e9152c8c41452cda90bebc0632013"), + [hashFromString("0xa7a66c1635f45e0413cec365f60fb46fa0489c4d2df75f6fd9c00d1da3729222")], + [hashFromString("0xa7a66c1635f45e0413cec365f60fb46fa0489c4d2df75f6fd9c00d1da3729222")], + tryAsServiceId(1729), + ), + createNotAccumulatedWorkReport( + hashFromString("0xf5983aaa6fe1e7428902ace29d14be81a664a65f6dfca1138ccb99136547324e"), + [hashFromString("0x18176cfbff4a8b40f2d9cba3c2ddc4a5b75e9152c8c41452cda90bebc0632013")], + [hashFromString("0x18176cfbff4a8b40f2d9cba3c2ddc4a5b75e9152c8c41452cda90bebc0632013")], + tryAsServiceId(1729), + ), + ], ], - ], - tinyChainSpec, - ), - }); + tinyChainSpec, + ), + }); - const expectedServices = createServices([ - [ - tryAsServiceId(1729), - hashFromString("0x3ecc56accce719e5214e8dbb034f49d5cf0c6942da3e5f3f047d1693cc60c74a"), - preimageBlob, - { lastAccumulation: tryAsTimeSlot(47) }, - ], - ]); - const expectedState: AccumulateState = InMemoryState.partial(tinyChainSpec, { - timeslot: input.slot, - services: expectedServices, - privilegedServices: state.privilegedServices, - recentlyAccumulated: tryAsPerEpochBlock( + const expectedServices = createServices([ [ - [], - [], - [], - [], - ["0x04f5f2b6509d847d49487d5db1275e8105225018952c7aec9d2d90f039c50e45"], - ["0x506524cb141b83715705c05983d17ebc7760c64d2ce81f15b6bc2dd1eeb55551"], - ["0x7bbdd7961afb2db642f7cdb87021fb053c67d2ac79d69a87fc9fbcf6786e30b9"], - [], - ["0x07b08ccece1b01a9202152a6fa99d23cf0160da721374ccaabd40885d8fc15d3"], - [], - ["0x9d7588b469d529d9a058c12bef3ce96106babf54aa3ba251066832cd160aa2c6"], + tryAsServiceId(1729), + hashFromString("0x3ecc56accce719e5214e8dbb034f49d5cf0c6942da3e5f3f047d1693cc60c74a"), + preimageBlob, + { lastAccumulation: tryAsTimeSlot(47) }, + ], + ]); + const expectedState: AccumulateState = InMemoryState.partial(tinyChainSpec, { + timeslot: input.slot, + services: expectedServices, + privilegedServices: state.privilegedServices, + recentlyAccumulated: tryAsPerEpochBlock( [ - "0x17e5809e4d5e9daf50361fb6557a00814301159fc81a39cd4d2687e110e0b7be", - "0x18176cfbff4a8b40f2d9cba3c2ddc4a5b75e9152c8c41452cda90bebc0632013", - "0x36a748779db31316ac26eebc08dadbeda8a8d4794892f6e340e32ab69e0d4d80", - "0x38b7a75bd6d296f01fc6b4b6569c38980af6ba2ea34e22d429cccfb6f5b7432a", - "0xa7a66c1635f45e0413cec365f60fb46fa0489c4d2df75f6fd9c00d1da3729222", - "0xd3d0ac423a2e9451db2e88bd75cc143b19424747fbcf2696792987436e8722a6", - "0xdf49de52326d7d3c99391cdd32b2ca7c06398e798b520970347160ecf8d9ce32", - "0xf5983aaa6fe1e7428902ace29d14be81a664a65f6dfca1138ccb99136547324e", - ], - ].map((x) => HashSet.from(x.map((x) => hashFromString(x)))), - tinyChainSpec, - ), - accumulationQueue: tryAsPerEpochBlock([[], [], [], [], [], [], [], [], [], [], [], []], tinyChainSpec), - }); - const expectedOutput: AccumulationOutput[] = []; - const accumulate = new Accumulate(tinyChainSpec, blake2b, state); + [], + [], + [], + [], + ["0x04f5f2b6509d847d49487d5db1275e8105225018952c7aec9d2d90f039c50e45"], + ["0x506524cb141b83715705c05983d17ebc7760c64d2ce81f15b6bc2dd1eeb55551"], + ["0x7bbdd7961afb2db642f7cdb87021fb053c67d2ac79d69a87fc9fbcf6786e30b9"], + [], + ["0x07b08ccece1b01a9202152a6fa99d23cf0160da721374ccaabd40885d8fc15d3"], + [], + ["0x9d7588b469d529d9a058c12bef3ce96106babf54aa3ba251066832cd160aa2c6"], + [ + "0x17e5809e4d5e9daf50361fb6557a00814301159fc81a39cd4d2687e110e0b7be", + "0x18176cfbff4a8b40f2d9cba3c2ddc4a5b75e9152c8c41452cda90bebc0632013", + "0x36a748779db31316ac26eebc08dadbeda8a8d4794892f6e340e32ab69e0d4d80", + "0x38b7a75bd6d296f01fc6b4b6569c38980af6ba2ea34e22d429cccfb6f5b7432a", + "0xa7a66c1635f45e0413cec365f60fb46fa0489c4d2df75f6fd9c00d1da3729222", + "0xd3d0ac423a2e9451db2e88bd75cc143b19424747fbcf2696792987436e8722a6", + "0xdf49de52326d7d3c99391cdd32b2ca7c06398e798b520970347160ecf8d9ce32", + "0xf5983aaa6fe1e7428902ace29d14be81a664a65f6dfca1138ccb99136547324e", + ], + ].map((x) => HashSet.from(x.map((x) => hashFromString(x)))), + tinyChainSpec, + ), + accumulationQueue: tryAsPerEpochBlock([[], [], [], [], [], [], [], [], [], [], [], []], tinyChainSpec), + }); + const expectedOutput: AccumulationOutput[] = []; + const accumulate = new Accumulate(tinyChainSpec, blake2b, state, pvm); - // when - const output = await accumulate.transition(input); - if (output.isError) { - assert.fail(`Expect ok, got ${resultToString(output)}`); - } - state.applyUpdate(output.ok.stateUpdate); + // when + const output = await accumulate.transition(input); + if (output.isError) { + assert.fail(`Expect ok, got ${resultToString(output)}`); + } + state.applyUpdate(output.ok.stateUpdate); - // then - deepEqual(output.ok.accumulationOutputLog.array, expectedOutput); - deepEqual(state, expectedState); - }); + // then + deepEqual(output.ok.accumulationOutputLog.array, expectedOutput); + deepEqual(state, expectedState); + }); - it("should detect creation of duplicated service ids", async () => { - const services = createServices([ - [ - tryAsServiceId(1729), - hashFromString("0x3ecc56accce719e5214e8dbb034f49d5cf0c6942da3e5f3f047d1693cc60c74a"), - preimageBlob, - { lastAccumulation: undefined }, - ], - ]); - const state = InMemoryState.partial(tinyChainSpec, { - timeslot: tryAsTimeSlot(46), - services, - privilegedServices: createPrivilegedServices(tinyChainSpec), - recentlyAccumulated: tryAsPerEpochBlock( + it("should detect creation of duplicated service ids", async () => { + const services = createServices([ [ - [], - [], - [], - [], - [], - ["0x04f5f2b6509d847d49487d5db1275e8105225018952c7aec9d2d90f039c50e45"], - ["0x506524cb141b83715705c05983d17ebc7760c64d2ce81f15b6bc2dd1eeb55551"], - ["0x7bbdd7961afb2db642f7cdb87021fb053c67d2ac79d69a87fc9fbcf6786e30b9"], - [], - ["0x07b08ccece1b01a9202152a6fa99d23cf0160da721374ccaabd40885d8fc15d3"], - [], - ["0x9d7588b469d529d9a058c12bef3ce96106babf54aa3ba251066832cd160aa2c6"], - ].map((x) => HashSet.from(x.map((x) => hashFromString(x)))), - tinyChainSpec, - ), - accumulationQueue: tryAsPerEpochBlock( - [ - [], - [ - createNotAccumulatedWorkReport( - hashFromString("0xa7a66c1635f45e0413cec365f60fb46fa0489c4d2df75f6fd9c00d1da3729222"), - [hashFromString("0x38b7a75bd6d296f01fc6b4b6569c38980af6ba2ea34e22d429cccfb6f5b7432a")], - [hashFromString("0x38b7a75bd6d296f01fc6b4b6569c38980af6ba2ea34e22d429cccfb6f5b7432a")], - tryAsServiceId(1729), - ), - ], - [], - [], - [], - [], - [ - createNotAccumulatedWorkReport( - hashFromString("0x38b7a75bd6d296f01fc6b4b6569c38980af6ba2ea34e22d429cccfb6f5b7432a"), - [hashFromString("0x17e5809e4d5e9daf50361fb6557a00814301159fc81a39cd4d2687e110e0b7be")], - [hashFromString("0x17e5809e4d5e9daf50361fb6557a00814301159fc81a39cd4d2687e110e0b7be")], - tryAsServiceId(1729), - ), - createNotAccumulatedWorkReport( - hashFromString("0x17e5809e4d5e9daf50361fb6557a00814301159fc81a39cd4d2687e110e0b7be"), - [hashFromString("0x36a748779db31316ac26eebc08dadbeda8a8d4794892f6e340e32ab69e0d4d80")], - [hashFromString("0x36a748779db31316ac26eebc08dadbeda8a8d4794892f6e340e32ab69e0d4d80")], - tryAsServiceId(1729), - ), - ], - [], - [], - [], + tryAsServiceId(1729), + hashFromString("0x3ecc56accce719e5214e8dbb034f49d5cf0c6942da3e5f3f047d1693cc60c74a"), + preimageBlob, + { lastAccumulation: undefined }, + ], + ]); + const state = InMemoryState.partial(tinyChainSpec, { + timeslot: tryAsTimeSlot(46), + services, + privilegedServices: createPrivilegedServices(tinyChainSpec), + recentlyAccumulated: tryAsPerEpochBlock( [ - createNotAccumulatedWorkReport( - hashFromString("0x36a748779db31316ac26eebc08dadbeda8a8d4794892f6e340e32ab69e0d4d80"), - [hashFromString("0xd3d0ac423a2e9451db2e88bd75cc143b19424747fbcf2696792987436e8722a6")], - [hashFromString("0xd3d0ac423a2e9451db2e88bd75cc143b19424747fbcf2696792987436e8722a6")], - tryAsServiceId(1729), - ), - ], + [], + [], + [], + [], + [], + ["0x04f5f2b6509d847d49487d5db1275e8105225018952c7aec9d2d90f039c50e45"], + ["0x506524cb141b83715705c05983d17ebc7760c64d2ce81f15b6bc2dd1eeb55551"], + ["0x7bbdd7961afb2db642f7cdb87021fb053c67d2ac79d69a87fc9fbcf6786e30b9"], + [], + ["0x07b08ccece1b01a9202152a6fa99d23cf0160da721374ccaabd40885d8fc15d3"], + [], + ["0x9d7588b469d529d9a058c12bef3ce96106babf54aa3ba251066832cd160aa2c6"], + ].map((x) => HashSet.from(x.map((x) => hashFromString(x)))), + tinyChainSpec, + ), + accumulationQueue: tryAsPerEpochBlock( [ - createNotAccumulatedWorkReport( - hashFromString("0x18176cfbff4a8b40f2d9cba3c2ddc4a5b75e9152c8c41452cda90bebc0632013"), - [hashFromString("0xa7a66c1635f45e0413cec365f60fb46fa0489c4d2df75f6fd9c00d1da3729222")], - [hashFromString("0xa7a66c1635f45e0413cec365f60fb46fa0489c4d2df75f6fd9c00d1da3729222")], - tryAsServiceId(1729), - ), - createNotAccumulatedWorkReport( - hashFromString("0xf5983aaa6fe1e7428902ace29d14be81a664a65f6dfca1138ccb99136547324e"), - [hashFromString("0x18176cfbff4a8b40f2d9cba3c2ddc4a5b75e9152c8c41452cda90bebc0632013")], - [hashFromString("0x18176cfbff4a8b40f2d9cba3c2ddc4a5b75e9152c8c41452cda90bebc0632013")], - tryAsServiceId(1729), - ), + [], + [ + createNotAccumulatedWorkReport( + hashFromString("0xa7a66c1635f45e0413cec365f60fb46fa0489c4d2df75f6fd9c00d1da3729222"), + [hashFromString("0x38b7a75bd6d296f01fc6b4b6569c38980af6ba2ea34e22d429cccfb6f5b7432a")], + [hashFromString("0x38b7a75bd6d296f01fc6b4b6569c38980af6ba2ea34e22d429cccfb6f5b7432a")], + tryAsServiceId(1729), + ), + ], + [], + [], + [], + [], + [ + createNotAccumulatedWorkReport( + hashFromString("0x38b7a75bd6d296f01fc6b4b6569c38980af6ba2ea34e22d429cccfb6f5b7432a"), + [hashFromString("0x17e5809e4d5e9daf50361fb6557a00814301159fc81a39cd4d2687e110e0b7be")], + [hashFromString("0x17e5809e4d5e9daf50361fb6557a00814301159fc81a39cd4d2687e110e0b7be")], + tryAsServiceId(1729), + ), + createNotAccumulatedWorkReport( + hashFromString("0x17e5809e4d5e9daf50361fb6557a00814301159fc81a39cd4d2687e110e0b7be"), + [hashFromString("0x36a748779db31316ac26eebc08dadbeda8a8d4794892f6e340e32ab69e0d4d80")], + [hashFromString("0x36a748779db31316ac26eebc08dadbeda8a8d4794892f6e340e32ab69e0d4d80")], + tryAsServiceId(1729), + ), + ], + [], + [], + [], + [ + createNotAccumulatedWorkReport( + hashFromString("0x36a748779db31316ac26eebc08dadbeda8a8d4794892f6e340e32ab69e0d4d80"), + [hashFromString("0xd3d0ac423a2e9451db2e88bd75cc143b19424747fbcf2696792987436e8722a6")], + [hashFromString("0xd3d0ac423a2e9451db2e88bd75cc143b19424747fbcf2696792987436e8722a6")], + tryAsServiceId(1729), + ), + ], + [ + createNotAccumulatedWorkReport( + hashFromString("0x18176cfbff4a8b40f2d9cba3c2ddc4a5b75e9152c8c41452cda90bebc0632013"), + [hashFromString("0xa7a66c1635f45e0413cec365f60fb46fa0489c4d2df75f6fd9c00d1da3729222")], + [hashFromString("0xa7a66c1635f45e0413cec365f60fb46fa0489c4d2df75f6fd9c00d1da3729222")], + tryAsServiceId(1729), + ), + createNotAccumulatedWorkReport( + hashFromString("0xf5983aaa6fe1e7428902ace29d14be81a664a65f6dfca1138ccb99136547324e"), + [hashFromString("0x18176cfbff4a8b40f2d9cba3c2ddc4a5b75e9152c8c41452cda90bebc0632013")], + [hashFromString("0x18176cfbff4a8b40f2d9cba3c2ddc4a5b75e9152c8c41452cda90bebc0632013")], + tryAsServiceId(1729), + ), + ], ], - ], - tinyChainSpec, - ), - }); + tinyChainSpec, + ), + }); - const accumulate = new Accumulate(tinyChainSpec, blake2b, state); + const accumulate = new Accumulate(tinyChainSpec, blake2b, state, pvm); - const mod = 2 ** 32 - MIN_PUBLIC_SERVICE_INDEX - 2 ** 8; - const offset = MIN_PUBLIC_SERVICE_INDEX; + const mod = 2 ** 32 - MIN_PUBLIC_SERVICE_INDEX - 2 ** 8; + const offset = MIN_PUBLIC_SERVICE_INDEX; - const createdIds: ServiceId[] = []; - let currentId = tryAsServiceId(0); + const createdIds: ServiceId[] = []; + let currentId = tryAsServiceId(0); - for (let i = 0; i < 10; i++) { - createdIds.push(currentId); - currentId = tryAsServiceId(((currentId - offset + 1 + mod) % mod) + offset); - } + for (let i = 0; i < 10; i++) { + createdIds.push(currentId); + currentId = tryAsServiceId(((currentId - offset + 1 + mod) % mod) + offset); + } - // create no duplications - assert.ok(!accumulate.hasDuplicatedServiceIdCreated(createdIds), "Should not trigger duplicated service id!"); + // create no duplications + assert.ok(!accumulate.hasDuplicatedServiceIdCreated(createdIds), "Should not trigger duplicated service id!"); - // create duplication - createdIds.push(tryAsServiceId(0)); - assert.ok(accumulate.hasDuplicatedServiceIdCreated(createdIds), "Should detect duplicated service id!"); + // create duplication + createdIds.push(tryAsServiceId(0)); + assert.ok(accumulate.hasDuplicatedServiceIdCreated(createdIds), "Should detect duplicated service id!"); + }); }); }); diff --git a/packages/jam/transition/accumulate/accumulate.ts b/packages/jam/transition/accumulate/accumulate.ts index 6fd135717..f485fef0e 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 } 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 { @@ -20,8 +20,7 @@ import { } from "@typeberry/jam-host-calls/externalities/state-update.js"; import { Logger } from "@typeberry/logger"; import { sumU64, tryAsU32, type U32 } from "@typeberry/numbers"; -import { tryAsGas } from "@typeberry/pvm-interpreter"; -import { Status } from "@typeberry/pvm-interpreter/status.js"; +import { Status, tryAsGas } from "@typeberry/pvm-interface"; import { type AccumulationOutput, type AutoAccumulate, @@ -86,6 +85,7 @@ export class Accumulate { public readonly chainSpec: ChainSpec, public readonly blake2b: Blake2b, public readonly state: AccumulateState, + public readonly pvm: PvmBackend, ) {} /** @@ -171,7 +171,14 @@ export class Accumulate { fetchExternalities, }; - const executor = PvmExecutor.createAccumulateExecutor(serviceId, code, externalities, this.chainSpec); + const executor = await PvmExecutor.createAccumulateExecutor( + serviceId, + code, + externalities, + this.chainSpec, + this.pvm, + ); + const invocationArgs = Encoder.encodeObject(ARGS_CODEC, { slot, serviceId, diff --git a/packages/jam/transition/accumulate/deferred-transfers.ts b/packages/jam/transition/accumulate/deferred-transfers.ts index 9df12d3d6..2ecf23eaf 100644 --- a/packages/jam/transition/accumulate/deferred-transfers.ts +++ b/packages/jam/transition/accumulate/deferred-transfers.ts @@ -1,7 +1,7 @@ import { type EntropyHash, type ServiceId, type TimeSlot, tryAsServiceGas } from "@typeberry/block"; import { W_C } from "@typeberry/block/gp-constants.js"; import { codec, Encoder } from "@typeberry/codec"; -import type { ChainSpec } from "@typeberry/config"; +import type { ChainSpec, PvmBackend } from "@typeberry/config"; import type { Blake2b } from "@typeberry/hash"; import type { PendingTransfer } from "@typeberry/jam-host-calls/externalities/pending-transfer.js"; import { @@ -10,7 +10,7 @@ import { } from "@typeberry/jam-host-calls/externalities/state-update.js"; import { Logger } from "@typeberry/logger"; import { sumU64, tryAsU32 } from "@typeberry/numbers"; -import { tryAsGas } from "@typeberry/pvm-interpreter"; +import { tryAsGas } from "@typeberry/pvm-interface"; import { ServiceAccountInfo, type ServicesUpdate, type State } from "@typeberry/state"; import { check, Result } from "@typeberry/utils"; import { AccumulateExternalities } from "../externalities/accumulate-externalities.js"; @@ -54,6 +54,7 @@ export class DeferredTransfers { public readonly chainSpec: ChainSpec, public readonly blake2b: Blake2b, private readonly state: DeferredTransfersState, + private readonly pvm: PvmBackend, ) {} async transition({ @@ -120,7 +121,12 @@ export class DeferredTransfers { logger.trace`Skipping ON_TRANSFER execution for service ${serviceId} because code is too long`; } } else { - const executor = PvmExecutor.createOnTransferExecutor(serviceId, code, { partialState, fetchExternalities }); + const executor = await PvmExecutor.createOnTransferExecutor( + serviceId, + code, + { partialState, fetchExternalities }, + this.pvm, + ); const args = Encoder.encodeObject( ARGS_CODEC, { timeslot, serviceId, transfersLength: tryAsU32(transfers.length) }, diff --git a/packages/jam/transition/accumulate/pvm-executor.ts b/packages/jam/transition/accumulate/pvm-executor.ts index 0c4fa1430..4c78780b8 100644 --- a/packages/jam/transition/accumulate/pvm-executor.ts +++ b/packages/jam/transition/accumulate/pvm-executor.ts @@ -1,6 +1,6 @@ import type { ServiceId } from "@typeberry/block"; import type { BytesBlob } from "@typeberry/bytes"; -import type { ChainSpec } from "@typeberry/config"; +import type { ChainSpec, PvmBackend } from "@typeberry/config"; import { Assign } from "@typeberry/jam-host-calls/accumulate/assign.js"; import { Bless } from "@typeberry/jam-host-calls/accumulate/bless.js"; import { Checkpoint } from "@typeberry/jam-host-calls/accumulate/checkpoint.js"; @@ -28,8 +28,7 @@ import { Missing } from "@typeberry/jam-host-calls/missing.js"; import { type AccountsRead, Read } from "@typeberry/jam-host-calls/read.js"; import { type AccountsWrite, Write } from "@typeberry/jam-host-calls/write.js"; import { type HostCallHandler, HostCalls, PvmHostCallExtension, PvmInstanceManager } from "@typeberry/pvm-host-calls"; -import type { Gas } from "@typeberry/pvm-interpreter"; -import { Program } from "@typeberry/pvm-program"; +import type { Gas } from "@typeberry/pvm-interface"; const ACCUMULATE_HOST_CALL_CLASSES = [ Bless, @@ -71,18 +70,22 @@ namespace entrypoint { export class PvmExecutor { private readonly pvm: PvmHostCallExtension; private hostCalls: HostCalls; - private pvmInstanceManager = new PvmInstanceManager(4); private constructor( private serviceCode: BytesBlob, hostCallHandlers: HostCallHandler[], private entrypoint: ProgramCounter, + pvmInstanceManager: PvmInstanceManager, ) { this.hostCalls = new HostCalls({ missing: new Missing(), handlers: hostCallHandlers, }); - this.pvm = new PvmHostCallExtension(this.pvmInstanceManager, this.hostCalls); + this.pvm = new PvmHostCallExtension(pvmInstanceManager, this.hostCalls); + } + + private static async prepareBackend(pvm: PvmBackend) { + return PvmInstanceManager.new(pvm); } /** Prepare accumulation host call handlers */ @@ -130,29 +133,31 @@ export class PvmExecutor { * @returns `ReturnValue` object that can be a status or memory slice */ async run(args: BytesBlob, gas: Gas) { - const program = Program.fromSpi(this.serviceCode.raw, args.raw, true); - - return this.pvm.runProgram(program.code, Number(this.entrypoint), gas, program.registers, program.memory); + return this.pvm.runProgram(this.serviceCode.raw, args.raw, Number(this.entrypoint), gas); } /** A utility function that can be used to prepare accumulate executor */ - static createAccumulateExecutor( + static async createAccumulateExecutor( serviceId: ServiceId, serviceCode: BytesBlob, externalities: AccumulateHostCallExternalities, chainSpec: ChainSpec, + pvm: PvmBackend, ) { const hostCallHandlers = PvmExecutor.prepareAccumulateHostCalls(serviceId, externalities, chainSpec); - return new PvmExecutor(serviceCode, hostCallHandlers, entrypoint.ACCUMULATE); + const instances = await PvmExecutor.prepareBackend(pvm); + return new PvmExecutor(serviceCode, hostCallHandlers, entrypoint.ACCUMULATE, instances); } /** A utility function that can be used to prepare on transfer executor */ - static createOnTransferExecutor( + static async createOnTransferExecutor( serviceId: ServiceId, serviceCode: BytesBlob, externalities: OnTransferHostCallExternalities, + pvm: PvmBackend, ) { const hostCallHandlers = PvmExecutor.prepareOnTransferHostCalls(serviceId, externalities); - return new PvmExecutor(serviceCode, hostCallHandlers, entrypoint.ON_TRANSFER); + const instances = await PvmExecutor.prepareBackend(pvm); + return new PvmExecutor(serviceCode, hostCallHandlers, entrypoint.ON_TRANSFER, instances); } } diff --git a/packages/jam/transition/chain-stf.ts b/packages/jam/transition/chain-stf.ts index 8577b0449..9f4067934 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 } 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"; @@ -145,6 +145,7 @@ export class OnChain { public readonly state: State & WithStateView, blocks: BlocksDb, public readonly hasher: TransitionHasher, + pvm: PvmBackend = PvmBackend.BuiltIn, ) { const bandersnatch = BandernsatchWasm.new(); this.statistics = new Statistics(chainSpec, state); @@ -158,9 +159,9 @@ export class OnChain { this.reports = new Reports(chainSpec, hasher.blake2b, state, new DbHeaderChain(blocks)); this.assurances = new Assurances(chainSpec, state, hasher.blake2b); - this.accumulate = new Accumulate(chainSpec, hasher.blake2b, state); + this.accumulate = new Accumulate(chainSpec, hasher.blake2b, state, pvm); this.accumulateOutput = new AccumulateOutput(); - this.deferredTransfers = new DeferredTransfers(chainSpec, hasher.blake2b, state); + this.deferredTransfers = new DeferredTransfers(chainSpec, hasher.blake2b, state, pvm); this.preimages = new Preimages(state, hasher.blake2b); this.authorization = new Authorization(chainSpec, state); diff --git a/packages/jam/transition/package.json b/packages/jam/transition/package.json index 7824b0a71..0d3bfd81c 100644 --- a/packages/jam/transition/package.json +++ b/packages/jam/transition/package.json @@ -13,11 +13,12 @@ "@typeberry/database": "*", "@typeberry/disputes": "*", "@typeberry/hash": "*", + "@typeberry/jam-host-calls": "*", "@typeberry/logger": "*", "@typeberry/mmr": "*", "@typeberry/numbers": "*", - "@typeberry/jam-host-calls": "*", "@typeberry/pvm-host-calls": "*", + "@typeberry/pvm-interface": "*", "@typeberry/pvm-interpreter": "*", "@typeberry/pvm-program": "*", "@typeberry/safrole": "*", diff --git a/packages/workers/importer/importer.ts b/packages/workers/importer/importer.ts index afdd9bfba..49a4546ee 100644 --- a/packages/workers/importer/importer.ts +++ b/packages/workers/importer/importer.ts @@ -1,5 +1,5 @@ import { type BlockView, type HeaderHash, type HeaderView, type StateRootHash, tryAsTimeSlot } from "@typeberry/block"; -import type { ChainSpec } from "@typeberry/config"; +import type { ChainSpec, PvmBackend } from "@typeberry/config"; import type { BlocksDb, LeafDb, StatesDb, StateUpdateError } from "@typeberry/database"; import { WithHash } from "@typeberry/hash"; import type { Logger } from "@typeberry/logger"; @@ -36,6 +36,7 @@ export class Importer { constructor( spec: ChainSpec, + pvm: PvmBackend, private readonly hasher: TransitionHasher, private readonly logger: Logger, private readonly blocks: BlocksDb, @@ -48,7 +49,7 @@ export class Importer { } this.verifier = new BlockVerifier(hasher, blocks); - this.stf = new OnChain(spec, state, blocks, hasher); + this.stf = new OnChain(spec, state, blocks, hasher, pvm); this.state = state; this.currentHash = currentBestHeaderHash; this.prepareForNextEpoch(); diff --git a/packages/workers/importer/main.ts b/packages/workers/importer/main.ts index 8e0de4921..adccaf2c5 100644 --- a/packages/workers/importer/main.ts +++ b/packages/workers/importer/main.ts @@ -18,11 +18,12 @@ type Config = WorkerConfig; export class ImporterConfig { static Codec = codec.Class(ImporterConfig, { omitSealVerification: codec.bool, + pvm: codec.u8.convert( + (i) => tryAsU8(i), + (o) => { + if (o === PvmBackend.BuiltIn) { + return PvmBackend.BuiltIn; + } + if (o === PvmBackend.Ananas) { + return PvmBackend.Ananas; + } + throw new Error(`Invalid PvmBackend: ${o}`); + }, + ), }); - static create({ omitSealVerification }: CodecRecord) { - return new ImporterConfig(omitSealVerification); + static create({ omitSealVerification, pvm }: CodecRecord) { + return new ImporterConfig(omitSealVerification, pvm); } - private constructor(public readonly omitSealVerification: boolean) {} + private constructor( + public readonly omitSealVerification: boolean, + public readonly pvm: PvmBackend, + ) {} }