@metamask/ocap-kernel
Version:
OCap kernel core components
297 lines • 14.6 kB
JavaScript
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
if (kind === "m") throw new TypeError("Private method is not writable");
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
};
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
};
var _VatHandle_instances, _VatHandle_vatStream, _VatHandle_logger, _VatHandle_kernelStore, _VatHandle_vatStore, _VatHandle_vatSyscall, _VatHandle_kernelQueue, _VatHandle_rpcClient, _VatHandle_rpcService, _VatHandle_init, _VatHandle_handleMessage, _VatHandle_getDeliveryCrankResults;
import { VatDeletedError, StreamReadError } from "@metamask/kernel-errors";
import { RpcClient, RpcService } from "@metamask/kernel-rpc-methods";
import { Logger } from "@metamask/logger";
import { isJsonRpcNotification, isJsonRpcResponse } from "@metamask/utils";
import { vatMethodSpecs, vatSyscallHandlers } from "./rpc/index.mjs";
import { kser, makeError } from "./services/kernel-marshal.mjs";
import { VatSyscall } from "./VatSyscall.mjs";
export class VatHandle {
/**
* Construct a new VatHandle instance.
*
* @param params - Named constructor parameters.
* @param params.vatId - Our vat ID.
* @param params.vatConfig - The configuration for this vat.
* @param params.vatStream - Communications channel connected to the vat worker.
* @param params.kernelStore - The kernel's persistent state store.
* @param params.kernelQueue - The kernel's queue.
* @param params.logger - Optional logger for error and diagnostic output.
*/
// eslint-disable-next-line no-restricted-syntax
constructor({ vatId, vatConfig, vatStream, kernelStore, kernelQueue, logger, }) {
_VatHandle_instances.add(this);
/** Communications channel to and from the vat itself */
_VatHandle_vatStream.set(this, void 0);
/** Logger for outputting messages (such as errors) to the console */
_VatHandle_logger.set(this, void 0);
/** Storage holding the kernel's persistent state */
_VatHandle_kernelStore.set(this, void 0);
/** Storage holding this vat's persistent state */
_VatHandle_vatStore.set(this, void 0);
/** The vat's syscall */
_VatHandle_vatSyscall.set(this, void 0);
/** The kernel's queue */
_VatHandle_kernelQueue.set(this, void 0);
_VatHandle_rpcClient.set(this, void 0);
_VatHandle_rpcService.set(this, void 0);
this.vatId = vatId;
this.config = vatConfig;
__classPrivateFieldSet(this, _VatHandle_logger, logger ?? new Logger(`[vat ${vatId}]`), "f");
__classPrivateFieldSet(this, _VatHandle_vatStream, vatStream, "f");
__classPrivateFieldSet(this, _VatHandle_kernelStore, kernelStore, "f");
__classPrivateFieldSet(this, _VatHandle_vatStore, kernelStore.makeVatStore(vatId), "f");
__classPrivateFieldSet(this, _VatHandle_kernelQueue, kernelQueue, "f");
__classPrivateFieldSet(this, _VatHandle_vatSyscall, new VatSyscall({
vatId,
kernelQueue,
kernelStore,
logger: __classPrivateFieldGet(this, _VatHandle_logger, "f").subLogger({ tags: ['syscall'] }),
}), "f");
__classPrivateFieldSet(this, _VatHandle_rpcClient, new RpcClient(vatMethodSpecs, async (request) => {
await __classPrivateFieldGet(this, _VatHandle_vatStream, "f").write(request);
}, `${this.vatId}:`), "f");
__classPrivateFieldSet(this, _VatHandle_rpcService, new RpcService(vatSyscallHandlers, {
handleSyscall: (params) => {
__classPrivateFieldGet(this, _VatHandle_vatSyscall, "f").handleSyscall(params);
},
}), "f");
}
/**
* Create a new VatHandle instance.
*
* @param params - Named constructor parameters.
* @param params.vatId - Our vat ID.
* @param params.vatConfig - The configuration for this vat.
* @param params.vatStream - Communications channel connected to the vat worker.
* @param params.kernelStore - The kernel's persistent state store.
* @param params.kernelQueue - The kernel's queue.
* @param params.logger - Optional logger for error and diagnostic output.
* @returns A promise for the new VatHandle instance.
*/
static async make(params) {
const vat = new VatHandle(params);
await __classPrivateFieldGet(vat, _VatHandle_instances, "m", _VatHandle_init).call(vat);
return vat;
}
/**
* Ping the vat.
*
* @returns A promise that resolves to the result of the ping.
*/
async ping() {
return await this.sendVatCommand({
method: 'ping',
params: [],
});
}
/**
* Make a 'message' delivery to the vat.
*
* @param target - The VRef of the object to which the message is addressed.
* @param message - The message to deliver.
* @returns The crank results.
*/
async deliverMessage(target, message) {
await this.sendVatCommand({
method: 'deliver',
params: ['message', target, message],
});
return __classPrivateFieldGet(this, _VatHandle_instances, "m", _VatHandle_getDeliveryCrankResults).call(this);
}
/**
* Make a 'notify' delivery to the vat.
*
* @param resolutions - One or more promise resolutions to deliver.
* @returns The crank results.
*/
async deliverNotify(resolutions) {
await this.sendVatCommand({
method: 'deliver',
params: ['notify', resolutions],
});
return __classPrivateFieldGet(this, _VatHandle_instances, "m", _VatHandle_getDeliveryCrankResults).call(this);
}
/**
* Make a 'dropExports' delivery to the vat.
*
* @param vrefs - The VRefs of the exports to be dropped.
* @returns The crank results.
*/
async deliverDropExports(vrefs) {
await this.sendVatCommand({
method: 'deliver',
params: ['dropExports', vrefs],
});
return __classPrivateFieldGet(this, _VatHandle_instances, "m", _VatHandle_getDeliveryCrankResults).call(this);
}
/**
* Make a 'retireExports' delivery to the vat.
*
* @param vrefs - The VRefs of the exports to be retired.
* @returns The crank results.
*/
async deliverRetireExports(vrefs) {
await this.sendVatCommand({
method: 'deliver',
params: ['retireExports', vrefs],
});
return __classPrivateFieldGet(this, _VatHandle_instances, "m", _VatHandle_getDeliveryCrankResults).call(this);
}
/**
* Make a 'retireImports' delivery to the vat.
*
* @param vrefs - The VRefs of the imports to be retired.
* @returns The crank results.
*/
async deliverRetireImports(vrefs) {
await this.sendVatCommand({
method: 'deliver',
params: ['retireImports', vrefs],
});
return __classPrivateFieldGet(this, _VatHandle_instances, "m", _VatHandle_getDeliveryCrankResults).call(this);
}
/**
* Make a 'bringOutYourDead' delivery to the vat.
*
* @returns The crank results.
*/
async deliverBringOutYourDead() {
await this.sendVatCommand({
method: 'deliver',
params: ['bringOutYourDead'],
});
return __classPrivateFieldGet(this, _VatHandle_instances, "m", _VatHandle_getDeliveryCrankResults).call(this);
}
/**
* Terminates the vat.
*
* @param terminating - If true, the vat is being killed permanently, so clean
* up its state and reject any promises that would be left dangling.
* @param error - The error to terminate the vat with.
*/
async terminate(terminating, error) {
await __classPrivateFieldGet(this, _VatHandle_vatStream, "f").end(error);
const terminationError = error ?? new VatDeletedError(this.vatId);
if (terminating) {
// Reject promises exported to other vats for which this vat is the decider
const failure = kser(terminationError);
for (const kpid of __classPrivateFieldGet(this, _VatHandle_kernelStore, "f").getPromisesByDecider(this.vatId)) {
__classPrivateFieldGet(this, _VatHandle_kernelQueue, "f").resolvePromises(this.vatId, [[kpid, true, failure]]);
}
__classPrivateFieldGet(this, _VatHandle_rpcClient, "f").rejectAll(terminationError);
__classPrivateFieldGet(this, _VatHandle_kernelStore, "f").deleteVat(this.vatId);
}
}
/**
* Send a command into the vat.
*
* @param payload - The payload of the command.
* @param payload.method - The method to call.
* @param payload.params - The parameters to pass to the method.
* @returns A promise that resolves the response to the command.
*/
async sendVatCommand({ method, params, }) {
const result = await __classPrivateFieldGet(this, _VatHandle_rpcClient, "f").call(method, params);
if (method === 'initVat' || method === 'deliver') {
const [[sets, deletes], deliveryError] = result;
__classPrivateFieldGet(this, _VatHandle_vatSyscall, "f").deliveryError = deliveryError ?? undefined;
const noErrors = !deliveryError && !__classPrivateFieldGet(this, _VatHandle_vatSyscall, "f").illegalSyscall;
// On errors, we neither update this vat's KV data nor rollback previous changes.
// This is safe because vats are always terminated when errors occur
// and they have their own databases, which are deleted when the vat is terminated.
// The main kernel database will be rolled back.
if (noErrors) {
__classPrivateFieldGet(this, _VatHandle_vatStore, "f").updateKVData(sets, deletes);
}
}
return result;
}
}
_VatHandle_vatStream = new WeakMap(), _VatHandle_logger = new WeakMap(), _VatHandle_kernelStore = new WeakMap(), _VatHandle_vatStore = new WeakMap(), _VatHandle_vatSyscall = new WeakMap(), _VatHandle_kernelQueue = new WeakMap(), _VatHandle_rpcClient = new WeakMap(), _VatHandle_rpcService = new WeakMap(), _VatHandle_instances = new WeakSet(), _VatHandle_init =
/**
* Initializes the vat.
*
* @returns A promise that resolves when the vat is initialized.
*/
async function _VatHandle_init() {
Promise.all([__classPrivateFieldGet(this, _VatHandle_vatStream, "f").drain(__classPrivateFieldGet(this, _VatHandle_instances, "m", _VatHandle_handleMessage).bind(this))]).catch(async (error) => {
__classPrivateFieldGet(this, _VatHandle_logger, "f").error(`Unexpected read error`, error);
await this.terminate(true, new StreamReadError({ vatId: this.vatId }, error));
});
await this.sendVatCommand({
method: 'initVat',
params: {
vatConfig: this.config,
state: __classPrivateFieldGet(this, _VatHandle_vatStore, "f").getKVData(),
},
});
}, _VatHandle_handleMessage =
/**
* Handle a message from the vat.
*
* @param message - The message to handle.
* @param message.id - The id of the message.
* @param message.payload - The payload (i.e., the message itself) to handle.
*/
async function _VatHandle_handleMessage(message) {
if (isJsonRpcResponse(message)) {
__classPrivateFieldGet(this, _VatHandle_rpcClient, "f").handleResponse(message.id, message);
}
else if (isJsonRpcNotification(message)) {
__classPrivateFieldGet(this, _VatHandle_rpcService, "f").assertHasMethod(message.method);
await __classPrivateFieldGet(this, _VatHandle_rpcService, "f").execute(message.method, message.params);
}
else {
// We don't expect any JSON-RPC requests from the vat, but the stream may permit them
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
throw new Error(`Received unexpected message: ${message}`);
}
}, _VatHandle_getDeliveryCrankResults =
/**
* Get the crank outcome for a given checkpoint result.
*
* @returns The crank outcome.
*/
async function _VatHandle_getDeliveryCrankResults() {
const results = {
didDelivery: this.vatId,
};
// These conditionals express a priority order: the consequences of an
// illegal syscall take precedence over a vat requesting termination, etc.
if (__classPrivateFieldGet(this, _VatHandle_vatSyscall, "f").illegalSyscall) {
results.abort = true;
const { info } = __classPrivateFieldGet(this, _VatHandle_vatSyscall, "f").illegalSyscall;
// TODO: For now, vat errors both rewind changes and terminate the vat.
// Some day, they might rewind changes and retry the syscall.
// We should terminate the vat only after a certain # of failed retries.
results.terminate = { vatId: this.vatId, reject: true, info };
}
else if (__classPrivateFieldGet(this, _VatHandle_vatSyscall, "f").deliveryError) {
results.abort = true;
const info = makeError(__classPrivateFieldGet(this, _VatHandle_vatSyscall, "f").deliveryError);
results.terminate = { vatId: this.vatId, reject: true, info };
}
else if (__classPrivateFieldGet(this, _VatHandle_vatSyscall, "f").vatRequestedTermination) {
if (__classPrivateFieldGet(this, _VatHandle_vatSyscall, "f").vatRequestedTermination.reject) {
results.abort = true; // vatPowers.exitWithFailure wants rewind
}
results.terminate = {
vatId: this.vatId,
...__classPrivateFieldGet(this, _VatHandle_vatSyscall, "f").vatRequestedTermination,
};
}
return harden(results);
};
//# sourceMappingURL=VatHandle.mjs.map