From 5a81ecff314fbf47133ba0f3f038054e0acd722c Mon Sep 17 00:00:00 2001 From: HeyImStas Date: Wed, 25 Mar 2026 16:53:37 +0100 Subject: [PATCH 1/8] added machine host call --- .../jam/in-core/externalities/refine.test.ts | 63 ++++++++++++++++++- packages/jam/in-core/externalities/refine.ts | 30 +++++++-- 2 files changed, 88 insertions(+), 5 deletions(-) diff --git a/packages/jam/in-core/externalities/refine.test.ts b/packages/jam/in-core/externalities/refine.test.ts index cb30bbad5..24c9fa2c6 100644 --- a/packages/jam/in-core/externalities/refine.test.ts +++ b/packages/jam/in-core/externalities/refine.test.ts @@ -14,11 +14,13 @@ import { Bytes, BytesBlob } from "@typeberry/bytes"; import { HashDictionary } from "@typeberry/collections"; import { tinyChainSpec } from "@typeberry/config"; import { HASH_SIZE } from "@typeberry/hash"; -import { SegmentExportError } from "@typeberry/jam-host-calls"; +import { SegmentExportError, tryAsMachineId, tryAsProgramCounter } from "@typeberry/jam-host-calls"; import { tryAsU32, tryAsU64 } from "@typeberry/numbers"; import { InMemoryService, InMemoryState, PreimageItem, ServiceAccountInfo, type State } from "@typeberry/state"; import { RefineExternalitiesImpl, type RefineExternalitiesParams } from "./refine.js"; +const MINIMAL_PROGRAM = new Uint8Array([0, 1, 1, 0, 0x00]); + function createSegment(byte = 0xab): Segment { return Bytes.fill(SEGMENT_BYTES, byte); } @@ -212,4 +214,63 @@ describe("RefineExternalitiesImpl", () => { assert.deepStrictEqual(exported[0].raw.subarray(0, 5), new Uint8Array([1, 2, 3, 4, 5])); }); }); + + describe("machineInit", () => { + it("should create a new inner PVM and return a machine ID", async () => { + const ext = createExt(); + const code = BytesBlob.blobFrom(MINIMAL_PROGRAM); + const pc = tryAsProgramCounter(0); + + const result = await ext.machineInit(code, pc); + + assert.strictEqual(result.isOk, true); + assert.strictEqual(result.ok, tryAsMachineId(0)); + }); + + it("should assign sequential machine IDs", async () => { + const ext = createExt(); + const code = BytesBlob.blobFrom(MINIMAL_PROGRAM); + const pc = tryAsProgramCounter(0); + + const r1 = await ext.machineInit(code, pc); + const r2 = await ext.machineInit(code, pc); + const r3 = await ext.machineInit(code, pc); + + assert.strictEqual(r1.isOk, true); + assert.strictEqual(r1.ok, tryAsMachineId(0)); + assert.strictEqual(r2.isOk, true); + assert.strictEqual(r2.ok, tryAsMachineId(1)); + assert.strictEqual(r3.isOk, true); + assert.strictEqual(r3.ok, tryAsMachineId(2)); + }); + + it("should return error for invalid program blob", async () => { + const ext = createExt(); + const invalidCode = BytesBlob.blobFrom(new Uint8Array([0xff, 0xff, 0xff])); + const pc = tryAsProgramCounter(0); + + const result = await ext.machineInit(invalidCode, pc); + + assert.strictEqual(result.isError, true); + }); + + it("should return error for empty program blob", async () => { + const ext = createExt(); + const emptyCode = BytesBlob.blobFrom(new Uint8Array([])); + const pc = tryAsProgramCounter(0); + + const result = await ext.machineInit(emptyCode, pc); + + assert.strictEqual(result.isError, true); + }); + + it("should accept a non-zero program counter", async () => { + const ext = createExt(); + const code = BytesBlob.blobFrom(MINIMAL_PROGRAM); + const pc = tryAsProgramCounter(0); + + const result = await ext.machineInit(code, pc); + assert.strictEqual(result.isOk, true); + }); + }); }); diff --git a/packages/jam/in-core/externalities/refine.ts b/packages/jam/in-core/externalities/refine.ts index d202373fe..70dca9af6 100644 --- a/packages/jam/in-core/externalities/refine.ts +++ b/packages/jam/in-core/externalities/refine.ts @@ -17,12 +17,13 @@ import { type ProgramCounter, type RefineExternalities, SegmentExportError, + tryAsMachineId, type ZeroVoidError, } from "@typeberry/jam-host-calls"; import type { U64 } from "@typeberry/numbers"; import type { HostCallMemory, HostCallRegisters } from "@typeberry/pvm-host-calls"; -import type { BigGas } from "@typeberry/pvm-interface"; -import type { ProgramDecoderError } from "@typeberry/pvm-interpreter"; +import { type BigGas, tryAsGas } from "@typeberry/pvm-interface"; +import { Interpreter, ProgramDecoder, type ProgramDecoderError } from "@typeberry/pvm-interpreter"; import type { State } from "@typeberry/state"; import { type OK, Result } from "@typeberry/utils"; @@ -39,6 +40,8 @@ export type RefineExternalitiesParams = { }; export class RefineExternalitiesImpl implements RefineExternalities { + /** Map of inner PVM instances keyed by MachineId. */ + private machines: Map = new Map(); /** Service being refined (used as default for historicalLookup). */ private readonly currentServiceId: ServiceId; /** State at the lookup anchor for preimage lookups. */ @@ -103,8 +106,27 @@ export class RefineExternalitiesImpl implements RefineExternalities { throw new Error("Method not implemented."); } - machineInit(_code: BytesBlob, _programCounter: ProgramCounter): Promise> { - throw new Error("Method not implemented."); + machineInit(code: BytesBlob, programCounter: ProgramCounter): Promise> { + // https://graypaper.fluffylabs.dev/#/ab2cdbd/346400346400?v=0.7.2 + const deblobResult = ProgramDecoder.deblob(code.raw); + if (deblobResult.isError) { + return Promise.resolve(Result.error(deblobResult.error, deblobResult.details)); + } + + const innerPvm = new Interpreter({ useSbrkGas: false }); + + innerPvm.resetGeneric(code.raw, Number(programCounter), tryAsGas(0)); + + // https://graypaper.fluffylabs.dev/#/ab2cdbd/348c00348c00?v=0.7.2 + let id = 0n; + while (this.machines.has(id)) { + id++; + } + + const machineId = tryAsMachineId(id); + // https://graypaper.fluffylabs.dev/#/ab2cdbd/340501340b01?v=0.7.2 + this.machines.set(id, innerPvm); + return Promise.resolve(Result.ok(machineId)); } machineInvoke( From 5f32d0be4b69c3bfd6b10c25b2691176a899b740 Mon Sep 17 00:00:00 2001 From: HeyImStas Date: Wed, 25 Mar 2026 16:57:18 +0100 Subject: [PATCH 2/8] self review --- packages/jam/in-core/externalities/refine.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/jam/in-core/externalities/refine.test.ts b/packages/jam/in-core/externalities/refine.test.ts index 24c9fa2c6..ec666b60b 100644 --- a/packages/jam/in-core/externalities/refine.test.ts +++ b/packages/jam/in-core/externalities/refine.test.ts @@ -267,7 +267,7 @@ describe("RefineExternalitiesImpl", () => { it("should accept a non-zero program counter", async () => { const ext = createExt(); const code = BytesBlob.blobFrom(MINIMAL_PROGRAM); - const pc = tryAsProgramCounter(0); + const pc = tryAsProgramCounter(1); const result = await ext.machineInit(code, pc); assert.strictEqual(result.isOk, true); From d2df08923bb1f9544e9e1620e397f6f496cf6a45 Mon Sep 17 00:00:00 2001 From: HeyImStas Date: Wed, 25 Mar 2026 16:59:41 +0100 Subject: [PATCH 3/8] listed pvm-interface --- package-lock.json | 1 + packages/jam/in-core/package.json | 2 ++ 2 files changed, 3 insertions(+) diff --git a/package-lock.json b/package-lock.json index 8f196d996..7ca77eb56 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9163,6 +9163,7 @@ "@typeberry/jam-host-calls": "*", "@typeberry/logger": "*", "@typeberry/numbers": "*", + "@typeberry/pvm-interface": "*", "@typeberry/state": "*", "@typeberry/transition": "*", "@typeberry/utils": "*" diff --git a/packages/jam/in-core/package.json b/packages/jam/in-core/package.json index 1cda131cc..044da9717 100644 --- a/packages/jam/in-core/package.json +++ b/packages/jam/in-core/package.json @@ -18,6 +18,8 @@ "@typeberry/jam-host-calls": "*", "@typeberry/logger": "*", "@typeberry/numbers": "*", + "@typeberry/pvm-interface" : "*", + "@typeberry/pvm-interpreter" : "*", "@typeberry/state": "*", "@typeberry/transition": "*", "@typeberry/utils": "*" From 5399fd9ab4ea4ecb17ab9fcf885a40c03d9da373 Mon Sep 17 00:00:00 2001 From: HeyImStas Date: Wed, 25 Mar 2026 17:02:08 +0100 Subject: [PATCH 4/8] npm i --- package-lock.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package-lock.json b/package-lock.json index 7ca77eb56..11e60f4e6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9164,6 +9164,7 @@ "@typeberry/logger": "*", "@typeberry/numbers": "*", "@typeberry/pvm-interface": "*", + "@typeberry/pvm-interpreter": "*", "@typeberry/state": "*", "@typeberry/transition": "*", "@typeberry/utils": "*" From 931e6ba4841e66a515bbfaa5cb5a8287e2f27347 Mon Sep 17 00:00:00 2001 From: HeyImStas Date: Wed, 25 Mar 2026 17:03:02 +0100 Subject: [PATCH 5/8] qa-fix --- packages/jam/in-core/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/jam/in-core/package.json b/packages/jam/in-core/package.json index 044da9717..3a7d28661 100644 --- a/packages/jam/in-core/package.json +++ b/packages/jam/in-core/package.json @@ -18,8 +18,8 @@ "@typeberry/jam-host-calls": "*", "@typeberry/logger": "*", "@typeberry/numbers": "*", - "@typeberry/pvm-interface" : "*", - "@typeberry/pvm-interpreter" : "*", + "@typeberry/pvm-interface": "*", + "@typeberry/pvm-interpreter": "*", "@typeberry/state": "*", "@typeberry/transition": "*", "@typeberry/utils": "*" From ac31007a6e7a98f1b3b7df5206dcb67227653618 Mon Sep 17 00:00:00 2001 From: HeyImStas Date: Wed, 1 Apr 2026 13:22:10 +0200 Subject: [PATCH 6/8] Using PvmInstanceManager, SortedArray, and using binary search for finding new machineId. Added resetGeneric to IPvmInterpreter --- package-lock.json | 2 + packages/core/pvm-interface/pvm.ts | 3 + .../jam/in-core/externalities/refine.test.ts | 3 +- packages/jam/in-core/externalities/refine.ts | 57 ++++++++++++++----- packages/jam/in-core/in-core.ts | 1 + packages/jam/in-core/package.json | 2 + 6 files changed, 53 insertions(+), 15 deletions(-) diff --git a/package-lock.json b/package-lock.json index 11e60f4e6..d760b1162 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9163,6 +9163,8 @@ "@typeberry/jam-host-calls": "*", "@typeberry/logger": "*", "@typeberry/numbers": "*", + "@typeberry/ordering": "*", + "@typeberry/pvm-host-calls": "*", "@typeberry/pvm-interface": "*", "@typeberry/pvm-interpreter": "*", "@typeberry/state": "*", diff --git a/packages/core/pvm-interface/pvm.ts b/packages/core/pvm-interface/pvm.ts index 1d86ad2cb..5ce94c5e1 100644 --- a/packages/core/pvm-interface/pvm.ts +++ b/packages/core/pvm-interface/pvm.ts @@ -17,6 +17,9 @@ export interface IPvmInterpreter { /** Prepare SPI program to be executed. */ resetJam(program: Uint8Array, args: Uint8Array, pc: number, gas: Gas): void; + /** Prepare a generic (non-SPI) program to be executed. */ + resetGeneric(rawProgram: Uint8Array, pc: number, gas: Gas): void; + /** Execute loaded program. */ runProgram(): void; diff --git a/packages/jam/in-core/externalities/refine.test.ts b/packages/jam/in-core/externalities/refine.test.ts index ec666b60b..bbeb42d9c 100644 --- a/packages/jam/in-core/externalities/refine.test.ts +++ b/packages/jam/in-core/externalities/refine.test.ts @@ -12,7 +12,7 @@ import { } from "@typeberry/block"; import { Bytes, BytesBlob } from "@typeberry/bytes"; import { HashDictionary } from "@typeberry/collections"; -import { tinyChainSpec } from "@typeberry/config"; +import { PvmBackend, tinyChainSpec } from "@typeberry/config"; import { HASH_SIZE } from "@typeberry/hash"; import { SegmentExportError, tryAsMachineId, tryAsProgramCounter } from "@typeberry/jam-host-calls"; import { tryAsU32, tryAsU64 } from "@typeberry/numbers"; @@ -83,6 +83,7 @@ function createExt(overrides: Partial = {}) { currentServiceId: tryAsServiceId(42), lookupState: overrides.lookupState ?? defaultState, exportOffset: overrides.exportOffset ?? 0, + pvmBackend: PvmBackend.BuiltIn, ...overrides, }); } diff --git a/packages/jam/in-core/externalities/refine.ts b/packages/jam/in-core/externalities/refine.ts index 70dca9af6..3f15cde2f 100644 --- a/packages/jam/in-core/externalities/refine.ts +++ b/packages/jam/in-core/externalities/refine.ts @@ -6,6 +6,8 @@ import { tryAsSegmentIndex, } from "@typeberry/block"; import type { BytesBlob } from "@typeberry/bytes"; +import { SortedArray } from "@typeberry/collections"; +import type { PvmBackend } from "@typeberry/config"; import type { Blake2bHash } from "@typeberry/hash"; import { type MachineId, @@ -21,12 +23,25 @@ import { type ZeroVoidError, } from "@typeberry/jam-host-calls"; import type { U64 } from "@typeberry/numbers"; -import type { HostCallMemory, HostCallRegisters } from "@typeberry/pvm-host-calls"; -import { type BigGas, tryAsGas } from "@typeberry/pvm-interface"; -import { Interpreter, ProgramDecoder, type ProgramDecoderError } from "@typeberry/pvm-interpreter"; +import { Ordering } from "@typeberry/ordering"; +import { type HostCallMemory, type HostCallRegisters, PvmInstanceManager } from "@typeberry/pvm-host-calls"; +import { type BigGas, type IPvmInterpreter, tryAsGas } from "@typeberry/pvm-interface"; +import { ProgramDecoder, type ProgramDecoderError } from "@typeberry/pvm-interpreter"; import type { State } from "@typeberry/state"; import { type OK, Result } from "@typeberry/utils"; +type MachineEntry = [MachineId, IPvmInterpreter]; + +const machineComparator = (a: MachineEntry, b: MachineEntry) => { + if (a[0] < b[0]) { + return Ordering.Less; + } + if (a[0] > b[0]) { + return Ordering.Greater; + } + return Ordering.Equal; +}; + /** * Parameters required to create a RefineExternalitiesImpl. */ @@ -37,11 +52,13 @@ export type RefineExternalitiesParams = { lookupState: State; /** Export offset -- sum of exports from prior work items in this package. */ exportOffset: number; + /** PVM backend to use for creating inner PVM instances. */ + pvmBackend: PvmBackend; }; export class RefineExternalitiesImpl implements RefineExternalities { - /** Map of inner PVM instances keyed by MachineId. */ - private machines: Map = new Map(); + /** Inner PVM instances sorted by MachineId. */ + private machines: SortedArray = SortedArray.fromSortedArray(machineComparator); /** Service being refined (used as default for historicalLookup). */ private readonly currentServiceId: ServiceId; /** State at the lookup anchor for preimage lookups. */ @@ -50,6 +67,8 @@ export class RefineExternalitiesImpl implements RefineExternalities { private readonly exportedSegments: Segment[] = []; /** Offset for segment indexing (sum of exports from prior items). */ private readonly exportOffset: number; + /** PVM backend for creating inner machines. */ + private readonly pvmBackend: PvmBackend; static create(params: RefineExternalitiesParams) { return new RefineExternalitiesImpl(params); @@ -59,6 +78,7 @@ export class RefineExternalitiesImpl implements RefineExternalities { this.currentServiceId = params.currentServiceId; this.lookupState = params.lookupState; this.exportOffset = params.exportOffset; + this.pvmBackend = params.pvmBackend; } getExportedSegments(): readonly Segment[] { @@ -106,27 +126,36 @@ export class RefineExternalitiesImpl implements RefineExternalities { throw new Error("Method not implemented."); } - machineInit(code: BytesBlob, programCounter: ProgramCounter): Promise> { + async machineInit(code: BytesBlob, programCounter: ProgramCounter): Promise> { // https://graypaper.fluffylabs.dev/#/ab2cdbd/346400346400?v=0.7.2 const deblobResult = ProgramDecoder.deblob(code.raw); if (deblobResult.isError) { - return Promise.resolve(Result.error(deblobResult.error, deblobResult.details)); + return Result.error(deblobResult.error, deblobResult.details); } - const innerPvm = new Interpreter({ useSbrkGas: false }); + const manager = await PvmInstanceManager.new(this.pvmBackend); + const innerPvm = await manager.getInstance(); innerPvm.resetGeneric(code.raw, Number(programCounter), tryAsGas(0)); // https://graypaper.fluffylabs.dev/#/ab2cdbd/348c00348c00?v=0.7.2 - let id = 0n; - while (this.machines.has(id)) { - id++; + // Binary search for the minimal free MachineId + const arr = this.machines.array; + let low = 0; + let high = arr.length; + while (low < high) { + const mid = (low + high) >> 1; + if (arr[mid][0] > BigInt(mid)) { + high = mid; + } else { + low = mid + 1; + } } - const machineId = tryAsMachineId(id); + const machineId = tryAsMachineId(low); // https://graypaper.fluffylabs.dev/#/ab2cdbd/340501340b01?v=0.7.2 - this.machines.set(id, innerPvm); - return Promise.resolve(Result.ok(machineId)); + this.machines.insert([machineId, innerPvm]); + return Result.ok(machineId); } machineInvoke( diff --git a/packages/jam/in-core/in-core.ts b/packages/jam/in-core/in-core.ts index acbde75e6..9bf2a5dc5 100644 --- a/packages/jam/in-core/in-core.ts +++ b/packages/jam/in-core/in-core.ts @@ -448,6 +448,7 @@ export class InCore { currentServiceId: args.currentServiceId, lookupState: args.lookupState, exportOffset: args.exportOffset, + pvmBackend: this.pvmBackend, }); return { diff --git a/packages/jam/in-core/package.json b/packages/jam/in-core/package.json index 3a7d28661..aa70d66af 100644 --- a/packages/jam/in-core/package.json +++ b/packages/jam/in-core/package.json @@ -18,6 +18,8 @@ "@typeberry/jam-host-calls": "*", "@typeberry/logger": "*", "@typeberry/numbers": "*", + "@typeberry/ordering": "*", + "@typeberry/pvm-host-calls": "*", "@typeberry/pvm-interface": "*", "@typeberry/pvm-interpreter": "*", "@typeberry/state": "*", From 687662e806b048caa3d1ca9814dde9f4df4877b5 Mon Sep 17 00:00:00 2001 From: HeyImStas Date: Mon, 6 Apr 2026 20:01:32 +0200 Subject: [PATCH 7/8] comment nit suggestion --- packages/jam/in-core/externalities/refine.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/jam/in-core/externalities/refine.ts b/packages/jam/in-core/externalities/refine.ts index 3f15cde2f..dc6434650 100644 --- a/packages/jam/in-core/externalities/refine.ts +++ b/packages/jam/in-core/externalities/refine.ts @@ -52,7 +52,10 @@ export type RefineExternalitiesParams = { lookupState: State; /** Export offset -- sum of exports from prior work items in this package. */ exportOffset: number; - /** PVM backend to use for creating inner PVM instances. */ + /** + * PVM backend to use for creating inner PVM instances. + * NIT: Could accept PVMInstanceManager + */ pvmBackend: PvmBackend; }; From 15bf66ea2dc12997593862f375d2526406188fad Mon Sep 17 00:00:00 2001 From: HeyImStas Date: Mon, 6 Apr 2026 20:03:24 +0200 Subject: [PATCH 8/8] qa-fix --- packages/jam/in-core/externalities/refine.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/jam/in-core/externalities/refine.ts b/packages/jam/in-core/externalities/refine.ts index dc6434650..65f13438c 100644 --- a/packages/jam/in-core/externalities/refine.ts +++ b/packages/jam/in-core/externalities/refine.ts @@ -53,9 +53,9 @@ export type RefineExternalitiesParams = { /** Export offset -- sum of exports from prior work items in this package. */ exportOffset: number; /** - * PVM backend to use for creating inner PVM instances. - * NIT: Could accept PVMInstanceManager - */ + * PVM backend to use for creating inner PVM instances. + * NIT: Could accept PVMInstanceManager + */ pvmBackend: PvmBackend; };