From 837c632106ce9fb6f4e2101c906d514704378bf5 Mon Sep 17 00:00:00 2001 From: Ikenna Omekam Date: Fri, 12 Apr 2024 12:13:25 -0500 Subject: [PATCH 01/16] feat: Allocate ports with PortAllocator --- packages/network/src/network.js | 34 +++++++++++++++++++ packages/network/test/test-network-misc.js | 31 +++++++++++++++++ .../vats/src/proposals/network-proposal.js | 13 +++---- packages/vats/src/vat-network.js | 11 +++++- 4 files changed, 82 insertions(+), 7 deletions(-) diff --git a/packages/network/src/network.js b/packages/network/src/network.js index 2404ff54ca6..705c90e7dcf 100644 --- a/packages/network/src/network.js +++ b/packages/network/src/network.js @@ -1426,3 +1426,37 @@ export function prepareLoopbackProtocolHandler(zone, { watch, allVows }) { return makeLoopbackProtocolHandler; } + +/** + * + * @param {import('@agoric/base-zone').Zone} zone + * @param {ReturnType} powers + */ +export const preparePortAllocator = (zone, { watch }) => { + const makePortAllocator = zone.exoClass( + 'PortAllocator', + M.interface('PortAllocator', { + allocateIBCPort: M.callWhen().returns(Shape.Vow$(Shape.Port)), + allocateICAControllerPort: M.callWhen().returns(Shape.Vow$(Shape.Port)), + }), + ({ protocol }) => ({ protocol, lastICAPortNum: 0n }), + { + allocateIBCPort() { + const { state } = this; + // Allocate an IBC port with a unique generated name. + return watch(E(state.protocol).bindPort(`/ibc-port/`)); + }, + allocateICAControllerPort() { + const { state } = this; + state.lastICAPortNum += 1n; + return watch( + E(state.protocol).bindPort( + `/ibc-port/icacontroller-${state.lastICAPortNum}`, + ), + ); + }, + }, + ); + + return makePortAllocator; +}; diff --git a/packages/network/test/test-network-misc.js b/packages/network/test/test-network-misc.js index 258b601d412..73433a4823e 100644 --- a/packages/network/test/test-network-misc.js +++ b/packages/network/test/test-network-misc.js @@ -14,6 +14,7 @@ import { prepareRouter, prepareLoopbackProtocolHandler, prepareNetworkProtocol, + preparePortAllocator, } from '../src/index.js'; import '../src/types.js'; @@ -167,6 +168,36 @@ test('handled protocol', async t => { await when(port.revoke()); }); +test('verify port allocation', async t => { + const zone = makeDurableZone(provideBaggage('network-handled-protocol')); + const powers = prepareVowTools(zone); + const { when } = powers; + const makeNetworkProtocol = prepareNetworkProtocol(zone, powers); + const makeEchoConnectionHandler = prepareEchoConnectionKit(zone); + const makeProtocolHandler = prepareProtocolHandler( + zone, + t, + makeEchoConnectionHandler, + powers, + ); + const protocol = makeNetworkProtocol(makeProtocolHandler()); + const makePortAllocator = preparePortAllocator(zone, powers); + const portAllocator = makePortAllocator({ protocol }); + + const ibcPort = await when(portAllocator.allocateIBCPort()); + t.is(ibcPort.getLocalAddress(), '/ibc-port/1'); + + const icaControllerPort1 = await when( + portAllocator.allocateICAControllerPort(), + ); + t.is(icaControllerPort1.getLocalAddress(), '/ibc-port/icacontroller-1'); + + const icaControllerPort2 = await when( + portAllocator.allocateICAControllerPort(), + ); + t.is(icaControllerPort2.getLocalAddress(), '/ibc-port/icacontroller-2'); +}); + test('protocol connection listen', async t => { const zone = makeDurableZone(provideBaggage('network-protocol-connection')); const powers = prepareVowTools(zone); diff --git a/packages/vats/src/proposals/network-proposal.js b/packages/vats/src/proposals/network-proposal.js index d9e8523b118..00554842eee 100644 --- a/packages/vats/src/proposals/network-proposal.js +++ b/packages/vats/src/proposals/network-proposal.js @@ -123,25 +123,25 @@ export const setupNetworkProtocols = async ( networkVat.reset(); networkVat.resolve(vats.network); + const portAllocator = E(vats.network).getPortAllocator(); const bridgeManager = await bridgeManagerP; const dibcBridgeManager = bridgeManager && E(bridgeManager).register(BRIDGE_ID.DIBC); // The Interchain Account (ICA) Controller must be bound to a port that starts // with 'icacontroller', so we provide one such port to each client. - let lastICAPort = 0; const makePorts = async () => { // Bind to some fresh ports (either unspecified name or `icacontroller-*`) // on the IBC implementation and provide them for the user to have. const ibcportP = []; for (let i = 0; i < NUM_IBC_PORTS_PER_CLIENT; i += 1) { - let bindAddr = '/ibc-port/'; if (i === NUM_IBC_PORTS_PER_CLIENT - 1) { - lastICAPort += 1; - bindAddr += `${INTERCHAIN_ACCOUNT_CONTROLLER_PORT_PREFIX}${lastICAPort}`; + const portP = when(E(portAllocator).allocateICAControllerPort()); + ibcportP.push(portP); + } else { + const portP = when(E(portAllocator).allocateIBCPort()); + ibcportP.push(portP); } - const port = when(E(vats.network).bindPort(bindAddr)); - ibcportP.push(port); } return Promise.all(ibcportP); }; @@ -168,6 +168,7 @@ export const getManifestForNetwork = (_powers, { networkRef, ibcRef }) => ({ zoe: 'zoe', provisioning: 'provisioning', vatUpgradeInfo: true, + portAllocator: 'portAllocator', }, produce: { networkVat: 'network', diff --git a/packages/vats/src/vat-network.js b/packages/vats/src/vat-network.js index 8c2c25ef076..97fde100337 100644 --- a/packages/vats/src/vat-network.js +++ b/packages/vats/src/vat-network.js @@ -3,10 +3,11 @@ import { makeDurableZone } from '@agoric/zone/durable.js'; import { prepareEchoConnectionKit, prepareLoopbackProtocolHandler, + preparePortAllocator, prepareRouterProtocol, } from '@agoric/network'; import { prepareVowTools } from '@agoric/vat-data/vow.js'; -import { Far } from '@endo/far'; +import { E, Far } from '@endo/far'; export function buildRootObject(_vatPowers, _args, baggage) { const zone = makeDurableZone(baggage); @@ -20,6 +21,11 @@ export function buildRootObject(_vatPowers, _args, baggage) { makeRouterProtocol(), ); + const makePortAllocator = preparePortAllocator(zone, powers); + const portAllocator = zone.makeOnce('PortAllocator', _key => + makePortAllocator({ protocol }), + ); + const makeLoopbackProtocolHandler = prepareLoopbackProtocolHandler( zone, powers, @@ -37,5 +43,8 @@ export function buildRootObject(_vatPowers, _args, baggage) { protocol.unregisterProtocolHandler(...args), /** @param {Parameters} args */ bindPort: (...args) => protocol.bindPort(...args), + getPortAllocator() { + return portAllocator; + }, }); } From 84b4fd48de85bdc4de2bada38cf3bc2e2e27d879 Mon Sep 17 00:00:00 2001 From: Ikenna Omekam Date: Mon, 22 Apr 2024 13:39:05 -0500 Subject: [PATCH 02/16] fix: add tests --- .../boot/test/bootstrapTests/ibcClientMock.js | 7 ++++--- .../boot/test/bootstrapTests/ibcServerMock.js | 5 +++-- .../bootstrapTests/test-net-ibc-upgrade.ts | 4 ++-- .../test/smartWallet/boot-test-utils.js | 1 - packages/network/src/network.js | 5 +++++ packages/orchestration/src/orchestration.js | 7 ++----- .../src/proposals/orchestration-proposal.js | 5 ++--- .../vats/src/proposals/network-proposal.js | 20 +++++++++---------- packages/vats/src/vat-network.js | 2 -- 9 files changed, 28 insertions(+), 28 deletions(-) diff --git a/packages/boot/test/bootstrapTests/ibcClientMock.js b/packages/boot/test/bootstrapTests/ibcClientMock.js index 4614e0b49c9..734340736e9 100644 --- a/packages/boot/test/bootstrapTests/ibcClientMock.js +++ b/packages/boot/test/bootstrapTests/ibcClientMock.js @@ -6,14 +6,15 @@ import { V as E } from '@agoric/vat-data/vow.js'; /** * @param {ZCF} zcf * @param {{ - * address: string, * networkVat: ERef>; * }} privateArgs * @param {import("@agoric/vat-data").Baggage} _baggage */ export const start = async (zcf, privateArgs, _baggage) => { - const { address, networkVat } = privateArgs; - const myPort = await E(networkVat).bindPort(address); + const { networkVat } = privateArgs; + + const portAllocator = await E(networkVat).getPortAllocator(); + const myPort = E(portAllocator).allocateIBCPort(); const { log } = console; let connP; diff --git a/packages/boot/test/bootstrapTests/ibcServerMock.js b/packages/boot/test/bootstrapTests/ibcServerMock.js index d56f316cb86..eaa5f1c0bf8 100644 --- a/packages/boot/test/bootstrapTests/ibcServerMock.js +++ b/packages/boot/test/bootstrapTests/ibcServerMock.js @@ -17,9 +17,10 @@ const { log } = console; * @param {import("@agoric/vat-data").Baggage} _baggage */ export const start = async (zcf, privateArgs, _baggage) => { - const { address, networkVat } = privateArgs; + const { networkVat } = privateArgs; - const boundPort = await E(networkVat).bindPort(address); + const portAllocator = await E(networkVat).getPortAllocator(); + const boundPort = E(portAllocator).allocateIBCPort(); /** @type {Array<[label: string, resolve: (value: any) => void, reject: (reason: any) => void]>} */ const queue = []; diff --git a/packages/boot/test/bootstrapTests/test-net-ibc-upgrade.ts b/packages/boot/test/bootstrapTests/test-net-ibc-upgrade.ts index bfb7079406d..8b087e31628 100644 --- a/packages/boot/test/bootstrapTests/test-net-ibc-upgrade.ts +++ b/packages/boot/test/bootstrapTests/test-net-ibc-upgrade.ts @@ -122,7 +122,7 @@ test.serial('upgrade at many points in network API flow', async t => { installation.ibcServerMock, {}, {}, - { address: '/ibc-port/', networkVat }, + { networkVat }, ); t.truthy(started.creatorFacet, `${label} ibcServerMock`); return [label, { server: started.creatorFacet }]; @@ -140,7 +140,7 @@ test.serial('upgrade at many points in network API flow', async t => { installation.ibcClientMock, {}, {}, - { address: '/ibc-port/', networkVat }, + { networkVat }, ); t.truthy(started.creatorFacet, `${label} ibcClientMock`); return [label, { ...opts, client: started.creatorFacet }]; diff --git a/packages/inter-protocol/test/smartWallet/boot-test-utils.js b/packages/inter-protocol/test/smartWallet/boot-test-utils.js index b222b0fdab3..1a924713a75 100644 --- a/packages/inter-protocol/test/smartWallet/boot-test-utils.js +++ b/packages/inter-protocol/test/smartWallet/boot-test-utils.js @@ -58,7 +58,6 @@ export const makeMock = log => network: Far('network', { registerProtocolHandler: noop, - bindPort: () => harden({ addListener: noop }), }), }, }); diff --git a/packages/network/src/network.js b/packages/network/src/network.js index 705c90e7dcf..d5b287f988d 100644 --- a/packages/network/src/network.js +++ b/packages/network/src/network.js @@ -1455,6 +1455,11 @@ export const preparePortAllocator = (zone, { watch }) => { ), ); }, + allocateIBCPegasusPort() { + const { state } = this; + // Allocate an IBC port with a unique generated name. + return watch(E(state.protocol).bindPort(`/ibc-port/pegasus`)); + }, }, ); diff --git a/packages/orchestration/src/orchestration.js b/packages/orchestration/src/orchestration.js index 99787a942ed..fae53bc3fe4 100644 --- a/packages/orchestration/src/orchestration.js +++ b/packages/orchestration/src/orchestration.js @@ -209,11 +209,8 @@ const prepareOrchestration = (zone, createChainAccount) => self: { async bindPort() { const network = getPower(this.state.powers, 'network'); - const port = await E(network).bindPort( - `/ibc-port/icacontroller-${this.state.icaControllerNonce}`, - ); - this.state.icaControllerNonce += 1; - return port; + const portAllocator = await E(network).getPortAllocator(); + return E(portAllocator).allocateICAControllerPort(); }, }, public: { diff --git a/packages/orchestration/src/proposals/orchestration-proposal.js b/packages/orchestration/src/proposals/orchestration-proposal.js index d9f3922692d..2c411797f6d 100644 --- a/packages/orchestration/src/proposals/orchestration-proposal.js +++ b/packages/orchestration/src/proposals/orchestration-proposal.js @@ -48,9 +48,8 @@ export const setupOrchestrationVat = async ( await networkVat; /** @type {AttenuatedNetwork} */ const network = Far('Attenuated Network', { - /** @param {string} localAddr */ - async bindPort(localAddr) { - return E(networkVat).bindPort(localAddr); + async getPortAllocator() { + return E(networkVat).getPortAllocator(); }, }); diff --git a/packages/vats/src/proposals/network-proposal.js b/packages/vats/src/proposals/network-proposal.js index 00554842eee..434926c9068 100644 --- a/packages/vats/src/proposals/network-proposal.js +++ b/packages/vats/src/proposals/network-proposal.js @@ -77,7 +77,7 @@ export const registerNetworkProtocols = async (vats, dibcBridgeManager) => { * * @param {BootstrapPowers & { * consume: { loadCriticalVat: VatLoader }; - * produce: { networkVat: Producer }; + * produce: { portAllocator: Producer }; * }} powers * @param {object} options * @param {{ networkRef: VatSourceRef; ibcRef: VatSourceRef }} options.options @@ -100,7 +100,7 @@ export const setupNetworkProtocols = async ( provisioning, vatUpgradeInfo: vatUpgradeInfoP, }, - produce: { networkVat, vatUpgradeInfo: produceVatUpgradeInfo }, + produce: { portAllocator, vatUpgradeInfo: produceVatUpgradeInfo }, }, options, ) => { @@ -121,9 +121,11 @@ export const setupNetworkProtocols = async ( info.init('ibc', ibcRef); info.init('network', networkRef); - networkVat.reset(); - networkVat.resolve(vats.network); - const portAllocator = E(vats.network).getPortAllocator(); + const allocator = E(vats.network).getPortAllocator(); + + portAllocator.reset(); + portAllocator.resolve(allocator); + const bridgeManager = await bridgeManagerP; const dibcBridgeManager = bridgeManager && E(bridgeManager).register(BRIDGE_ID.DIBC); @@ -136,10 +138,10 @@ export const setupNetworkProtocols = async ( const ibcportP = []; for (let i = 0; i < NUM_IBC_PORTS_PER_CLIENT; i += 1) { if (i === NUM_IBC_PORTS_PER_CLIENT - 1) { - const portP = when(E(portAllocator).allocateICAControllerPort()); + const portP = when(E(allocator).allocateICAControllerPort()); ibcportP.push(portP); } else { - const portP = when(E(portAllocator).allocateIBCPort()); + const portP = when(E(allocator).allocateIBCPort()); ibcportP.push(portP); } } @@ -152,7 +154,7 @@ export const setupNetworkProtocols = async ( await registerNetworkProtocols(vats, dibcBridgeManager); // Add an echo listener on our ibc-port network (whether real or virtual). - const echoPort = await when(E(vats.network).bindPort('/ibc-port/echo')); + const echoPort = await when(E(allocator).allocateICAControllerPort()); const { listener } = await E(vats.network).makeEchoConnectionKit(); await when(E(echoPort).addListener(listener)); return E(client).assignBundle([_a => ({ ibcport: makePorts() })]); @@ -168,10 +170,8 @@ export const getManifestForNetwork = (_powers, { networkRef, ibcRef }) => ({ zoe: 'zoe', provisioning: 'provisioning', vatUpgradeInfo: true, - portAllocator: 'portAllocator', }, produce: { - networkVat: 'network', vatUpgradeInfo: true, }, zone: true, diff --git a/packages/vats/src/vat-network.js b/packages/vats/src/vat-network.js index 97fde100337..176d74a3043 100644 --- a/packages/vats/src/vat-network.js +++ b/packages/vats/src/vat-network.js @@ -41,8 +41,6 @@ export function buildRootObject(_vatPowers, _args, baggage) { /** @param {Parameters} args */ unregisterProtocolHandler: (...args) => protocol.unregisterProtocolHandler(...args), - /** @param {Parameters} args */ - bindPort: (...args) => protocol.bindPort(...args), getPortAllocator() { return portAllocator; }, From 787fb5b9972f5bfe06d19c933535685a158a83de Mon Sep 17 00:00:00 2001 From: Ikenna Omekam Date: Tue, 23 Apr 2024 12:32:11 -0500 Subject: [PATCH 03/16] feat: add portAllocator to promise space --- packages/network/src/network.js | 1 + packages/network/src/types.js | 7 +++++++ packages/orchestration/src/orchestration.js | 8 +++----- .../src/proposals/orchestration-proposal.js | 18 +++++++++--------- packages/orchestration/src/types.d.ts | 4 +--- .../pegasus/src/proposals/core-proposal.js | 6 +++--- .../vats/src/proposals/network-proposal.js | 8 +++++--- packages/vats/test/test-network.js | 4 +++- 8 files changed, 32 insertions(+), 24 deletions(-) diff --git a/packages/network/src/network.js b/packages/network/src/network.js index d5b287f988d..2446f5e9c23 100644 --- a/packages/network/src/network.js +++ b/packages/network/src/network.js @@ -1438,6 +1438,7 @@ export const preparePortAllocator = (zone, { watch }) => { M.interface('PortAllocator', { allocateIBCPort: M.callWhen().returns(Shape.Vow$(Shape.Port)), allocateICAControllerPort: M.callWhen().returns(Shape.Vow$(Shape.Port)), + allocateIBCPegasusPort: M.callWhen().returns(Shape.Vow$(Shape.Port)), }), ({ protocol }) => ({ protocol, lastICAPortNum: 0n }), { diff --git a/packages/network/src/types.js b/packages/network/src/types.js index 04206a67d5f..731b85abc6f 100644 --- a/packages/network/src/types.js +++ b/packages/network/src/types.js @@ -188,3 +188,10 @@ * ) => PromiseVow} outbound * Create an outbound connection */ + +/** + * @typedef {object} PortAllocator + * @property {() => PromiseVow} allocateIBCPort + * @property {() => PromiseVow} allocateICAControllerPort + * @property {() => PromiseVow} allocateIBCPegasusPort + */ diff --git a/packages/orchestration/src/orchestration.js b/packages/orchestration/src/orchestration.js index fae53bc3fe4..72ed1757d71 100644 --- a/packages/orchestration/src/orchestration.js +++ b/packages/orchestration/src/orchestration.js @@ -9,7 +9,6 @@ import { makeTxPacket, parsePacketAck } from './utils/tx.js'; import '@agoric/network/exported.js'; /** - * @import { AttenuatedNetwork } from './types.js'; * @import { IBCConnectionID } from '@agoric/vats'; * @import { Zone } from '@agoric/base-zone'; * @import { TxBody } from '@agoric/cosmic-proto/cosmos/tx/v1beta1/tx.js'; @@ -25,7 +24,7 @@ const trace = makeTracer('Orchestration'); /** * @typedef {object} OrchestrationPowers - * @property {ERef} network + * @property {PortAllocator} portAllocator */ /** @@ -203,13 +202,12 @@ const prepareOrchestration = (zone, createChainAccount) => powers.init(/** @type {keyof OrchestrationPowers} */ (name), power); } } - return { powers, icaControllerNonce: 0 }; + return { powers }; }, { self: { async bindPort() { - const network = getPower(this.state.powers, 'network'); - const portAllocator = await E(network).getPortAllocator(); + const portAllocator = getPower(this.state.powers, 'portAllocator'); return E(portAllocator).allocateICAControllerPort(); }, }, diff --git a/packages/orchestration/src/proposals/orchestration-proposal.js b/packages/orchestration/src/proposals/orchestration-proposal.js index 2c411797f6d..2c6cf6f6cf5 100644 --- a/packages/orchestration/src/proposals/orchestration-proposal.js +++ b/packages/orchestration/src/proposals/orchestration-proposal.js @@ -8,7 +8,7 @@ import { Far } from '@endo/far'; * @param {BootstrapPowers & { * consume: { * loadCriticalVat: VatLoader; - * networkVat: NetworkVat; + * portAllocator: PortAllocator; * }; * produce: { * orchestration: Producer; @@ -25,7 +25,7 @@ import { Far } from '@endo/far'; */ export const setupOrchestrationVat = async ( { - consume: { loadCriticalVat, networkVat }, + consume: { loadCriticalVat, portAllocator }, produce: { orchestrationVat, orchestration, @@ -45,16 +45,16 @@ export const setupOrchestrationVat = async ( orchestrationVat.reset(); orchestrationVat.resolve(vats.orchestration); - await networkVat; - /** @type {AttenuatedNetwork} */ - const network = Far('Attenuated Network', { - async getPortAllocator() { - return E(networkVat).getPortAllocator(); + await portAllocator; + + const allocator = Far('PortAllocator', { + async allocateICAControllerPort() { + return E(portAllocator).allocateICAControllerPort(); }, }); const newOrchestrationKit = await E(vats.orchestration).makeOrchestration({ - network, + portAllocator: allocator, }); orchestration.reset(); @@ -83,7 +83,7 @@ export const getManifestForOrchestration = (_powers, { orchestrationRef }) => ({ [setupOrchestrationVat.name]: { consume: { loadCriticalVat: true, - networkVat: true, + portAllocator: 'portAllocator', }, produce: { orchestration: 'orchestration', diff --git a/packages/orchestration/src/types.d.ts b/packages/orchestration/src/types.d.ts index e787c2f5f56..6c8678f089c 100644 --- a/packages/orchestration/src/types.d.ts +++ b/packages/orchestration/src/types.d.ts @@ -1,6 +1,4 @@ -import type { RouterProtocol } from '@agoric/network/src/router'; - -export type AttenuatedNetwork = Pick; +import type { PortAllocator } from '@agoric/network/src/network'; export type * from './orchestration.js'; export type * from './vat-orchestration.js'; diff --git a/packages/pegasus/src/proposals/core-proposal.js b/packages/pegasus/src/proposals/core-proposal.js index e2f906d3c7c..29ef181c25e 100644 --- a/packages/pegasus/src/proposals/core-proposal.js +++ b/packages/pegasus/src/proposals/core-proposal.js @@ -19,7 +19,7 @@ export const getManifestForPegasus = ({ restoreRef }, { pegasusRef }) => ({ }, }, listenPegasus: { - consume: { networkVat: t, pegasusConnectionsAdmin: t, zoe: t }, + consume: { portAllocator: t, pegasusConnectionsAdmin: t, zoe: t }, produce: { pegasusConnections: t, pegasusConnectionsAdmin: t }, instance: { consume: { [CONTRACT_NAME]: t }, @@ -93,7 +93,7 @@ export const addPegasusTransferPort = async ( harden(addPegasusTransferPort); export const listenPegasus = async ({ - consume: { networkVat, pegasusConnectionsAdmin: pegasusNameAdmin, zoe }, + consume: { portAllocator, pegasusConnectionsAdmin: pegasusNameAdmin, zoe }, produce: { pegasusConnections, pegasusConnectionsAdmin }, instance: { consume: { [CONTRACT_NAME]: pegasusInstance }, @@ -104,7 +104,7 @@ export const listenPegasus = async ({ pegasusConnectionsAdmin.resolve(nameAdmin); const pegasus = await E(zoe).getPublicFacet(pegasusInstance); - const port = await E(networkVat).bindPort('/ibc-port/pegasus'); + const port = await E(portAllocator).allocateIBCPegasusPort(); return addPegasusTransferPort(port, pegasus, pegasusNameAdmin); }; harden(listenPegasus); diff --git a/packages/vats/src/proposals/network-proposal.js b/packages/vats/src/proposals/network-proposal.js index 434926c9068..94249b81f71 100644 --- a/packages/vats/src/proposals/network-proposal.js +++ b/packages/vats/src/proposals/network-proposal.js @@ -12,7 +12,6 @@ import { makeScalarBigMapStore } from '@agoric/vat-data'; import { when } from '@agoric/vat-data/vow.js'; const NUM_IBC_PORTS_PER_CLIENT = 3; -const INTERCHAIN_ACCOUNT_CONTROLLER_PORT_PREFIX = 'icacontroller-'; /** * @param {SoloVats | NetVats} vats @@ -121,10 +120,12 @@ export const setupNetworkProtocols = async ( info.init('ibc', ibcRef); info.init('network', networkRef); - const allocator = E(vats.network).getPortAllocator(); + const portAllocatorP = E(vats.network).getPortAllocator(); portAllocator.reset(); - portAllocator.resolve(allocator); + portAllocator.resolve(portAllocatorP); + + const allocator = await portAllocatorP; const bridgeManager = await bridgeManagerP; const dibcBridgeManager = @@ -172,6 +173,7 @@ export const getManifestForNetwork = (_powers, { networkRef, ibcRef }) => ({ vatUpgradeInfo: true, }, produce: { + portAllocator: 'portAllocator', vatUpgradeInfo: true, }, zone: true, diff --git a/packages/vats/test/test-network.js b/packages/vats/test/test-network.js index 6bc41bffb17..ca07074d983 100644 --- a/packages/vats/test/test-network.js +++ b/packages/vats/test/test-network.js @@ -125,10 +125,12 @@ test('network - ibc', async t => { bridgeHandler, ); + const portAllocator = await E(networkVat).getPortAllocator(); + // Actually test the ibc port binding. // TODO: Do more tests on the returned Port object. t.log('Opening a Listening Port'); - const p = await when(E(networkVat).bindPort('/ibc-port/')); + const p = await when(E(portAllocator).allocateIBCPort()); const ev1 = await events.next(); t.assert(!ev1.done); t.deepEqual(ev1.value, ['bindPort', { packet: { source_port: 'port-1' } }]); From 1dc5ceff9a78509c7108f956a5a285a20f0f5053 Mon Sep 17 00:00:00 2001 From: Ikenna Omekam Date: Tue, 23 Apr 2024 12:53:28 -0500 Subject: [PATCH 04/16] fix: correct zone --- packages/network/test/test-network-misc.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/network/test/test-network-misc.js b/packages/network/test/test-network-misc.js index 73433a4823e..2febcd2c2d0 100644 --- a/packages/network/test/test-network-misc.js +++ b/packages/network/test/test-network-misc.js @@ -169,7 +169,9 @@ test('handled protocol', async t => { }); test('verify port allocation', async t => { - const zone = makeDurableZone(provideBaggage('network-handled-protocol')); + const zone = makeDurableZone( + provideBaggage('network-verify-port-allocation'), + ); const powers = prepareVowTools(zone); const { when } = powers; const makeNetworkProtocol = prepareNetworkProtocol(zone, powers); From 4a12122d4102e56f46c4b13a0a190ec5098204ea Mon Sep 17 00:00:00 2001 From: Ikenna Omekam Date: Tue, 23 Apr 2024 13:07:16 -0500 Subject: [PATCH 05/16] fix: linting --- packages/orchestration/src/orchestration.js | 3 ++- .../orchestration/src/proposals/orchestration-proposal.js | 5 +++-- packages/orchestration/src/types.d.ts | 5 +++++ packages/vats/src/vat-network.js | 2 +- 4 files changed, 11 insertions(+), 4 deletions(-) diff --git a/packages/orchestration/src/orchestration.js b/packages/orchestration/src/orchestration.js index 72ed1757d71..96bc3f262ca 100644 --- a/packages/orchestration/src/orchestration.js +++ b/packages/orchestration/src/orchestration.js @@ -9,6 +9,7 @@ import { makeTxPacket, parsePacketAck } from './utils/tx.js'; import '@agoric/network/exported.js'; /** + * @import { AttenuatedPortAllocator } from './types.js'; * @import { IBCConnectionID } from '@agoric/vats'; * @import { Zone } from '@agoric/base-zone'; * @import { TxBody } from '@agoric/cosmic-proto/cosmos/tx/v1beta1/tx.js'; @@ -24,7 +25,7 @@ const trace = makeTracer('Orchestration'); /** * @typedef {object} OrchestrationPowers - * @property {PortAllocator} portAllocator + * @property {ERef} portAllocator */ /** diff --git a/packages/orchestration/src/proposals/orchestration-proposal.js b/packages/orchestration/src/proposals/orchestration-proposal.js index 2c6cf6f6cf5..e2b342e623c 100644 --- a/packages/orchestration/src/proposals/orchestration-proposal.js +++ b/packages/orchestration/src/proposals/orchestration-proposal.js @@ -2,13 +2,13 @@ import { V as E } from '@agoric/vat-data/vow.js'; import { Far } from '@endo/far'; -/** @import { AttenuatedNetwork, Orchestration, OrchestrationVat } from '../types' */ +/** @import { AttenuatedPortAllocator, Orchestration, OrchestrationVat } from '../types' */ /** * @param {BootstrapPowers & { * consume: { * loadCriticalVat: VatLoader; - * portAllocator: PortAllocator; + * portAllocator: AttenuatedPortAllocator; * }; * produce: { * orchestration: Producer; @@ -47,6 +47,7 @@ export const setupOrchestrationVat = async ( await portAllocator; + /** @type {AttenuatedPortAllocator} */ const allocator = Far('PortAllocator', { async allocateICAControllerPort() { return E(portAllocator).allocateICAControllerPort(); diff --git a/packages/orchestration/src/types.d.ts b/packages/orchestration/src/types.d.ts index 6c8678f089c..74ef4ac3851 100644 --- a/packages/orchestration/src/types.d.ts +++ b/packages/orchestration/src/types.d.ts @@ -1,5 +1,10 @@ import type { PortAllocator } from '@agoric/network/src/network'; +export type AttenuatedPortAllocator = Pick< + PortAllocator, + 'allocateICAControllerPort' +>; + export type * from './orchestration.js'; export type * from './vat-orchestration.js'; export type * from './utils/tx.js'; diff --git a/packages/vats/src/vat-network.js b/packages/vats/src/vat-network.js index 176d74a3043..98c6f9f1b98 100644 --- a/packages/vats/src/vat-network.js +++ b/packages/vats/src/vat-network.js @@ -7,7 +7,7 @@ import { prepareRouterProtocol, } from '@agoric/network'; import { prepareVowTools } from '@agoric/vat-data/vow.js'; -import { E, Far } from '@endo/far'; +import { Far } from '@endo/far'; export function buildRootObject(_vatPowers, _args, baggage) { const zone = makeDurableZone(baggage); From 38bcf1f841316642c6f35935314c24d0153611a4 Mon Sep 17 00:00:00 2001 From: Ikenna Omekam Date: Tue, 23 Apr 2024 13:35:37 -0500 Subject: [PATCH 06/16] fix: use portAllocator instead of networkVat --- packages/boot/test/bootstrapTests/ibcClientMock.js | 7 +++---- packages/boot/test/bootstrapTests/ibcServerMock.js | 7 +++---- packages/boot/test/bootstrapTests/test-net-ibc-upgrade.ts | 6 +++--- 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/packages/boot/test/bootstrapTests/ibcClientMock.js b/packages/boot/test/bootstrapTests/ibcClientMock.js index 734340736e9..2cfbbe92f7b 100644 --- a/packages/boot/test/bootstrapTests/ibcClientMock.js +++ b/packages/boot/test/bootstrapTests/ibcClientMock.js @@ -6,15 +6,14 @@ import { V as E } from '@agoric/vat-data/vow.js'; /** * @param {ZCF} zcf * @param {{ - * networkVat: ERef>; + * portAllocator: ERef; * }} privateArgs * @param {import("@agoric/vat-data").Baggage} _baggage */ export const start = async (zcf, privateArgs, _baggage) => { - const { networkVat } = privateArgs; + const { portAllocator } = privateArgs; - const portAllocator = await E(networkVat).getPortAllocator(); - const myPort = E(portAllocator).allocateIBCPort(); + const myPort = await E(portAllocator).allocateIBCPort(); const { log } = console; let connP; diff --git a/packages/boot/test/bootstrapTests/ibcServerMock.js b/packages/boot/test/bootstrapTests/ibcServerMock.js index eaa5f1c0bf8..2c3d9ab3cb4 100644 --- a/packages/boot/test/bootstrapTests/ibcServerMock.js +++ b/packages/boot/test/bootstrapTests/ibcServerMock.js @@ -12,15 +12,14 @@ const { log } = console; * @param {ZCF} zcf * @param {{ * address: string, - * networkVat: ERef>; + * portAllocator: ERef; * }} privateArgs * @param {import("@agoric/vat-data").Baggage} _baggage */ export const start = async (zcf, privateArgs, _baggage) => { - const { networkVat } = privateArgs; + const { portAllocator } = privateArgs; - const portAllocator = await E(networkVat).getPortAllocator(); - const boundPort = E(portAllocator).allocateIBCPort(); + const boundPort = await E(portAllocator).allocateIBCPort(); /** @type {Array<[label: string, resolve: (value: any) => void, reject: (reason: any) => void]>} */ const queue = []; diff --git a/packages/boot/test/bootstrapTests/test-net-ibc-upgrade.ts b/packages/boot/test/bootstrapTests/test-net-ibc-upgrade.ts index 8b087e31628..dfe36d871ff 100644 --- a/packages/boot/test/bootstrapTests/test-net-ibc-upgrade.ts +++ b/packages/boot/test/bootstrapTests/test-net-ibc-upgrade.ts @@ -113,7 +113,7 @@ const upgradeVats = async (t, EV, vatsToUpgrade) => { test.serial('upgrade at many points in network API flow', async t => { const { installation } = t.context; const { EV } = t.context.runUtils; - const networkVat = await EV.vat('bootstrap').consumeItem('networkVat'); + const portAllocator = await EV.vat('bootstrap').consumeItem('portAllocator'); const zoe: ZoeService = await EV.vat('bootstrap').consumeItem('zoe'); const flow = entries({ @@ -122,7 +122,7 @@ test.serial('upgrade at many points in network API flow', async t => { installation.ibcServerMock, {}, {}, - { networkVat }, + { portAllocator }, ); t.truthy(started.creatorFacet, `${label} ibcServerMock`); return [label, { server: started.creatorFacet }]; @@ -140,7 +140,7 @@ test.serial('upgrade at many points in network API flow', async t => { installation.ibcClientMock, {}, {}, - { networkVat }, + { portAllocator }, ); t.truthy(started.creatorFacet, `${label} ibcClientMock`); return [label, { ...opts, client: started.creatorFacet }]; From 6cb9010a5fed1f64f4737ac90320afddc5d0390c Mon Sep 17 00:00:00 2001 From: Ikenna Omekam Date: Tue, 23 Apr 2024 13:37:17 -0500 Subject: [PATCH 07/16] fix: styling --- packages/network/src/network.js | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/packages/network/src/network.js b/packages/network/src/network.js index 2446f5e9c23..4fd7e1fa77e 100644 --- a/packages/network/src/network.js +++ b/packages/network/src/network.js @@ -1432,8 +1432,8 @@ export function prepareLoopbackProtocolHandler(zone, { watch, allVows }) { * @param {import('@agoric/base-zone').Zone} zone * @param {ReturnType} powers */ -export const preparePortAllocator = (zone, { watch }) => { - const makePortAllocator = zone.exoClass( +export const preparePortAllocator = (zone, { watch }) => + zone.exoClass( 'PortAllocator', M.interface('PortAllocator', { allocateIBCPort: M.callWhen().returns(Shape.Vow$(Shape.Port)), @@ -1463,6 +1463,3 @@ export const preparePortAllocator = (zone, { watch }) => { }, }, ); - - return makePortAllocator; -}; From f9d8ce10a02a1b78803c09777bba9af39bbbbd4a Mon Sep 17 00:00:00 2001 From: Ikenna Omekam Date: Tue, 23 Apr 2024 13:59:12 -0500 Subject: [PATCH 08/16] fix: documentation --- packages/network/README.md | 11 ++++------- packages/vats/tools/boot-test-utils.js | 1 - 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/packages/network/README.md b/packages/network/README.md index f8e9e08a925..3155cecc7c3 100644 --- a/packages/network/README.md +++ b/packages/network/README.md @@ -57,15 +57,12 @@ E(home.ibcport[0]).connect(remoteEndpoint, connectionHandler) The other side of `connect()` is a "listening port". These ports are waiting for inbound connections to be established. -To get a listening port, you need a `NetworkInterface` object (such as the one on your `ag-solo` under `home.network`) and ask it to `bindPort()` to an endpoint. You can either provide a specific port name, or allow the API to allocate a random one for you. The endpoint specifies the type of connection that this port will be able to accept (IBC, TCP, etc), and some properties of that connection. `bindPort()` uses a "multiaddress" to encode this information. +To get a listening port, you need a `NetworkInterface` object (such as the one on your `ag-solo` under `home.network`) and ask it for a port, via the `PortAllocator`. ```js // ask for a random allocation - ends with a slash -E(home.network).bindPort('/ibc-port/') - .then(port => usePort(port)); - -// or ask for a specific port name -E(home.network).bindPort('/ibc-port/my-cool-port-name') +E(home.network).getPortAllocator() + .then(portAllocator => E(portAllocator).allocateIBCPort()) .then(port => usePort(port)); ``` @@ -147,7 +144,7 @@ Note that if you want to listen on this port again, you can just call `port.addL ### Closing the Port Entirely -Removing a listener doesn't release the port address to make it available for other `bindPort()` requests. You can call: +Removing a listener doesn't release the port address to make it available for other `PortAllocator` requests. You can call: ```js port.revoke(); diff --git a/packages/vats/tools/boot-test-utils.js b/packages/vats/tools/boot-test-utils.js index 28c7f12fc47..09afd1bae01 100644 --- a/packages/vats/tools/boot-test-utils.js +++ b/packages/vats/tools/boot-test-utils.js @@ -74,7 +74,6 @@ export const makeMock = log => network: Far('network', { registerProtocolHandler: noop, makeLoopbackProtocolHandler: noop, - bindPort: () => Far('network - listener', { addListener: noop }), }), }, }); From f7a4fac6bfe568b7261573bce118fbc164acd31c Mon Sep 17 00:00:00 2001 From: Ikenna Omekam Date: Tue, 23 Apr 2024 14:21:08 -0500 Subject: [PATCH 09/16] fix: remove network vat from restart --- .../test/bootstrapTests/test-vats-restart.ts | 33 ------------------- .../vats/src/proposals/network-proposal.js | 14 +++----- 2 files changed, 5 insertions(+), 42 deletions(-) diff --git a/packages/boot/test/bootstrapTests/test-vats-restart.ts b/packages/boot/test/bootstrapTests/test-vats-restart.ts index 236ad99b1e7..bd1f3340fef 100644 --- a/packages/boot/test/bootstrapTests/test-vats-restart.ts +++ b/packages/boot/test/bootstrapTests/test-vats-restart.ts @@ -105,20 +105,6 @@ test.serial('run network vat proposal', async t => { t.pass(); // reached here without throws }); -test.serial('register network protocol before upgrade', async t => { - const { EV } = t.context.runUtils; - const net = await EV.vat('bootstrap').consumeItem('networkVat'); - const h1 = await EV(net).makeLoopbackProtocolHandler(); - - t.log('register P1'); - await EV(net).registerProtocolHandler(['P1'], h1); - - t.log('register P1 again? No.'); - await t.throwsAsync(EV(net).registerProtocolHandler(['P1'], h1), { - message: /key "P1" already registered/, - }); -}); - test.serial('make IBC callbacks before upgrade', async t => { const { EV } = t.context.runUtils; const vatStore = await EV.vat('bootstrap').consumeItem('vatStore'); @@ -155,25 +141,6 @@ test.serial('use IBC callbacks after upgrade', async t => { t.truthy(h.bridgeHandler, 'bridgeHandler'); }); -test.serial('networkVat registrations are durable', async t => { - const { EV } = t.context.runUtils; - const net = await EV.vat('bootstrap').consumeItem('networkVat'); - - const h2 = await EV(net).makeLoopbackProtocolHandler(); - t.log('register P1 again? No.'); - await t.throwsAsync(EV(net).registerProtocolHandler(['P1'], h2), { - message: /key "P1" already registered/, - }); - - t.log('IBC protocol handler already registered?'); - await t.throwsAsync( - EV(net).registerProtocolHandler(['/ibc-port', '/ibc-hop'], h2), - { - message: /key "\/ibc-port" already registered in collection "prefix"/, - }, - ); -}); - test.serial('read metrics', async t => { const { EV } = t.context.runUtils; diff --git a/packages/vats/src/proposals/network-proposal.js b/packages/vats/src/proposals/network-proposal.js index 94249b81f71..f168053456f 100644 --- a/packages/vats/src/proposals/network-proposal.js +++ b/packages/vats/src/proposals/network-proposal.js @@ -56,15 +56,11 @@ export const registerNetworkProtocols = async (vats, dibcBridgeManager) => { }; /** - * Create the network and IBC vats; produce `networkVat` in the core / bootstrap - * space. + * Create the network and IBC vats; produce `portAllocator` in the core / + * bootstrap space. * - * The `networkVat` is CLOSELY HELD in the core space, where later, we claim - * ports using `E(networkVat).bindPort(_path_)`. As discussed in - * `ProtocolHandler` docs, _path_ is: - * - * - /ibc-port/NAME for an IBC port with a known name or, - * - /ibc-port/ for an IBC port with a fresh name. + * The `portAllocator` is CLOSELY HELD in the core space, where later, we claim + * ports using `E(portAllocator).allocateIBCPort`, for example. * * Contracts are expected to use the services of the network and IBC vats by way * of such ports. @@ -155,7 +151,7 @@ export const setupNetworkProtocols = async ( await registerNetworkProtocols(vats, dibcBridgeManager); // Add an echo listener on our ibc-port network (whether real or virtual). - const echoPort = await when(E(allocator).allocateICAControllerPort()); + const echoPort = await when(E(allocator).allocateIBCPort()); const { listener } = await E(vats.network).makeEchoConnectionKit(); await when(E(echoPort).addListener(listener)); return E(client).assignBundle([_a => ({ ibcport: makePorts() })]); From 988253b4c651094f469f25ff7aa81f646dc4d3b8 Mon Sep 17 00:00:00 2001 From: Ikenna Omekam Date: Tue, 23 Apr 2024 15:48:53 -0500 Subject: [PATCH 10/16] feat: add local port allocation --- packages/network/src/network.js | 8 +++++++- packages/vats/src/proposals/network-proposal.js | 4 ++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/packages/network/src/network.js b/packages/network/src/network.js index 4fd7e1fa77e..0fe19d628de 100644 --- a/packages/network/src/network.js +++ b/packages/network/src/network.js @@ -1439,6 +1439,7 @@ export const preparePortAllocator = (zone, { watch }) => allocateIBCPort: M.callWhen().returns(Shape.Vow$(Shape.Port)), allocateICAControllerPort: M.callWhen().returns(Shape.Vow$(Shape.Port)), allocateIBCPegasusPort: M.callWhen().returns(Shape.Vow$(Shape.Port)), + allocateLocalPort: M.callWhen().returns(Shape.Vow$(Shape.Port)), }), ({ protocol }) => ({ protocol, lastICAPortNum: 0n }), { @@ -1458,8 +1459,13 @@ export const preparePortAllocator = (zone, { watch }) => }, allocateIBCPegasusPort() { const { state } = this; - // Allocate an IBC port with a unique generated name. + // Allocate a Pegasus IBC port with a unique generated name. return watch(E(state.protocol).bindPort(`/ibc-port/pegasus`)); }, + allocateLocalPort() { + const { state } = this; + // Allocate a local port with a unique generated name. + return watch(E(state.protocol).bindPort(`/local/`)); + }, }, ); diff --git a/packages/vats/src/proposals/network-proposal.js b/packages/vats/src/proposals/network-proposal.js index f168053456f..4a2001980da 100644 --- a/packages/vats/src/proposals/network-proposal.js +++ b/packages/vats/src/proposals/network-proposal.js @@ -67,8 +67,8 @@ export const registerNetworkProtocols = async (vats, dibcBridgeManager) => { * * Testing facilities include: * - * - loopback ports: `E(networkVat).bindPort('/local/')` - * - an echo port: `E(vats.network).bindPort('/ibc-port/echo')` + * - loopback ports: `E(portAllocator).allocateLocalPort()` + * - an echo port: `E(portAllocator).allocateIBCPort()` * * @param {BootstrapPowers & { * consume: { loadCriticalVat: VatLoader }; From 36b520fb4bea55d920322afea24dc76e97e956af Mon Sep 17 00:00:00 2001 From: Ikenna Omekam Date: Thu, 25 Apr 2024 20:49:48 -0500 Subject: [PATCH 11/16] Apply suggestions from code review Co-authored-by: Michael FIG --- packages/network/src/network.js | 2 +- packages/network/src/types.js | 7 +------ 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/packages/network/src/network.js b/packages/network/src/network.js index 0fe19d628de..68e039f6d58 100644 --- a/packages/network/src/network.js +++ b/packages/network/src/network.js @@ -1459,7 +1459,7 @@ export const preparePortAllocator = (zone, { watch }) => }, allocateIBCPegasusPort() { const { state } = this; - // Allocate a Pegasus IBC port with a unique generated name. + // Allocate the singleton Pegasus IBC port. return watch(E(state.protocol).bindPort(`/ibc-port/pegasus`)); }, allocateLocalPort() { diff --git a/packages/network/src/types.js b/packages/network/src/types.js index 731b85abc6f..5de3319506c 100644 --- a/packages/network/src/types.js +++ b/packages/network/src/types.js @@ -189,9 +189,4 @@ * Create an outbound connection */ -/** - * @typedef {object} PortAllocator - * @property {() => PromiseVow} allocateIBCPort - * @property {() => PromiseVow} allocateICAControllerPort - * @property {() => PromiseVow} allocateIBCPegasusPort - */ +/** @typedef {ReturnType>} PortAllocator */ From 96b8508339d27877a4f7f6c714a47db02232b6f9 Mon Sep 17 00:00:00 2001 From: Ikenna Omekam Date: Thu, 25 Apr 2024 21:28:45 -0500 Subject: [PATCH 12/16] refactor: PR feedback --- packages/network/src/types.js | 2 +- packages/network/test/test-network-misc.js | 4 ++-- .../src/proposals/orchestration-proposal.js | 17 ++++------------- packages/orchestration/src/service.js | 5 +++-- packages/orchestration/src/types.d.ts | 6 ------ 5 files changed, 10 insertions(+), 24 deletions(-) diff --git a/packages/network/src/types.js b/packages/network/src/types.js index 5de3319506c..43e0e90c366 100644 --- a/packages/network/src/types.js +++ b/packages/network/src/types.js @@ -189,4 +189,4 @@ * Create an outbound connection */ -/** @typedef {ReturnType>} PortAllocator */ +/** @typedef {ReturnType>} PortAllocator */ diff --git a/packages/network/test/test-network-misc.js b/packages/network/test/test-network-misc.js index 2febcd2c2d0..2db5786dd22 100644 --- a/packages/network/test/test-network-misc.js +++ b/packages/network/test/test-network-misc.js @@ -54,7 +54,7 @@ const prepareProtocolHandler = ( }, async generatePortID() { this.state.nonce += 1; - return `${this.state.nonce}`; + return `port-${this.state.nonce}`; }, async onBind(port, localAddr) { t.assert(port, `port is supplied to onBind`); @@ -187,7 +187,7 @@ test('verify port allocation', async t => { const portAllocator = makePortAllocator({ protocol }); const ibcPort = await when(portAllocator.allocateIBCPort()); - t.is(ibcPort.getLocalAddress(), '/ibc-port/1'); + t.is(ibcPort.getLocalAddress(), '/ibc-port/port-1'); const icaControllerPort1 = await when( portAllocator.allocateICAControllerPort(), diff --git a/packages/orchestration/src/proposals/orchestration-proposal.js b/packages/orchestration/src/proposals/orchestration-proposal.js index cfe416cc748..c889383bcda 100644 --- a/packages/orchestration/src/proposals/orchestration-proposal.js +++ b/packages/orchestration/src/proposals/orchestration-proposal.js @@ -1,9 +1,7 @@ // @ts-check import { V as E } from '@agoric/vat-data/vow.js'; -import { Far } from '@endo/far'; /** - * @import { AttenuatedPortAllocator } from '../types' * @import { OrchestrationService } from '../service.js' * @import { OrchestrationVat } from '../vat-orchestration.js' */ @@ -12,7 +10,7 @@ import { Far } from '@endo/far'; * @param {BootstrapPowers & { * consume: { * loadCriticalVat: VatLoader; - * portAllocator: AttenuatedPortAllocator; + * portAllocator: PortAllocator; * }; * produce: { * orchestration: Producer; @@ -29,7 +27,7 @@ import { Far } from '@endo/far'; */ export const setupOrchestrationVat = async ( { - consume: { loadCriticalVat, portAllocator }, + consume: { loadCriticalVat, portAllocator: portAllocatorP }, produce: { orchestrationVat, orchestration, @@ -49,17 +47,10 @@ export const setupOrchestrationVat = async ( orchestrationVat.reset(); orchestrationVat.resolve(vats.orchestration); - await portAllocator; - - /** @type {AttenuatedPortAllocator} */ - const allocator = Far('PortAllocator', { - async allocateICAControllerPort() { - return E(portAllocator).allocateICAControllerPort(); - }, - }); + const portAllocator = await portAllocatorP; const newOrchestrationKit = await E(vats.orchestration).makeOrchestration({ - portAllocator: allocator, + portAllocator, }); orchestration.reset(); diff --git a/packages/orchestration/src/service.js b/packages/orchestration/src/service.js index 43fb19567e8..ced55999021 100644 --- a/packages/orchestration/src/service.js +++ b/packages/orchestration/src/service.js @@ -11,10 +11,11 @@ import { makeICAConnectionAddress, parseAddress } from './utils/address.js'; import { makeTxPacket, parsePacketAck } from './utils/tx.js'; /** - * @import { AttenuatedPortAllocator, ChainAccount, ChainAddress } from './types.js'; + * @import { ChainAccount, ChainAddress } from './types.js'; * @import { IBCConnectionID } from '@agoric/vats'; * @import { Zone } from '@agoric/base-zone'; * @import { TxBody } from '@agoric/cosmic-proto/cosmos/tx/v1beta1/tx.js'; + * */ const { Fail, bare } = assert; @@ -24,7 +25,7 @@ const trace = makeTracer('Orchestration'); /** * @typedef {object} OrchestrationPowers - * @property {ERef} portAllocator + * @property {ERef} portAllocator */ /** diff --git a/packages/orchestration/src/types.d.ts b/packages/orchestration/src/types.d.ts index 398bc700e91..bd3de46d26e 100644 --- a/packages/orchestration/src/types.d.ts +++ b/packages/orchestration/src/types.d.ts @@ -12,12 +12,6 @@ import type { Redelegation, UnbondingDelegation, } from '@agoric/cosmic-proto/cosmos/staking/v1beta1/staking.js'; -import type { PortAllocator } from '@agoric/network/src/network'; - -export type AttenuatedPortAllocator = Pick< - PortAllocator, - 'allocateICAControllerPort' ->; /** * static declaration of known chain types will allow type support for From 252a503b1b6176df153593486c0a03fba0887707 Mon Sep 17 00:00:00 2001 From: Ikenna Omekam Date: Fri, 26 Apr 2024 18:27:32 -0500 Subject: [PATCH 13/16] feat: add param to allocateIBCPort --- packages/network/src/network.js | 35 ++++++++++++------- packages/network/test/test-network-misc.js | 9 +++++ .../pegasus/src/proposals/core-proposal.js | 2 +- .../vats/src/proposals/network-proposal.js | 5 +-- 4 files changed, 36 insertions(+), 15 deletions(-) diff --git a/packages/network/src/network.js b/packages/network/src/network.js index 68e039f6d58..58ebb9e6d06 100644 --- a/packages/network/src/network.js +++ b/packages/network/src/network.js @@ -1436,17 +1436,26 @@ export const preparePortAllocator = (zone, { watch }) => zone.exoClass( 'PortAllocator', M.interface('PortAllocator', { - allocateIBCPort: M.callWhen().returns(Shape.Vow$(Shape.Port)), + allocateIBCPort: M.callWhen() + .optional(M.string()) + .returns(Shape.Vow$(Shape.Port)), allocateICAControllerPort: M.callWhen().returns(Shape.Vow$(Shape.Port)), - allocateIBCPegasusPort: M.callWhen().returns(Shape.Vow$(Shape.Port)), - allocateLocalPort: M.callWhen().returns(Shape.Vow$(Shape.Port)), + allocateLocalPort: M.callWhen() + .optional(M.string()) + .returns(Shape.Vow$(Shape.Port)), }), ({ protocol }) => ({ protocol, lastICAPortNum: 0n }), { - allocateIBCPort() { + allocateIBCPort(specifiedName = '') { const { state } = this; + let localAddr = `/ibc-port/${specifiedName}`; + + if (specifiedName) { + localAddr = `/ibc-port/custom-${specifiedName}`; + } + // Allocate an IBC port with a unique generated name. - return watch(E(state.protocol).bindPort(`/ibc-port/`)); + return watch(E(state.protocol).bindPort(localAddr)); }, allocateICAControllerPort() { const { state } = this; @@ -1457,15 +1466,17 @@ export const preparePortAllocator = (zone, { watch }) => ), ); }, - allocateIBCPegasusPort() { - const { state } = this; - // Allocate the singleton Pegasus IBC port. - return watch(E(state.protocol).bindPort(`/ibc-port/pegasus`)); - }, - allocateLocalPort() { + allocateLocalPort(specifiedName = '') { const { state } = this; + + let localAddr = `/local/${specifiedName}`; + + if (specifiedName) { + localAddr = `/local/custom-${specifiedName}`; + } + // Allocate a local port with a unique generated name. - return watch(E(state.protocol).bindPort(`/local/`)); + return watch(E(state.protocol).bindPort(localAddr)); }, }, ); diff --git a/packages/network/test/test-network-misc.js b/packages/network/test/test-network-misc.js index 2db5786dd22..20a83e622b0 100644 --- a/packages/network/test/test-network-misc.js +++ b/packages/network/test/test-network-misc.js @@ -189,6 +189,9 @@ test('verify port allocation', async t => { const ibcPort = await when(portAllocator.allocateIBCPort()); t.is(ibcPort.getLocalAddress(), '/ibc-port/port-1'); + const namedIbcPort = await when(portAllocator.allocateIBCPort('test-1')); + t.is(namedIbcPort.getLocalAddress(), '/ibc-port/custom-test-1'); + const icaControllerPort1 = await when( portAllocator.allocateICAControllerPort(), ); @@ -198,6 +201,12 @@ test('verify port allocation', async t => { portAllocator.allocateICAControllerPort(), ); t.is(icaControllerPort2.getLocalAddress(), '/ibc-port/icacontroller-2'); + + const localPort = await when(portAllocator.allocateLocalPort()); + t.is(localPort.getLocalAddress(), '/local/port-5'); + + const namedLocalPort = await when(portAllocator.allocateLocalPort('local-1')); + t.is(namedLocalPort.getLocalAddress(), '/local/custom-local-1'); }); test('protocol connection listen', async t => { diff --git a/packages/pegasus/src/proposals/core-proposal.js b/packages/pegasus/src/proposals/core-proposal.js index 29ef181c25e..9e6007574b6 100644 --- a/packages/pegasus/src/proposals/core-proposal.js +++ b/packages/pegasus/src/proposals/core-proposal.js @@ -104,7 +104,7 @@ export const listenPegasus = async ({ pegasusConnectionsAdmin.resolve(nameAdmin); const pegasus = await E(zoe).getPublicFacet(pegasusInstance); - const port = await E(portAllocator).allocateIBCPegasusPort(); + const port = await E(portAllocator).allocateIBCPort('pegasus'); return addPegasusTransferPort(port, pegasus, pegasusNameAdmin); }; harden(listenPegasus); diff --git a/packages/vats/src/proposals/network-proposal.js b/packages/vats/src/proposals/network-proposal.js index 4a2001980da..50f11dd3e90 100644 --- a/packages/vats/src/proposals/network-proposal.js +++ b/packages/vats/src/proposals/network-proposal.js @@ -68,7 +68,8 @@ export const registerNetworkProtocols = async (vats, dibcBridgeManager) => { * Testing facilities include: * * - loopback ports: `E(portAllocator).allocateLocalPort()` - * - an echo port: `E(portAllocator).allocateIBCPort()` + * - an echo port: `E(portAllocator).allocateIBCPort("echo")d + * /ibc-port/custom-echo * * @param {BootstrapPowers & { * consume: { loadCriticalVat: VatLoader }; @@ -151,7 +152,7 @@ export const setupNetworkProtocols = async ( await registerNetworkProtocols(vats, dibcBridgeManager); // Add an echo listener on our ibc-port network (whether real or virtual). - const echoPort = await when(E(allocator).allocateIBCPort()); + const echoPort = await when(E(allocator).allocateIBCPort('echo')); const { listener } = await E(vats.network).makeEchoConnectionKit(); await when(E(echoPort).addListener(listener)); return E(client).assignBundle([_a => ({ ibcport: makePorts() })]); From 7e69bedce4fdc8bf5f8fab364ace00bb6114f9f4 Mon Sep 17 00:00:00 2001 From: Ikenna Omekam Date: Fri, 26 Apr 2024 18:39:49 -0500 Subject: [PATCH 14/16] add IBC port name validation --- packages/network/src/network.js | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/packages/network/src/network.js b/packages/network/src/network.js index 58ebb9e6d06..23f589fb519 100644 --- a/packages/network/src/network.js +++ b/packages/network/src/network.js @@ -1451,6 +1451,15 @@ export const preparePortAllocator = (zone, { watch }) => let localAddr = `/ibc-port/${specifiedName}`; if (specifiedName) { + // Contains at least two characters and only allows valid characters specified in IBC spec + const match = specifiedName.match( + new RegExp('^[a-zA-Z0-9.,_+\\-#<>\\[\\]]{2,128}$'), + ); + + if (!match) { + throw new Error(`Invalid IBC port name: ${specifiedName}`); + } + localAddr = `/ibc-port/custom-${specifiedName}`; } @@ -1472,6 +1481,15 @@ export const preparePortAllocator = (zone, { watch }) => let localAddr = `/local/${specifiedName}`; if (specifiedName) { + // Contains at least two characters and only allows valid characters specified in IBC spec + const match = specifiedName.match( + new RegExp('^[a-zA-Z0-9.,_+\\-#<>\\[\\]]{2,128}$'), + ); + + if (!match) { + throw new Error(`Invalid IBC port name: ${specifiedName}`); + } + localAddr = `/local/custom-${specifiedName}`; } From a7993a8fb9fd5c9bac4a8f122d95aded01303759 Mon Sep 17 00:00:00 2001 From: Ikenna Omekam Date: Fri, 26 Apr 2024 19:39:40 -0500 Subject: [PATCH 15/16] feat: add validation of port name --- packages/network/src/network.js | 36 +++++++++++----------- packages/network/test/test-network-misc.js | 4 +++ 2 files changed, 22 insertions(+), 18 deletions(-) diff --git a/packages/network/src/network.js b/packages/network/src/network.js index 23f589fb519..66404d7e508 100644 --- a/packages/network/src/network.js +++ b/packages/network/src/network.js @@ -46,6 +46,20 @@ export function getPrefixes(addr) { return ret; } +/** + * Validate IBC port name + * @param {string} specifiedName + */ +function throwIfInvalidPortName(specifiedName) { + // Contains between 2 and 128 characters + // Can contain alphanumeric characters + // Valid symbols: ., ,, _, +, -, #, [, ], <, > + const portNameRegex = new RegExp('^[a-zA-Z0-9.,_+\\-#<>\\[\\]]{2,128}$'); + if (!portNameRegex.test(specifiedName)) { + throw new Error(`Invalid IBC port name: ${specifiedName}`); + } +} + /** * @typedef {object} ConnectionOpts * @property {Endpoint[]} addrs @@ -1448,17 +1462,10 @@ export const preparePortAllocator = (zone, { watch }) => { allocateIBCPort(specifiedName = '') { const { state } = this; - let localAddr = `/ibc-port/${specifiedName}`; + let localAddr = `/ibc-port/`; if (specifiedName) { - // Contains at least two characters and only allows valid characters specified in IBC spec - const match = specifiedName.match( - new RegExp('^[a-zA-Z0-9.,_+\\-#<>\\[\\]]{2,128}$'), - ); - - if (!match) { - throw new Error(`Invalid IBC port name: ${specifiedName}`); - } + throwIfInvalidPortName(specifiedName); localAddr = `/ibc-port/custom-${specifiedName}`; } @@ -1478,17 +1485,10 @@ export const preparePortAllocator = (zone, { watch }) => allocateLocalPort(specifiedName = '') { const { state } = this; - let localAddr = `/local/${specifiedName}`; + let localAddr = `/local/`; if (specifiedName) { - // Contains at least two characters and only allows valid characters specified in IBC spec - const match = specifiedName.match( - new RegExp('^[a-zA-Z0-9.,_+\\-#<>\\[\\]]{2,128}$'), - ); - - if (!match) { - throw new Error(`Invalid IBC port name: ${specifiedName}`); - } + throwIfInvalidPortName(specifiedName); localAddr = `/local/custom-${specifiedName}`; } diff --git a/packages/network/test/test-network-misc.js b/packages/network/test/test-network-misc.js index 20a83e622b0..414fbafcc04 100644 --- a/packages/network/test/test-network-misc.js +++ b/packages/network/test/test-network-misc.js @@ -207,6 +207,10 @@ test('verify port allocation', async t => { const namedLocalPort = await when(portAllocator.allocateLocalPort('local-1')); t.is(namedLocalPort.getLocalAddress(), '/local/custom-local-1'); + + await t.throwsAsync(when(portAllocator.allocateIBCPort('/test-1')), { + message: 'Invalid IBC port name: /test-1', + }); }); test('protocol connection listen', async t => { From 4a64a8a903c12f568aa4e5188e7c225f5bd45934 Mon Sep 17 00:00:00 2001 From: Ikenna Omekam Date: Tue, 30 Apr 2024 12:52:09 -0400 Subject: [PATCH 16/16] refactor: add custom to name --- packages/boot/test/bootstrapTests/ibcClientMock.js | 2 +- packages/boot/test/bootstrapTests/ibcServerMock.js | 2 +- packages/network/README.md | 2 +- packages/network/src/network.js | 8 ++++---- packages/network/test/test-network-misc.js | 14 +++++++++----- packages/pegasus/src/proposals/core-proposal.js | 2 +- packages/vats/src/proposals/network-proposal.js | 12 ++++++------ packages/vats/test/test-network.js | 2 +- 8 files changed, 24 insertions(+), 20 deletions(-) diff --git a/packages/boot/test/bootstrapTests/ibcClientMock.js b/packages/boot/test/bootstrapTests/ibcClientMock.js index 2cfbbe92f7b..33458da604e 100644 --- a/packages/boot/test/bootstrapTests/ibcClientMock.js +++ b/packages/boot/test/bootstrapTests/ibcClientMock.js @@ -13,7 +13,7 @@ import { V as E } from '@agoric/vat-data/vow.js'; export const start = async (zcf, privateArgs, _baggage) => { const { portAllocator } = privateArgs; - const myPort = await E(portAllocator).allocateIBCPort(); + const myPort = await E(portAllocator).allocateCustomIBCPort(); const { log } = console; let connP; diff --git a/packages/boot/test/bootstrapTests/ibcServerMock.js b/packages/boot/test/bootstrapTests/ibcServerMock.js index 2c3d9ab3cb4..1370146c65e 100644 --- a/packages/boot/test/bootstrapTests/ibcServerMock.js +++ b/packages/boot/test/bootstrapTests/ibcServerMock.js @@ -19,7 +19,7 @@ const { log } = console; export const start = async (zcf, privateArgs, _baggage) => { const { portAllocator } = privateArgs; - const boundPort = await E(portAllocator).allocateIBCPort(); + const boundPort = await E(portAllocator).allocateCustomIBCPort(); /** @type {Array<[label: string, resolve: (value: any) => void, reject: (reason: any) => void]>} */ const queue = []; diff --git a/packages/network/README.md b/packages/network/README.md index 3155cecc7c3..55d813e7612 100644 --- a/packages/network/README.md +++ b/packages/network/README.md @@ -62,7 +62,7 @@ To get a listening port, you need a `NetworkInterface` object (such as the one o ```js // ask for a random allocation - ends with a slash E(home.network).getPortAllocator() - .then(portAllocator => E(portAllocator).allocateIBCPort()) + .then(portAllocator => E(portAllocator).allocateCustomIBCPort()) .then(port => usePort(port)); ``` diff --git a/packages/network/src/network.js b/packages/network/src/network.js index 66404d7e508..2a09b56b33e 100644 --- a/packages/network/src/network.js +++ b/packages/network/src/network.js @@ -1450,17 +1450,17 @@ export const preparePortAllocator = (zone, { watch }) => zone.exoClass( 'PortAllocator', M.interface('PortAllocator', { - allocateIBCPort: M.callWhen() + allocateCustomIBCPort: M.callWhen() .optional(M.string()) .returns(Shape.Vow$(Shape.Port)), allocateICAControllerPort: M.callWhen().returns(Shape.Vow$(Shape.Port)), - allocateLocalPort: M.callWhen() + allocateCustomLocalPort: M.callWhen() .optional(M.string()) .returns(Shape.Vow$(Shape.Port)), }), ({ protocol }) => ({ protocol, lastICAPortNum: 0n }), { - allocateIBCPort(specifiedName = '') { + allocateCustomIBCPort(specifiedName = '') { const { state } = this; let localAddr = `/ibc-port/`; @@ -1482,7 +1482,7 @@ export const preparePortAllocator = (zone, { watch }) => ), ); }, - allocateLocalPort(specifiedName = '') { + allocateCustomLocalPort(specifiedName = '') { const { state } = this; let localAddr = `/local/`; diff --git a/packages/network/test/test-network-misc.js b/packages/network/test/test-network-misc.js index 414fbafcc04..9c23e443b4d 100644 --- a/packages/network/test/test-network-misc.js +++ b/packages/network/test/test-network-misc.js @@ -186,10 +186,12 @@ test('verify port allocation', async t => { const makePortAllocator = preparePortAllocator(zone, powers); const portAllocator = makePortAllocator({ protocol }); - const ibcPort = await when(portAllocator.allocateIBCPort()); + const ibcPort = await when(portAllocator.allocateCustomIBCPort()); t.is(ibcPort.getLocalAddress(), '/ibc-port/port-1'); - const namedIbcPort = await when(portAllocator.allocateIBCPort('test-1')); + const namedIbcPort = await when( + portAllocator.allocateCustomIBCPort('test-1'), + ); t.is(namedIbcPort.getLocalAddress(), '/ibc-port/custom-test-1'); const icaControllerPort1 = await when( @@ -202,13 +204,15 @@ test('verify port allocation', async t => { ); t.is(icaControllerPort2.getLocalAddress(), '/ibc-port/icacontroller-2'); - const localPort = await when(portAllocator.allocateLocalPort()); + const localPort = await when(portAllocator.allocateCustomLocalPort()); t.is(localPort.getLocalAddress(), '/local/port-5'); - const namedLocalPort = await when(portAllocator.allocateLocalPort('local-1')); + const namedLocalPort = await when( + portAllocator.allocateCustomLocalPort('local-1'), + ); t.is(namedLocalPort.getLocalAddress(), '/local/custom-local-1'); - await t.throwsAsync(when(portAllocator.allocateIBCPort('/test-1')), { + await t.throwsAsync(when(portAllocator.allocateCustomIBCPort('/test-1')), { message: 'Invalid IBC port name: /test-1', }); }); diff --git a/packages/pegasus/src/proposals/core-proposal.js b/packages/pegasus/src/proposals/core-proposal.js index 9e6007574b6..dde255ccad1 100644 --- a/packages/pegasus/src/proposals/core-proposal.js +++ b/packages/pegasus/src/proposals/core-proposal.js @@ -104,7 +104,7 @@ export const listenPegasus = async ({ pegasusConnectionsAdmin.resolve(nameAdmin); const pegasus = await E(zoe).getPublicFacet(pegasusInstance); - const port = await E(portAllocator).allocateIBCPort('pegasus'); + const port = await E(portAllocator).allocateCustomIBCPort('pegasus'); return addPegasusTransferPort(port, pegasus, pegasusNameAdmin); }; harden(listenPegasus); diff --git a/packages/vats/src/proposals/network-proposal.js b/packages/vats/src/proposals/network-proposal.js index 50f11dd3e90..7b88b855ae4 100644 --- a/packages/vats/src/proposals/network-proposal.js +++ b/packages/vats/src/proposals/network-proposal.js @@ -60,16 +60,16 @@ export const registerNetworkProtocols = async (vats, dibcBridgeManager) => { * bootstrap space. * * The `portAllocator` is CLOSELY HELD in the core space, where later, we claim - * ports using `E(portAllocator).allocateIBCPort`, for example. + * ports using `E(portAllocator).allocateCustomIBCPort`, for example. * * Contracts are expected to use the services of the network and IBC vats by way * of such ports. * * Testing facilities include: * - * - loopback ports: `E(portAllocator).allocateLocalPort()` - * - an echo port: `E(portAllocator).allocateIBCPort("echo")d - * /ibc-port/custom-echo + * - loopback ports: `E(portAllocator).allocateCustomLocalPort()` + * - an echo port: `E(portAllocator).allocateCustomIBCPort("echo")` + * - echo port addrees: /ibc-port/custom-echo * * @param {BootstrapPowers & { * consume: { loadCriticalVat: VatLoader }; @@ -139,7 +139,7 @@ export const setupNetworkProtocols = async ( const portP = when(E(allocator).allocateICAControllerPort()); ibcportP.push(portP); } else { - const portP = when(E(allocator).allocateIBCPort()); + const portP = when(E(allocator).allocateCustomIBCPort()); ibcportP.push(portP); } } @@ -152,7 +152,7 @@ export const setupNetworkProtocols = async ( await registerNetworkProtocols(vats, dibcBridgeManager); // Add an echo listener on our ibc-port network (whether real or virtual). - const echoPort = await when(E(allocator).allocateIBCPort('echo')); + const echoPort = await when(E(allocator).allocateCustomIBCPort('echo')); const { listener } = await E(vats.network).makeEchoConnectionKit(); await when(E(echoPort).addListener(listener)); return E(client).assignBundle([_a => ({ ibcport: makePorts() })]); diff --git a/packages/vats/test/test-network.js b/packages/vats/test/test-network.js index ca07074d983..0dde76387f8 100644 --- a/packages/vats/test/test-network.js +++ b/packages/vats/test/test-network.js @@ -130,7 +130,7 @@ test('network - ibc', async t => { // Actually test the ibc port binding. // TODO: Do more tests on the returned Port object. t.log('Opening a Listening Port'); - const p = await when(E(portAllocator).allocateIBCPort()); + const p = await when(E(portAllocator).allocateCustomIBCPort()); const ev1 = await events.next(); t.assert(!ev1.done); t.deepEqual(ev1.value, ['bindPort', { packet: { source_port: 'port-1' } }]);