Skip to content

Commit

Permalink
Add ecalli handling for other pvms
Browse files Browse the repository at this point in the history
  • Loading branch information
krystian50 committed Jan 9, 2025
1 parent 1e2d1ce commit 4aa914d
Show file tree
Hide file tree
Showing 2 changed files with 116 additions and 42 deletions.
111 changes: 94 additions & 17 deletions src/packages/web-worker/command-handlers/host-call.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { WriteAccounts } from "@/packages/host-calls/write";
import { isInternalPvm } from "../utils";
import { ReadAccounts } from "@/packages/host-calls/read";
import { tryAsServiceId } from "@typeberry/block";
import { Memory, MemoryIndex, SbrkIndex, tryAsGas } from "@typeberry/pvm-debugger-adapter";

type HostCallParams = {
pvm: PvmApiInterface | null;
Expand All @@ -26,9 +27,96 @@ type HostCallResponse =
error?: unknown;
};

type ExecuteParams = Parameters<write.Write["execute"]>;
type RegistersType = ExecuteParams[1];
type MemoryType = ExecuteParams[2];
// [KrFr] Gas counter is not used. Can be mocked for now.
const getGasCounter = (pvm: PvmApiInterface) => {
if (isInternalPvm(pvm)) {
return pvm.getInterpreter().getGasCounter();
}

return {
get: () => {
return tryAsGas(10_000);
},
set: () => {},
sub: () => {
return false;
},
};
};

const getRegisters = (pvm: PvmApiInterface) => {
if (isInternalPvm(pvm)) {
return pvm.getInterpreter().getRegisters();
}

return {
getU32: (registerIndex: number) => {
return pvm.getRegisters()[registerIndex];
},
setU32: (registerIndex: number, value: number) => {
const registers = pvm.getRegisters();
registers[registerIndex] = value;
pvm.setRegisters(registers);
},

// [KrFr] The following functions are not used in the read and write host call handlers. Can be mocked for now.
/* eslint-disable @typescript-eslint/no-unused-vars */
getI32: (_registerIndex: number) => {
return 0;
},
setI32: (_registerIndex: number, _value: number) => {},

getU64: (_registerIndex: number) => {
return BigInt(0);
},
getI64: (_registerIndex: number) => {
return BigInt(0);
},
setU64: (_registerIndex: number, _value: bigint) => {},
setI64: (_registerIndex: number, _value: bigint) => {},
/* eslint-enable @typescript-eslint/no-unused-vars */
};
};

const getMemory = (pvm: PvmApiInterface) => {
if (isInternalPvm(pvm)) {
return pvm.getInterpreter().getMemory();
}

return {
loadInto: (result: Uint8Array, startAddress: MemoryIndex) => {

Check failure on line 87 in src/packages/web-worker/command-handlers/host-call.ts

View workflow job for this annotation

GitHub Actions / build (22.x)

'result' is declared but its value is never read.
result = pvm.getPageDump(startAddress);
return null;
},
// eslint-disable-next-line @typescript-eslint/no-unused-vars
isWriteable: (_destinationStart: MemoryIndex, _length: number) => {
return true; // Simulates that the memory slice is writeable
},
storeFrom: (address: MemoryIndex, bytes: Uint8Array) => {
pvm.setMemory(address, bytes);
return null;
},

// [KrFr] The following functions are not used in the read and write host call handlers. Can be mocked for now.
/* eslint-disable @typescript-eslint/no-unused-vars */
fromInitialMemory: () => {
return new Memory(); // Returns the mock itself for simplicity
},
reset: () => {},
copyFrom: (_memory: Memory) => {},
sbrk: (_length: number) => {
return 0 as SbrkIndex; // Simulates returning the current sbrk index
},
getPageDump: (_pageNumber: number) => {
return null; // Simulates no page dump available
},
getDirtyPages: function* () {
// Simulates an empty iterator
yield* [];
},
/* eslint-enable @typescript-eslint/no-unused-vars */
};
};

const hostCall = async ({
pvm,
Expand All @@ -41,32 +129,21 @@ const hostCall = async ({
storage: Storage;
serviceId: number;
}): Promise<HostCallResponse> => {
if (!isInternalPvm(pvm)) {
return { hostCallIdentifier, status: CommandStatus.ERROR, error: new Error("External PVM not supported") };
}

if (hostCallIdentifier === HostCallIdentifiers.READ) {
const readAccounts = new ReadAccounts(storage);
const jamHostCall = new read.Read(readAccounts);
// TODO the types are the same, but exported from different packages and lost track of the type
jamHostCall.currentServiceId = tryAsServiceId(serviceId) as unknown as typeof jamHostCall.currentServiceId;

await jamHostCall.execute(
pvm.getInterpreter().getGasCounter(),
pvm.getInterpreter().getRegisters() as unknown as RegistersType,
pvm.getInterpreter().getMemory() as unknown as MemoryType,
);
await jamHostCall.execute(getGasCounter(pvm), getRegisters(pvm), getMemory(pvm));

return { hostCallIdentifier, status: CommandStatus.SUCCESS };
} else if (hostCallIdentifier === HostCallIdentifiers.WRITE) {
const writeAccounts = new WriteAccounts(storage);
const jamHostCall = new write.Write(writeAccounts);

await jamHostCall.execute(
pvm.getInterpreter().getGasCounter(),
pvm.getInterpreter().getRegisters() as unknown as RegistersType,
pvm.getInterpreter().getMemory() as unknown as MemoryType,
);
await jamHostCall.execute(getGasCounter(pvm), getRegisters(pvm), getMemory(pvm));

return { hostCallIdentifier, storage, status: CommandStatus.SUCCESS };
}

Expand Down
47 changes: 22 additions & 25 deletions src/store/workers/workersSlice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -288,33 +288,30 @@ export const handleHostCall = createAsyncThunk("workers/handleHostCall", async (
}

await Promise.all(
state.workers
// [KF] Remove or change this condition when we support host calls on more PVMs.
.filter(({ id }) => id === "typeberry")
.map(async (worker) => {
const resp = await asyncWorkerPostMessage(worker.id, worker.worker, {
command: Commands.HOST_CALL,
payload: { hostCallIdentifier: worker.exitArg as HostCallIdentifiers },
});

if (
resp.payload.hostCallIdentifier === HostCallIdentifiers.WRITE &&
resp.payload.storage &&
// Remove if we decide to make storage initialization optional
state.debugger.storage
) {
const newStorage = mergePVMAndDebuggerEcalliStorage(resp.payload.storage, state.debugger.storage);
dispatch(setStorage(newStorage));
}
state.workers.map(async (worker) => {
const resp = await asyncWorkerPostMessage(worker.id, worker.worker, {
command: Commands.HOST_CALL,
payload: { hostCallIdentifier: worker.exitArg as HostCallIdentifiers },
});

if ((getState() as RootState).debugger.isRunMode) {
dispatch(continueAllWorkers());
}
if (
resp.payload.hostCallIdentifier === HostCallIdentifiers.WRITE &&
resp.payload.storage &&
// Remove if we decide to make storage initialization optional
state.debugger.storage
) {
const newStorage = mergePVMAndDebuggerEcalliStorage(resp.payload.storage, state.debugger.storage);
dispatch(setStorage(newStorage));
}

if (hasCommandStatusError(resp)) {
throw new Error(resp.error.message);
}
}),
if ((getState() as RootState).debugger.isRunMode) {
dispatch(continueAllWorkers());
}

if (hasCommandStatusError(resp)) {
throw new Error(resp.error.message);
}
}),
);

if (selectShouldContinueRunning(state)) {
Expand Down

0 comments on commit 4aa914d

Please sign in to comment.