From 69f169ea0f80d6e5ecbe3130ef4542ceda27921f Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Thu, 18 Apr 2024 11:07:52 -0700 Subject: [PATCH 1/4] refactor(daemon): Dry up preformulate --- packages/daemon/src/daemon.js | 68 ++++++++++++----------------------- 1 file changed, 22 insertions(+), 46 deletions(-) diff --git a/packages/daemon/src/daemon.js b/packages/daemon/src/daemon.js index d7a831c6e2..a5c3dfbdff 100644 --- a/packages/daemon/src/daemon.js +++ b/packages/daemon/src/daemon.js @@ -134,46 +134,31 @@ const makeDaemonCore = async ( const localNodeId = deriveId('node', rootEntropy, cryptoPowers.makeSha512()); console.log('Node', localNodeId); - const knownPeersFormulaNumber = deriveId( - 'peers', - rootEntropy, - cryptoPowers.makeSha512(), - ); - const knownPeersId = formatId({ - number: knownPeersFormulaNumber, - node: localNodeId, - }); - await persistencePowers.writeFormula(knownPeersFormulaNumber, { - type: 'known-peers-store', - }); + /** + * @param {string} derivation + * @param {Formula} formula + */ + const preformulate = async (derivation, formula) => { + const formulaId = deriveId( + derivation, + rootEntropy, + cryptoPowers.makeSha512(), + ); + const id = formatId({ + number: formulaId, + node: localNodeId, + }); + await persistencePowers.writeFormula(formulaId, formula); + return { id, formulaId }; + }; - // Prepare least authority formula - const leastAuthorityFormulaNumber = deriveId( - 'none', - rootEntropy, - cryptoPowers.makeSha512(), - ); - const leastAuthorityId = formatId({ - number: leastAuthorityFormulaNumber, - node: localNodeId, + const { id: knownPeersId } = await preformulate('peers', { + type: 'known-peers-store', }); - await persistencePowers.writeFormula(leastAuthorityFormulaNumber, { + const { id: leastAuthorityId } = await preformulate('least-authority', { type: 'least-authority', }); - - // Prepare main worker formula - const mainWorkerFormulaNumber = deriveId( - 'main', - rootEntropy, - cryptoPowers.makeSha512(), - ); - const mainWorkerId = formatId({ - number: mainWorkerFormulaNumber, - node: localNodeId, - }); - await persistencePowers.writeFormula(mainWorkerFormulaNumber, { - type: 'worker', - }); + const { id: mainWorkerId } = await preformulate('main', { type: 'worker' }); /** @type {Builtins} */ const builtins = { @@ -186,16 +171,7 @@ const makeDaemonCore = async ( await Promise.all( Object.entries(specials).map(async ([specialName, makeFormula]) => { const formula = makeFormula(builtins); - const formulaNumber = deriveId( - specialName, - rootEntropy, - cryptoPowers.makeSha512(), - ); - const id = formatId({ - number: formulaNumber, - node: localNodeId, - }); - await persistencePowers.writeFormula(formulaNumber, formula); + const { id } = await preformulate(specialName, formula); return [specialName, id]; }), ), From cc58f77d458ef04e356a33747def2b3a4f93ce51 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Thu, 18 Apr 2024 13:17:13 -0700 Subject: [PATCH 2/4] refactor(daemon): Consolidate interface definitions --- packages/daemon/src/daemon.js | 148 ++++++++++------------- packages/daemon/src/directory.js | 21 ++-- packages/daemon/src/guest.js | 22 ++-- packages/daemon/src/host.js | 23 ++-- packages/daemon/src/interfaces.js | 100 +++++++++++++++ packages/daemon/src/interfaces/web.js | 9 ++ packages/daemon/src/mail.js | 65 +++------- packages/daemon/src/reader-ref.js | 57 +++++---- packages/daemon/src/types.d.ts | 4 +- packages/daemon/src/web-page.js | 42 +++---- packages/daemon/src/worker.js | 122 +++++++++---------- packages/daemon/test/context-consumer.js | 30 ++--- 12 files changed, 348 insertions(+), 295 deletions(-) create mode 100644 packages/daemon/src/interfaces.js create mode 100644 packages/daemon/src/interfaces/web.js diff --git a/packages/daemon/src/daemon.js b/packages/daemon/src/daemon.js index a5c3dfbdff..57e3ceec7a 100644 --- a/packages/daemon/src/daemon.js +++ b/packages/daemon/src/daemon.js @@ -4,7 +4,6 @@ /* global setTimeout, clearTimeout */ import { makeExo } from '@endo/exo'; -import { M } from '@endo/patterns'; import { E, Far } from '@endo/far'; import { makePromiseKit } from '@endo/promise-kit'; import { q } from '@endo/errors'; @@ -22,6 +21,18 @@ import { makeWeakMultimap } from './multimap.js'; import { makeLoopbackNetwork } from './networks/loopback.js'; import { assertValidFormulaType } from './formula-type.js'; +// Sorted: +import { + DaemonFacetForWorkerInterface, + GuestInterface, + InspectorHubInterface, + InspectorInterface, + InvitationInterface, + WorkerInterface, + BlobInterface, + EndoInterface, +} from './interfaces.js'; + /** @import { ERef, FarRef } from '@endo/eventual-send' */ /** @import { PromiseKit } from '@endo/promise-kit' */ /** @import { Builtins, Context, Controller, DaemonCore, DaemonCoreExternal, DaemonicPowers, DeferredTasks, DirectoryFormula, EndoBootstrap, EndoDirectory, EndoFormula, EndoGateway, EndoGreeter, EndoGuest, EndoHost, EndoInspector, EndoNetwork, EndoPeer, EndoReadable, EndoWorker, EvalFormula, FarContext, Formula, FormulaMakerTable, FormulateResult, GuestFormula, HandleFormula, HostFormula, Invitation, InvitationDeferredTaskParams, InvitationFormula, KnownEndoInspectors, LookupFormula, LoopbackNetworkFormula, MakeBundleFormula, MakeCapletDeferredTaskParams, MakeUnconfinedFormula, PeerFormula, PeerInfo, PetInspectorFormula, PetStore, PetStoreFormula, Provide, ReadableBlobFormula, Sha512, Specials, WeakMultimap, WorkerDaemonFacet, WorkerFormula, } from './types.js' */ @@ -51,23 +62,15 @@ const delay = async (ms, cancelled) => { * @returns {EndoInspector} The inspector for the given formula. */ const makeInspector = (type, number, record) => - makeExo( - `Inspector (${type} ${number})`, - M.interface( - `Inspector (${type} ${number})`, - {}, - { defaultGuards: 'passable' }, - ), - { - lookup: async petName => { - if (!Object.hasOwn(record, petName)) { - return undefined; - } - return record[petName]; - }, - list: () => Object.keys(record), + makeExo(`Inspector (${type} ${number})`, InspectorInterface, { + lookup: async petName => { + if (!Object.hasOwn(record, petName)) { + return undefined; + } + return record[petName]; }, - ); + list: () => Object.keys(record), + }); /** * @param {Context} context - The context to make far. @@ -303,15 +306,10 @@ const makeDaemonCore = async ( /** * @param {string} workerId512 */ - const makeWorkerBootstrap = async workerId512 => { - // TODO validate workerId512 + const makeDaemonFacetForWorker = async workerId512 => { return makeExo( - `Endo for worker ${workerId512}`, - M.interface( - `Endo for worker ${workerId512}`, - {}, - { defaultGuards: 'passable' }, - ), + `Endo facet for worker ${workerId512}`, + DaemonFacetForWorkerInterface, {}, ); }; @@ -321,8 +319,7 @@ const makeDaemonCore = async ( * @param {Context} context */ const makeIdentifiedWorker = async (workerId512, context) => { - // TODO validate workerId512 - const daemonWorkerFacet = makeWorkerBootstrap(workerId512); + const daemonWorkerFacet = makeDaemonFacetForWorker(workerId512); const { promise: forceCancelled, reject: forceCancel } = /** @type {PromiseKit} */ (makePromiseKit()); @@ -355,11 +352,7 @@ const makeDaemonCore = async ( context.onCancel(gracefulCancel); - const worker = makeExo( - 'EndoWorker', - M.interface('EndoWorker', {}, { defaultGuards: 'passable' }), - {}, - ); + const worker = makeExo('EndoWorker', WorkerInterface, {}); workerDaemonFacets.set(worker, workerDaemonFacet); @@ -372,12 +365,16 @@ const makeDaemonCore = async ( const makeReadableBlob = sha512 => { const { text, json, streamBase64 } = contentStore.fetch(sha512); /** @type {FarRef} */ - return Far(`Readable file with SHA-512 ${sha512.slice(0, 8)}...`, { - sha512: () => sha512, - streamBase64, - text, - json, - }); + return makeExo( + `Readable file with SHA-512 ${sha512.slice(0, 8)}...`, + BlobInterface, + { + sha512: () => sha512, + streamBase64, + text, + json, + }, + ); }; /** @@ -555,8 +552,7 @@ const makeDaemonCore = async ( }, endo: async ({ host: hostId, networks: networksId, peers: peersId }) => { /** @type {FarRef} */ - const endoBootstrap = Far('Endo private facet', { - // TODO for user named + const endoBootstrap = makeExo('Endo', EndoInterface, { ping: async () => 'pong', terminate: async () => { cancel(new Error('Termination requested')); @@ -595,31 +591,27 @@ const makeDaemonCore = async ( }; return /** @type {FarRef} */ ( /** @type {unknown} */ ( - makeExo( - 'EndoGuest', - M.interface('EndoGuest', {}, { defaultGuards: 'passable' }), - { - has: disallowedFn, - identify: disallowedFn, - list: disallowedFn, - followNameChanges: disallowedFn, - lookup: disallowedFn, - reverseLookup: disallowedFn, - write: disallowedFn, - remove: disallowedFn, - move: disallowedFn, - copy: disallowedFn, - listMessages: disallowedFn, - followMessages: disallowedFn, - resolve: disallowedFn, - reject: disallowedFn, - adopt: disallowedFn, - dismiss: disallowedFn, - request: disallowedFn, - send: disallowedFn, - makeDirectory: disallowedFn, - }, - ) + makeExo('EndoGuest', GuestInterface, { + has: disallowedFn, + identify: disallowedFn, + list: disallowedFn, + followNameChanges: disallowedFn, + lookup: disallowedFn, + reverseLookup: disallowedFn, + write: disallowedFn, + remove: disallowedFn, + move: disallowedFn, + copy: disallowedFn, + listMessages: disallowedFn, + followMessages: disallowedFn, + resolve: disallowedFn, + reject: disallowedFn, + adopt: disallowedFn, + dismiss: disallowedFn, + request: disallowedFn, + send: disallowedFn, + makeDirectory: disallowedFn, + }) ) ); }, @@ -1373,8 +1365,8 @@ const makeDaemonCore = async ( return { id, value }; }; - /** @type {DaemonCore['formulateEndoBootstrap']} */ - const formulateEndoBootstrap = async specifiedFormulaNumber => { + /** @type {DaemonCore['formulateEndo']} */ + const formulateEndo = async specifiedFormulaNumber => { const identifiers = await formulaGraphJobs.enqueue(async () => { const formulaNumber = await (specifiedFormulaNumber ?? randomHex512()); const endoId = formatId({ @@ -1537,11 +1529,7 @@ const makeDaemonCore = async ( return provide(guestHandleId); }; - return makeExo( - 'Invitation', - M.interface('Invitation', {}, { defaultGuards: 'passable' }), - { accept, locate }, - ); + return makeExo('Invitation', InvitationInterface, { accept, locate }); }; const makeContext = makeContextMaker({ @@ -1681,21 +1669,17 @@ const makeDaemonCore = async ( /** @returns {string[]} The list of all names in the pet store. */ const list = () => petStore.list(); - const info = makeExo( - 'Endo inspector facet', - M.interface('Endo inspector facet', {}, { defaultGuards: 'passable' }), - { - lookup, - list, - }, - ); + const info = makeExo('EndoInspectorHub', InspectorHubInterface, { + lookup, + list, + }); return info; }; /** @type {DaemonCoreExternal} */ return { - formulateEndoBootstrap, + formulateEndo, provide, nodeId: localNodeId, }; @@ -1733,7 +1717,7 @@ const provideEndoBootstrap = async ( daemonCore.provide(endoId) ); } else { - const { value: endoBootstrap } = await daemonCore.formulateEndoBootstrap( + const { value: endoBootstrap } = await daemonCore.formulateEndo( endoFormulaNumber, ); return endoBootstrap; diff --git a/packages/daemon/src/directory.js b/packages/daemon/src/directory.js index 389e63abeb..9908a26446 100644 --- a/packages/daemon/src/directory.js +++ b/packages/daemon/src/directory.js @@ -2,10 +2,11 @@ import { E } from '@endo/far'; import { makeExo } from '@endo/exo'; -import { M } from '@endo/patterns'; import { makeIteratorRef } from './reader-ref.js'; import { formatLocator, idFromLocator } from './locator.js'; +import { DirectoryInterface } from './interfaces.js'; + const { quote: q } = assert; /** @import { DaemonCore, MakeDirectoryNode, EndoDirectory, NameHub, LocatorNameChange, Context } from './types.js' */ @@ -255,17 +256,13 @@ export const makeDirectoryMaker = ({ const petStore = await provide(petStoreId, 'pet-store'); const directory = makeDirectoryNode(petStore); - return makeExo( - 'EndoDirectory', - M.interface('EndoDirectory', {}, { defaultGuards: 'passable' }), - { - ...directory, - /** @param {string} locator */ - followLocatorNameChanges: locator => - makeIteratorRef(directory.followLocatorNameChanges(locator)), - followNameChanges: () => makeIteratorRef(directory.followNameChanges()), - }, - ); + return makeExo('EndoDirectory', DirectoryInterface, { + ...directory, + /** @param {string} locator */ + followLocatorNameChanges: locator => + makeIteratorRef(directory.followLocatorNameChanges(locator)), + followNameChanges: () => makeIteratorRef(directory.followNameChanges()), + }); }; return { makeIdentifiedDirectory, makeDirectoryNode }; diff --git a/packages/daemon/src/guest.js b/packages/daemon/src/guest.js index 93902e8f9b..d7d1740638 100644 --- a/packages/daemon/src/guest.js +++ b/packages/daemon/src/guest.js @@ -1,11 +1,11 @@ // @ts-check import { makeExo } from '@endo/exo'; -import { M } from '@endo/patterns'; import { makeIteratorRef } from './reader-ref.js'; import { makePetSitter } from './pet-sitter.js'; /** @import { Context, EndoGuest, MakeDirectoryNode, MakeMailbox, Provide } from './types.js' */ +import { GuestInterface } from './interfaces.js'; /** * @param {object} args @@ -114,18 +114,14 @@ export const makeGuestMaker = ({ provide, makeMailbox, makeDirectoryNode }) => { deliver, }; - return makeExo( - 'EndoGuest', - M.interface('EndoGuest', {}, { defaultGuards: 'passable' }), - { - ...guest, - /** @param {string} locator */ - followLocatorNameChanges: locator => - makeIteratorRef(guest.followLocatorNameChanges(locator)), - followMessages: () => makeIteratorRef(guest.followMessages()), - followNameChanges: () => makeIteratorRef(guest.followNameChanges()), - }, - ); + return makeExo('EndoGuest', GuestInterface, { + ...guest, + /** @param {string} locator */ + followLocatorNameChanges: locator => + makeIteratorRef(guest.followLocatorNameChanges(locator)), + followMessages: () => makeIteratorRef(guest.followMessages()), + followNameChanges: () => makeIteratorRef(guest.followNameChanges()), + }); }; return makeGuest; diff --git a/packages/daemon/src/host.js b/packages/daemon/src/host.js index 5b915edd5f..80accd7f6b 100644 --- a/packages/daemon/src/host.js +++ b/packages/daemon/src/host.js @@ -6,13 +6,14 @@ import { E } from '@endo/far'; import { makeExo } from '@endo/exo'; -import { M } from '@endo/patterns'; import { makeIteratorRef } from './reader-ref.js'; import { assertPetName, petNamePathFrom } from './pet-name.js'; import { parseId, formatId } from './formula-identifier.js'; import { makePetSitter } from './pet-sitter.js'; import { makeDeferredTasks } from './deferred-tasks.js'; +import { HostInterface } from './interfaces.js'; + const { quote: q } = assert; /** @param {string} name */ @@ -631,18 +632,14 @@ export const makeHostMaker = ({ accept, }; - const hostExo = makeExo( - 'EndoHost', - M.interface('EndoHost', {}, { defaultGuards: 'passable' }), - { - ...host, - /** @param {string} locator */ - followLocatorNameChanges: locator => - makeIteratorRef(host.followLocatorNameChanges(locator)), - followMessages: () => makeIteratorRef(host.followMessages()), - followNameChanges: () => makeIteratorRef(host.followNameChanges()), - }, - ); + const hostExo = makeExo('EndoHost', HostInterface, { + ...host, + /** @param {string} locator */ + followLocatorNameChanges: locator => + makeIteratorRef(host.followLocatorNameChanges(locator)), + followMessages: () => makeIteratorRef(host.followMessages()), + followNameChanges: () => makeIteratorRef(host.followNameChanges()), + }); await provide(mainWorkerId, 'worker'); diff --git a/packages/daemon/src/interfaces.js b/packages/daemon/src/interfaces.js new file mode 100644 index 0000000000..a318ee1e0e --- /dev/null +++ b/packages/daemon/src/interfaces.js @@ -0,0 +1,100 @@ +// @ts-check + +import { M } from '@endo/patterns'; + +export const WorkerInterface = M.interface('EndoWorker', {}); + +export const HostInterface = M.interface( + 'EndoHost', + {}, + { defaultGuards: 'passable' }, +); + +export const GuestInterface = M.interface( + 'EndoGuest', + {}, + { defaultGuards: 'passable' }, +); + +export const InvitationInterface = M.interface( + 'EndoInvitation', + {}, + { defaultGuards: 'passable' }, +); + +export const InspectorHubInterface = M.interface( + 'EndoInspectorHub', + {}, + { defaultGuards: 'passable' }, +); + +export const InspectorInterface = M.interface( + `EndoInspector`, + {}, + { defaultGuards: 'passable' }, +); + +export const BlobInterface = M.interface( + 'EndoBlobInterface', + {}, + { defaultGuards: 'passable' }, +); + +export const ResponderInterface = M.interface( + 'EndoResponder', + {}, + { + defaultGuards: 'passable', + }, +); + +export const EnvelopeInterface = M.interface('EndoEnvelope', {}); + +export const DismisserInterface = M.interface( + 'EndoDismisser', + {}, + { defaultGuards: 'passable' }, +); + +export const HandleInterface = M.interface( + 'EndoHandle', + {}, + { + defaultGuards: 'passable', + }, +); + +export const DirectoryInterface = M.interface( + 'EndoDirectory', + {}, + { + defaultGuards: 'passable', + }, +); + +export const DaemonFacetForWorkerInterface = M.interface( + 'EndoDaemonFacetForWorker', + {}, +); + +export const WorkerFacetForDaemonInterface = M.interface( + 'EndoWorkerFacetForDaemon', + {}, + { defaultGuards: 'passable' }, +); + +export const EndoInterface = M.interface( + 'Endo', + {}, + { + defaultGuards: 'passable', + }, +); + +export const AsyncIteratorInterface = M.interface( + 'AsyncIterator', + {}, + { + defaultGuards: 'passable', + }, +); diff --git a/packages/daemon/src/interfaces/web.js b/packages/daemon/src/interfaces/web.js new file mode 100644 index 0000000000..726168855d --- /dev/null +++ b/packages/daemon/src/interfaces/web.js @@ -0,0 +1,9 @@ +import { M } from '@endo/patterns'; + +export const WebPageControllerInterface = M.interface( + 'EndoWebPageController', + {}, + { + defaultGuards: 'passable', + }, +); diff --git a/packages/daemon/src/mail.js b/packages/daemon/src/mail.js index 4778df9085..3b02cb6f53 100644 --- a/packages/daemon/src/mail.js +++ b/packages/daemon/src/mail.js @@ -2,11 +2,17 @@ import { E } from '@endo/eventual-send'; import { makeExo } from '@endo/exo'; -import { M } from '@endo/patterns'; import { makePromiseKit } from '@endo/promise-kit'; import { makeChangeTopic } from './pubsub.js'; import { assertPetName } from './pet-name.js'; +import { + ResponderInterface, + EnvelopeInterface, + DismisserInterface, + HandleInterface, +} from './interfaces.js'; + /** @import { ERef } from '@endo/eventual-send' */ /** @import { PromiseKit } from '@endo/promise-kit' */ /** @import { Envelope, EnvelopedMessage, Handle, Mail, MakeMailbox, Provide, Request, StampedMessage, Topic } from './types.js' */ @@ -25,19 +31,9 @@ const makeRequest = (description, fromId, toId) => { () => /** @type {const} */ ('fulfilled'), () => /** @type {const} */ ('rejected'), ); - const responder = makeExo( - 'Responder', - M.interface( - 'Responder', - {}, - { - defaultGuards: 'passable', - }, - ), - { - respondId: resolve, - }, - ); + const responder = makeExo('EndoResponder', ResponderInterface, { + respondId: resolve, + }); const request = harden({ type: /** @type {const} */ ('request'), from: fromId, @@ -49,8 +45,7 @@ const makeRequest = (description, fromId, toId) => { return harden({ request, response: promise }); }; -const EnvelopeShape = M.interface('Envelope', {}); -const makeEnvelope = () => makeExo('Envelope', EnvelopeShape, {}); +const makeEnvelope = () => makeExo('Envelope', EnvelopeInterface, {}); /** * @param {object} args @@ -90,22 +85,12 @@ export const makeMailboxMaker = ({ provide }) => { const messageNumber = nextMessageNumber; nextMessageNumber += 1; - const dismisser = makeExo( - 'Dismisser', - M.interface( - 'Dismisser', - {}, - { - defaultGuards: 'passable', - }, - ), - { - dismiss() { - messages.delete(messageNumber); - dismissal.resolve(); - }, + const dismisser = makeExo('Dismisser', DismisserInterface, { + dismiss() { + messages.delete(messageNumber); + dismissal.resolve(); }, - ); + }); const message = harden({ ...envelope, @@ -328,20 +313,10 @@ export const makeMailboxMaker = ({ provide }) => { deliver(message); }; - const handle = makeExo( - 'Handle', - M.interface( - 'Handle', - {}, - { - defaultGuards: 'passable', - }, - ), - { - receive, - open, - }, - ); + const handle = makeExo('Handle', HandleInterface, { + receive, + open, + }); return harden({ handle: () => handle, diff --git a/packages/daemon/src/reader-ref.js b/packages/daemon/src/reader-ref.js index c3590be549..9e82d55fb0 100644 --- a/packages/daemon/src/reader-ref.js +++ b/packages/daemon/src/reader-ref.js @@ -3,7 +3,8 @@ import { encodeBase64 } from '@endo/base64'; import { mapReader } from '@endo/stream'; import { makeExo } from '@endo/exo'; -import { M } from '@endo/patterns'; + +import { AsyncIteratorInterface } from './interfaces.js'; /** @import { Reader } from '@endo/stream' */ /** @import { FarRef } from '@endo/eventual-send' */ @@ -37,36 +38,32 @@ export const asyncIterate = iterable => { export const makeIteratorRef = iterable => { const iterator = asyncIterate(iterable); // @ts-ignore while switching from Far - return makeExo( - 'AsyncIterator', - M.interface('AsyncIterator', {}, { defaultGuards: 'passable' }), - { - async next() { - return iterator.next(undefined); - }, - /** - * @param {any} value - */ - async return(value) { - if (iterator.return !== undefined) { - return iterator.return(value); - } - return harden({ done: true, value: undefined }); - }, - /** - * @param {any} error - */ - async throw(error) { - if (iterator.throw !== undefined) { - return iterator.throw(error); - } - return harden({ done: true, value: undefined }); - }, - [Symbol.asyncIterator]() { - return this; - }, + return makeExo('AsyncIterator', AsyncIteratorInterface, { + async next() { + return iterator.next(undefined); + }, + /** + * @param {any} value + */ + async return(value) { + if (iterator.return !== undefined) { + return iterator.return(value); + } + return harden({ done: true, value: undefined }); + }, + /** + * @param {any} error + */ + async throw(error) { + if (iterator.throw !== undefined) { + return iterator.throw(error); + } + return harden({ done: true, value: undefined }); + }, + [Symbol.asyncIterator]() { + return this; }, - ); + }); }; /** diff --git a/packages/daemon/src/types.d.ts b/packages/daemon/src/types.d.ts index 877d72db6b..eb54f697ab 100644 --- a/packages/daemon/src/types.d.ts +++ b/packages/daemon/src/types.d.ts @@ -827,7 +827,7 @@ export interface DaemonCore { formulateDirectory: () => FormulateResult; - formulateEndoBootstrap: ( + formulateEndo: ( specifiedFormulaNumber: string, ) => FormulateResult>; @@ -933,7 +933,7 @@ export interface DaemonCore { } export interface DaemonCoreExternal { - formulateEndoBootstrap: DaemonCore['formulateEndoBootstrap']; + formulateEndo: DaemonCore['formulateEndo']; nodeId: string; provide: DaemonCore['provide']; } diff --git a/packages/daemon/src/web-page.js b/packages/daemon/src/web-page.js index 11878fbb7a..065a176b67 100644 --- a/packages/daemon/src/web-page.js +++ b/packages/daemon/src/web-page.js @@ -4,10 +4,12 @@ import '@endo/init/debug.js'; import { makeCapTP } from '@endo/captp'; import { E, Far } from '@endo/far'; -import { makeExo } from '@endo/exo'; import { M } from '@endo/patterns'; +import { makeExo } from '@endo/exo'; import { importBundle } from '@endo/import-bundle'; +import { WebPageControllerInterface } from './interfaces/web.js'; + const getPrototypeChain = obj => { const chain = []; while (obj) { @@ -81,28 +83,24 @@ const endowments = Object.freeze({ const url = new URL(window.location.href); url.protocol = 'ws'; -const bootstrap = makeExo( - 'WebFacet', - M.interface('WebFacet', {}, { defaultGuards: 'passable' }), - { - ping() { - console.log('received ping'); - return 'pong'; - }, - async makeBundle(bundle, powers) { - const namespace = await importBundle(bundle, { - endowments, - }); - return namespace.make(powers); - }, - reject(message) { - document.body.innerHTML = ''; - const $title = document.createElement('h1'); - $title.innerText = `💔 ${message}`; - document.body.appendChild($title); - }, +const bootstrap = makeExo('EndoWebPageForServer', WebPageControllerInterface, { + ping() { + console.log('received ping'); + return 'pong'; }, -); + async makeBundle(bundle, powers) { + const namespace = await importBundle(bundle, { + endowments, + }); + return namespace.make(powers); + }, + reject(message) { + document.body.innerHTML = ''; + const $title = document.createElement('h1'); + $title.innerText = `💔 ${message}`; + document.body.appendChild($title); + }, +}); const textEncoder = new TextEncoder(); const textDecoder = new TextDecoder(); diff --git a/packages/daemon/src/worker.js b/packages/daemon/src/worker.js index d0a9a5c69e..eaa88ebff2 100644 --- a/packages/daemon/src/worker.js +++ b/packages/daemon/src/worker.js @@ -6,6 +6,8 @@ import { makeExo } from '@endo/exo'; import { M } from '@endo/patterns'; import { makeNetstringCapTP } from './connection.js'; +import { WorkerFacetForDaemonInterface } from './interfaces.js'; + /** @import { ERef } from '@endo/eventual-send' */ /** @import { EndoReadable, MignonicPowers } from './types.js' */ @@ -45,69 +47,65 @@ const normalizeFilePath = path => { * @param {(error: Error) => void} args.cancel */ export const makeWorkerFacet = ({ cancel }) => { - return makeExo( - 'EndoWorkerFacet', - M.interface('EndoWorkerFacet', {}, { defaultGuards: 'passable' }), - { - terminate: async () => { - console.error('Endo worker received terminate request'); - cancel(Error('terminate')); - }, - - /** - * @param {string} source - * @param {Array} names - * @param {Array} values - * @param {string} $id - * @param {Promise} $cancelled - */ - evaluate: async (source, names, values, $id, $cancelled) => { - const compartment = new Compartment( - harden({ - ...endowments, - $id, - $cancelled, - ...Object.fromEntries( - names.map((name, index) => [name, values[index]]), - ), - }), - ); - return compartment.evaluate(source); - }, - - /** - * @param {string} specifier - * @param {Promise} powersP - * @param {Promise} contextP - */ - makeUnconfined: async (specifier, powersP, contextP) => { - // Windows absolute path includes drive letter which is confused for - // protocol specifier. So, we reformat the specifier to include the - // file protocol. - const specifierUrl = normalizeFilePath(specifier); - const namespace = await import(specifierUrl); - return namespace.make(powersP, contextP); - }, - - /** - * @param {ERef} readableP - * @param {Promise} powersP - * @param {Promise} contextP - */ - makeBundle: async (readableP, powersP, contextP) => { - const bundleText = await E(readableP).text(); - const bundle = JSON.parse(bundleText); - - // We defer importing the import-bundle machinery to this in order to - // avoid an up-front cost for workers that never use importBundle. - const { importBundle } = await import('@endo/import-bundle'); - const namespace = await importBundle(bundle, { - endowments, - }); - return namespace.make(powersP, contextP); - }, + return makeExo('EndoWorkerFacetForDaemon', WorkerFacetForDaemonInterface, { + terminate: async () => { + console.error('Endo worker received terminate request'); + cancel(Error('terminate')); }, - ); + + /** + * @param {string} source + * @param {Array} names + * @param {Array} values + * @param {string} $id + * @param {Promise} $cancelled + */ + evaluate: async (source, names, values, $id, $cancelled) => { + const compartment = new Compartment( + harden({ + ...endowments, + $id, + $cancelled, + ...Object.fromEntries( + names.map((name, index) => [name, values[index]]), + ), + }), + ); + return compartment.evaluate(source); + }, + + /** + * @param {string} specifier + * @param {Promise} powersP + * @param {Promise} contextP + */ + makeUnconfined: async (specifier, powersP, contextP) => { + // Windows absolute path includes drive letter which is confused for + // protocol specifier. So, we reformat the specifier to include the + // file protocol. + const specifierUrl = normalizeFilePath(specifier); + const namespace = await import(specifierUrl); + return namespace.make(powersP, contextP); + }, + + /** + * @param {ERef} readableP + * @param {Promise} powersP + * @param {Promise} contextP + */ + makeBundle: async (readableP, powersP, contextP) => { + const bundleText = await E(readableP).text(); + const bundle = JSON.parse(bundleText); + + // We defer importing the import-bundle machinery to this in order to + // avoid an up-front cost for workers that never use importBundle. + const { importBundle } = await import('@endo/import-bundle'); + const namespace = await importBundle(bundle, { + endowments, + }); + return namespace.make(powersP, contextP); + }, + }); }; /** diff --git a/packages/daemon/test/context-consumer.js b/packages/daemon/test/context-consumer.js index cf330dec9e..916084b371 100644 --- a/packages/daemon/test/context-consumer.js +++ b/packages/daemon/test/context-consumer.js @@ -1,20 +1,22 @@ import { E } from '@endo/far'; -import { makeExo } from '@endo/exo'; import { M } from '@endo/patterns'; +import { makeExo } from '@endo/exo'; + +export const ContextConsumerInterface = M.interface( + 'Context consumer', + {}, + { defaultGuards: 'passable' }, +); export const make = async (_powers, context) => { - return makeExo( - 'Context consumer', - M.interface('Context consumer', {}, { defaultGuards: 'passable' }), - { - async awaitCancellation() { - try { - await E(context).whenCancelled(); - } catch { - return 'cancelled'; - } - throw new Error('should have been cancelled'); - }, + return makeExo('Context consumer', ContextConsumerInterface, { + async awaitCancellation() { + try { + await E(context).whenCancelled(); + } catch { + return 'cancelled'; + } + throw new Error('should have been cancelled'); }, - ); + }); }; From 05d6bf8b444e423f88ece215e45517ff677a8f49 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Thu, 18 Apr 2024 15:56:49 -0700 Subject: [PATCH 3/4] refactor(daemon): Errata --- packages/daemon/src/daemon.js | 16 +++++++++++++--- packages/daemon/src/types.d.ts | 2 +- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/packages/daemon/src/daemon.js b/packages/daemon/src/daemon.js index 57e3ceec7a..a8fbee61d0 100644 --- a/packages/daemon/src/daemon.js +++ b/packages/daemon/src/daemon.js @@ -137,6 +137,9 @@ const makeDaemonCore = async ( const localNodeId = deriveId('node', rootEntropy, cryptoPowers.makeSha512()); console.log('Node', localNodeId); + // We generate formulas for some entities that are presumed to exist + // because they are parts of the daemon's root object. + /** * @param {string} derivation * @param {Formula} formula @@ -180,9 +183,11 @@ const makeDaemonCore = async ( ), ); + // The following are the root state tables for the daemon. + /** * The two functions "formulate" and "provide" share a responsibility for - * maintaining the memoization tables "controllerForId", "typeForId", and + * maintaining the memoization tables "controllerForId", "formulaForId", and * "idForRef". * "formulate" is used for creating and persisting new formulas, whereas * "provide" is used for "reincarnating" the values of stored formulas. @@ -203,8 +208,11 @@ const makeDaemonCore = async ( /** @type {WeakMap<{}, string>} */ const agentIdForHandle = new WeakMap(); + // The following are functions that manage that state. + /** @param {string} id */ const getFormulaForId = async id => { + // No synchronous preamble. await null; let formula = formulaForId.get(id); @@ -244,6 +252,8 @@ const makeDaemonCore = async ( provideController(id).value ); + // The following concern connections to other daemons. + const provideRemoteControl = makeRemoteControlProvider(localNodeId); /** @@ -264,8 +274,8 @@ const makeDaemonCore = async ( ); }; - // Gateway is equivalent to E's "nonce locator". It provides a value for - // a formula identifier to a remote client. + // Gateway is equivalent to E's "nonce locator". + // It provides a value for a formula identifier to a remote client. const localGateway = Far('Gateway', { /** @param {string} requestedId */ provide: async requestedId => { diff --git a/packages/daemon/src/types.d.ts b/packages/daemon/src/types.d.ts index eb54f697ab..91b2f97721 100644 --- a/packages/daemon/src/types.d.ts +++ b/packages/daemon/src/types.d.ts @@ -1,5 +1,5 @@ import type { ERef } from '@endo/eventual-send'; -import { FarRef } from '@endo/far'; +import type { FarRef } from '@endo/far'; import type { Reader, Writer, Stream } from '@endo/stream'; export type SomehowAsyncIterable = From ddc08257a2c1999d9d3ce3e8b7b5e8795aed56a4 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Thu, 18 Apr 2024 15:56:08 -0700 Subject: [PATCH 4/4] fix(cli): Update cat.js guide --- packages/cli/demo/cat.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/cli/demo/cat.js b/packages/cli/demo/cat.js index 56622135aa..3ea46387b6 100644 --- a/packages/cli/demo/cat.js +++ b/packages/cli/demo/cat.js @@ -4,17 +4,18 @@ // This command will set up the cat page, create a URL, // and open it. // -// > endo install familiar-chat cat.js --powers SELF +// > endo install cat.js --powers AGENT --listen 8920 --name cat // // Thereafter, // -// > endo open familiar-chat +// > endo open cat // // To interact with the permission manager, you can mock requests from a fake // guest. // // > endo eval 42 --name ft -// > endo request --as cat 'pet me' +// > endo mkguest feline +// > endo request --as feline 'pet me' // // At this point, the command will pause, waiting for a response. // In the Familiar Chat window, resolve the request with the pet name "ft" and