UNPKG

@metamask/ocap-kernel

Version:
300 lines 14.7 kB
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); const [, deliveryError] = await __classPrivateFieldGet(vat, _VatHandle_instances, "m", _VatHandle_init).call(vat); if (deliveryError) { throw new Error(`Failed to initialize vat ${vat.vatId}: ${deliveryError}`); } 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 for the vat's initial delivery result. */ 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)); }); return 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