Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions packages/core/pvm-interface/pvm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
66 changes: 64 additions & 2 deletions packages/jam/in-core/externalities/refine.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,15 @@ 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 } 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);
}
Expand Down Expand Up @@ -81,6 +83,7 @@ function createExt(overrides: Partial<RefineExternalitiesParams> = {}) {
currentServiceId: tryAsServiceId(42),
lookupState: overrides.lookupState ?? defaultState,
exportOffset: overrides.exportOffset ?? 0,
pvmBackend: PvmBackend.BuiltIn,
...overrides,
});
}
Expand Down Expand Up @@ -212,4 +215,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(1);

const result = await ext.machineInit(code, pc);
assert.strictEqual(result.isOk, true);
});
});
});
64 changes: 59 additions & 5 deletions packages/jam/in-core/externalities/refine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -17,15 +19,29 @@ 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 { 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.
*/
Expand All @@ -36,9 +52,16 @@ 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.
* NIT: Could accept PVMInstanceManager
*/
pvmBackend: PvmBackend;
};

export class RefineExternalitiesImpl implements RefineExternalities {
/** Inner PVM instances sorted by MachineId. */
private machines: SortedArray<MachineEntry> = SortedArray.fromSortedArray(machineComparator);
/** Service being refined (used as default for historicalLookup). */
private readonly currentServiceId: ServiceId;
/** State at the lookup anchor for preimage lookups. */
Expand All @@ -47,6 +70,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);
Expand All @@ -56,6 +81,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[] {
Expand Down Expand Up @@ -103,8 +129,36 @@ export class RefineExternalitiesImpl implements RefineExternalities {
throw new Error("Method not implemented.");
}

machineInit(_code: BytesBlob, _programCounter: ProgramCounter): Promise<Result<MachineId, ProgramDecoderError>> {
throw new Error("Method not implemented.");
async machineInit(code: BytesBlob, programCounter: ProgramCounter): Promise<Result<MachineId, ProgramDecoderError>> {
// https://graypaper.fluffylabs.dev/#/ab2cdbd/346400346400?v=0.7.2
const deblobResult = ProgramDecoder.deblob(code.raw);
if (deblobResult.isError) {
return Result.error(deblobResult.error, deblobResult.details);
}

const manager = await PvmInstanceManager.new(this.pvmBackend);
Comment thread
tomusdrw marked this conversation as resolved.
const innerPvm = await manager.getInstance();

innerPvm.resetGeneric(code.raw, Number(programCounter), tryAsGas(0));

// https://graypaper.fluffylabs.dev/#/ab2cdbd/348c00348c00?v=0.7.2
// 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;
}
}
Comment thread
tomusdrw marked this conversation as resolved.

const machineId = tryAsMachineId(low);
// https://graypaper.fluffylabs.dev/#/ab2cdbd/340501340b01?v=0.7.2
this.machines.insert([machineId, innerPvm]);
return Result.ok(machineId);
Comment thread
tomusdrw marked this conversation as resolved.
}

machineInvoke(
Expand Down
1 change: 1 addition & 0 deletions packages/jam/in-core/in-core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -442,6 +442,7 @@ export class InCore {
currentServiceId: args.currentServiceId,
lookupState: args.lookupState,
exportOffset: args.exportOffset,
pvmBackend: this.pvmBackend,
});

return {
Expand Down
4 changes: 4 additions & 0 deletions packages/jam/in-core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@
"@typeberry/jam-host-calls": "*",
"@typeberry/logger": "*",
"@typeberry/numbers": "*",
"@typeberry/ordering": "*",
"@typeberry/pvm-host-calls": "*",
"@typeberry/pvm-interface": "*",
"@typeberry/pvm-interpreter": "*",
"@typeberry/state": "*",
"@typeberry/transition": "*",
"@typeberry/utils": "*"
Expand Down
Loading