@metamask/ocap-kernel
Version:
OCap kernel core components
200 lines • 7.39 kB
JavaScript
import { coerceMessage } from "../../types.mjs";
import { getCListMethods } from "./clist.mjs";
import { getVatMethods } from "./vat.mjs";
import { Fail } from "../../utils/assert.mjs";
/**
* Create a translator object that provides functionality for translating
* references and messages between kernel and vat spaces.
*
* @param ctx - The store context.
* @returns A translator object that maps various kernel data structures
* onto `kv`.
*/
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
export function getTranslators(ctx) {
const { krefToEref, erefToKref, allocateErefForKref } = getCListMethods(ctx);
const { exportFromVat } = getVatMethods(ctx);
/**
* Translate a reference from kernel space into vat space.
*
* @param vatId - The vat for whom translation is desired.
* @param kref - The KRef of the entity of interest.
* @param importIfNeeded - If true, allocate a new clist entry if necessary;
* if false, require that such an entry already exist.
*
* @returns the VRef corresponding to `kref` in `vatId`.
*/
function translateRefKtoV(vatId, kref, importIfNeeded) {
let eref = krefToEref(vatId, kref);
if (!eref) {
if (importIfNeeded) {
eref = allocateErefForKref(vatId, kref);
}
else {
throw Fail `unmapped kref ${kref} vat=${vatId}`;
}
}
return eref;
}
/**
* Translate a capdata object from kernel space into vat space.
*
* @param vatId - The vat for whom translation is desired.
* @param capdata - The object to be translated.
*
* @returns a translated copy of `capdata` intelligible to `vatId`.
*/
function translateCapDataKtoV(vatId, capdata) {
const slots = [];
for (const slot of capdata.slots) {
slots.push(translateRefKtoV(vatId, slot, true));
}
return { body: capdata.body, slots };
}
/**
* Translate a message from kernel space into vat space.
*
* @param vatId - The vat for whom translation is desired.
* @param message - The message to be translated.
*
* @returns a translated copy of `message` intelligible to `vatId`.
*/
function translateMessageKtoV(vatId, message) {
const methargs = translateCapDataKtoV(vatId, message.methargs);
const result = message.result
? translateRefKtoV(vatId, message.result, true)
: message.result;
const vatMessage = coerceMessage({ ...message, methargs, result });
return vatMessage;
}
/**
* Translate a reference from vat space into kernel space.
*
* @param vatId - The vat for whom translation is desired.
* @param vref - The VRef of the entity of interest.
*
* @returns the KRef corresponding to `vref` in this vat.
*/
function translateRefVtoK(vatId, vref) {
let kref = erefToKref(vatId, vref);
kref ?? (kref = exportFromVat(vatId, vref));
return kref;
}
/**
* Translate a capdata object from vat space into kernel space.
*
* @param vatId - The vat for whom translation is desired.
* @param capdata - The object to be translated.
*
* @returns a translated copy of `capdata` intelligible to the kernel.
*/
function translateCapDataVtoK(vatId, capdata) {
const slots = [];
for (const slot of capdata.slots) {
slots.push(translateRefVtoK(vatId, slot));
}
return { body: capdata.body, slots };
}
/**
* Translate a message from vat space into kernel space.
*
* @param vatId - The vat for whom translation is desired.
* @param message - The message to be translated.
*
* @returns a translated copy of `message` intelligible to the kernel.
*/
function translateMessageVtoK(vatId, message) {
const methargs = translateCapDataVtoK(vatId, message.methargs);
if (typeof message.result !== 'string') {
throw TypeError(`message result must be a string`);
}
const result = translateRefVtoK(vatId, message.result);
return { methargs, result };
}
/**
* Translate a syscall from vat space into kernel space.
*
* @param vatId - The vat for whom translation is desired.
* @param vso - The syscall object to be translated.
*
* @returns a translated copy of `vso` intelligible to the kernel.
*/
function translateSyscallVtoK(vatId, vso) {
let kso;
switch (vso[0]) {
case 'send': {
// [VRef, Message];
const [op, target, message] = vso;
kso = [
op,
translateRefVtoK(vatId, target),
// @ts-expect-error: Agoric's Message type has the property `result: string | undefined | null`.
// Ours is `result?: string | null`. We can safely ignore the `undefined` case.
translateMessageVtoK(vatId, coerceMessage(message)),
];
break;
}
case 'subscribe': {
// [VRef];
const [op, promise] = vso;
kso = [op, translateRefVtoK(vatId, promise)];
break;
}
case 'resolve': {
// [VatOneResolution[]];
const [op, resolutions] = vso;
const kResolutions = resolutions.map((resolution) => {
const [vpid, rejected, data] = resolution;
return [
translateRefVtoK(vatId, vpid),
rejected,
translateCapDataVtoK(vatId, data),
];
});
kso = [op, kResolutions];
break;
}
case 'exit': {
// [boolean, SwingSetCapData];
const [op, isFailure, info] = vso;
kso = [
op,
isFailure,
translateCapDataVtoK(vatId, info),
];
break;
}
case 'dropImports':
case 'retireImports':
case 'retireExports':
case 'abandonExports': {
// [VRef[]];
const [op, vrefs] = vso;
const krefs = vrefs.map((ref) => translateRefVtoK(vatId, ref));
kso = [op, krefs];
break;
}
case 'callNow':
case 'vatstoreGet':
case 'vatstoreGetNextKey':
case 'vatstoreSet':
case 'vatstoreDelete': {
const [op] = vso;
throw Error(`vat ${vatId} issued invalid syscall ${op}`);
}
default: {
// Compile-time exhaustiveness check
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
throw Error(`vat ${vatId} issued unknown syscall ${vso[0]}`);
}
}
return kso;
}
return {
translateRefKtoV,
translateCapDataKtoV,
translateMessageKtoV,
translateSyscallVtoK,
};
}
//# sourceMappingURL=translators.mjs.map