From a028690bd15a40d39c6fcc0cd390f0c4b4510ed2 Mon Sep 17 00:00:00 2001 From: Erik Marks Date: Mon, 25 Mar 2024 11:45:02 -0700 Subject: [PATCH] refactor(daemon): Add known-peers-store formula Replace the known peers store adapter with a dedicated `known-peers-store` formula type, instantiated the same way as other "special" formulas such as `least-authority`. The adapter used a `pet-store` formula under the hood. Giving it a different formula entirely is probably less likely to confuse us in the future. --- packages/daemon/src/daemon.js | 71 +++++++++-------------- packages/daemon/src/formula-identifier.js | 9 +++ packages/daemon/src/formula-type.js | 1 + packages/daemon/src/pet-store.js | 12 ++-- packages/daemon/src/types.d.ts | 14 ++--- 5 files changed, 50 insertions(+), 57 deletions(-) diff --git a/packages/daemon/src/daemon.js b/packages/daemon/src/daemon.js index 8e1d2ed689..1b793cd9f8 100644 --- a/packages/daemon/src/daemon.js +++ b/packages/daemon/src/daemon.js @@ -15,7 +15,7 @@ import { makeGuestMaker } from './guest.js'; import { makeHostMaker } from './host.js'; import { assertPetName } from './pet-name.js'; import { makeContextMaker } from './context.js'; -import { parseId, formatId } from './formula-identifier.js'; +import { assertValidNumber, parseId, formatId } from './formula-identifier.js'; import { makeSerialJobs } from './serial-jobs.js'; import { makeWeakMultimap } from './weak-multimap.js'; import { makeLoopbackNetwork } from './networks/loopback.js'; @@ -127,15 +127,18 @@ const makeDaemonCore = async ( ); console.log('Node', ownNodeIdentifier); - const peersFormulaNumber = deriveId( + const knownPeersFormulaNumber = deriveId( 'peers', rootEntropy, cryptoPowers.makeSha512(), ); - const ownPeersId = formatId({ - number: peersFormulaNumber, + const knownPeersId = formatId({ + number: knownPeersFormulaNumber, node: ownNodeIdentifier, }); + await persistencePowers.writeFormula(knownPeersFormulaNumber, { + type: 'known-peers-store', + }); // Prepare least authority formula const leastAuthorityFormulaNumber = deriveId( @@ -620,10 +623,11 @@ const makeDaemonCore = async ( ); }, addPeerInfo: async peerInfo => { - // eslint-disable-next-line no-use-before-define - const knownPeers = await provideKnownPeers(formula.peers); + const knownPeers = /** @type {import('./types.js').PetStore} */ ( + // eslint-disable-next-line no-use-before-define + await provide(formula.peers) + ); const { node: nodeIdentifier, addresses } = peerInfo; - // eslint-disable-next-line no-use-before-define if (knownPeers.has(nodeIdentifier)) { // We already have this peer. // TODO: merge connection info @@ -687,9 +691,19 @@ const makeDaemonCore = async ( } else if (formula.type === 'pet-store') { const external = petStorePowers.makeIdentifiedPetStore( formulaNumber, + 'pet-store', assertPetName, ); return { external, internal: undefined }; + } else if (formula.type === 'known-peers-store') { + const external = petStorePowers.makeIdentifiedPetStore( + formulaNumber, + 'known-peers-store', + // The known peers store is just a pet store that only accepts node identifiers + // (i.e. formula numbers) as "names". + assertValidNumber, + ); + return { external, internal: undefined }; } else if (formula.type === 'pet-inspector') { // Behold, unavoidable forward-reference: // eslint-disable-next-line no-use-before-define @@ -823,9 +837,11 @@ const makeDaemonCore = async ( if (nodeIdentifier === ownNodeIdentifier) { throw new Error(`Cannot get peer formula identifier for self`); } - // eslint-disable-next-line no-use-before-define - const knownPeers = await provideKnownPeers(ownPeersId); - const peerId = knownPeers.identify(nodeIdentifier); + const knownPeers = /** @type {import('./types.js').PetStore} */ ( + // eslint-disable-next-line no-use-before-define + await provide(knownPeersId) + ); + const peerId = knownPeers.identifyLocal(nodeIdentifier); if (peerId === undefined) { throw new Error( `No peer found for node identifier ${q(nodeIdentifier)}.`, @@ -1372,12 +1388,6 @@ const makeDaemonCore = async ( await randomHex512(), ); const { id: networksDirectoryId } = await formulateNetworksDirectory(); - const { id: newPeersId } = await formulateNumberedPetStore( - peersFormulaNumber, - ); - if (newPeersId !== ownPeersId) { - assert.Fail`Peers PetStore formula identifier did not match expected value, expected ${ownPeersId}, got ${newPeersId}`; - } // Ensure the default host is formulated and persisted. const { id: defaultHostId } = await formulateNumberedHost( @@ -1399,7 +1409,7 @@ const makeDaemonCore = async ( const formula = { type: 'endo', networks: identifiers.networksDirectoryId, - peers: ownPeersId, + peers: knownPeersId, host: identifiers.defaultHostId, leastAuthority: leastAuthorityId, }; @@ -1481,33 +1491,6 @@ const makeDaemonCore = async ( throw new Error('Cannot connect to peer: no supported addresses'); }; - /** - * The "known peers store" is like a pet store, but maps node identifiers to - * full peer ids. - * - * @type {import('./types.js').DaemonCore['provideKnownPeers']} - */ - const provideKnownPeers = async peersFormulaId => { - // "Known peers" is just a pet store with an adapter over it. - const petStore = /** @type {import('./types.js').PetStore} */ ( - await provide(peersFormulaId) - ); - - // Pet stores do not accept full ids as names. - /** @param {string} nodeIdentifier */ - const getNameFor = nodeIdentifier => { - return `p${nodeIdentifier.slice(0, 126)}`; - }; - - return harden({ - has: nodeIdentifier => petStore.has(getNameFor(nodeIdentifier)), - identify: nodeIdentifier => - petStore.identifyLocal(getNameFor(nodeIdentifier)), - write: (nodeIdentifier, peerId) => - petStore.write(getNameFor(nodeIdentifier), peerId), - }); - }; - /** * This is used to provide a value for a formula identifier that is known to * originate from the specified peer. diff --git a/packages/daemon/src/formula-identifier.js b/packages/daemon/src/formula-identifier.js index b570b2ecd6..4ee33bf01e 100644 --- a/packages/daemon/src/formula-identifier.js +++ b/packages/daemon/src/formula-identifier.js @@ -11,6 +11,15 @@ const idPattern = /^(?[0-9a-f]{128}):(?[0-9a-f]{128})$/; export const isValidNumber = allegedNumber => typeof allegedNumber === 'string' && numberPattern.test(allegedNumber); +/** + * @param {string} allegedNumber - The formula number or node identifier to test. + */ +export const assertValidNumber = allegedNumber => { + if (!isValidNumber(allegedNumber)) { + throw assert.error(`Invalid number ${q(allegedNumber)}`); + } +}; + /** * @param {string} id * @param {string} [petName] diff --git a/packages/daemon/src/formula-type.js b/packages/daemon/src/formula-type.js index 78e7dc9c62..bf2679e944 100644 --- a/packages/daemon/src/formula-type.js +++ b/packages/daemon/src/formula-type.js @@ -10,6 +10,7 @@ const formulaTypes = new Set([ 'guest', 'handle', 'host', + 'known-peers-store', 'least-authority', 'lookup', 'loopback-network', diff --git a/packages/daemon/src/pet-store.js b/packages/daemon/src/pet-store.js index e55a7b2979..d2bf2db1d2 100644 --- a/packages/daemon/src/pet-store.js +++ b/packages/daemon/src/pet-store.js @@ -222,11 +222,13 @@ export const makePetStoreMaker = (filePowers, locator) => { }; /** - * @param {string} formulaNumber - * @param {(name: string) => void} assertValidName - * @returns {Promise} + * @type {import('./types.js').PetStorePowers['makeIdentifiedPetStore']} */ - const makeIdentifiedPetStore = (formulaNumber, assertValidName) => { + const makeIdentifiedPetStore = ( + formulaNumber, + formulaType, + assertValidName, + ) => { if (!isValidNumber(formulaNumber)) { throw new Error( `Invalid formula number for pet store ${q(formulaNumber)}`, @@ -236,7 +238,7 @@ export const makePetStoreMaker = (filePowers, locator) => { const suffix = formulaNumber.slice(2); const petNameDirectoryPath = filePowers.joinPath( locator.statePath, - 'pet-store', + formulaType, prefix, suffix, ); diff --git a/packages/daemon/src/types.d.ts b/packages/daemon/src/types.d.ts index 0b44b5e58d..7b5e17ffaf 100644 --- a/packages/daemon/src/types.d.ts +++ b/packages/daemon/src/types.d.ts @@ -181,6 +181,10 @@ type HandleFormula = { target: string; }; +type KnownPeersStoreFormula = { + type: 'known-peers-store'; +}; + type PetStoreFormula = { type: 'pet-store'; }; @@ -210,6 +214,7 @@ export type Formula = | WebBundleFormula | HandleFormula | PetInspectorFormula + | KnownPeersStoreFormula | PetStoreFormula | DirectoryFormula | PeerFormula; @@ -488,12 +493,6 @@ export interface EndoPeer { export type EndoPeerControllerPartial = ControllerPartial; export type EndoPeerController = Controller; -export interface EndoKnownPeers { - has: (nodeIdentifier: string) => boolean; - identify: (nodeIdentifier: string) => string | undefined; - write: (nodeIdentifier: string, peerId: string) => Promise; -} - export interface EndoGateway { provide: (id: string) => Promise; } @@ -626,6 +625,7 @@ export type AssertValidNameFn = (name: string) => void; export type PetStorePowers = { makeIdentifiedPetStore: ( id: string, + formulaType: 'pet-store' | 'known-peers-store', assertValidName: AssertValidNameFn, ) => Promise; }; @@ -858,8 +858,6 @@ export interface DaemonCore { provideController: (id: string) => Controller; provideControllerAndResolveHandle: (id: string) => Promise; - - provideKnownPeers: (peersFormulaId: string) => Promise; } export interface DaemonCoreExternal {