diff --git a/packages/orchestration/src/exos/icqConnectionKit.js b/packages/orchestration/src/exos/icqConnectionKit.js index 6c29d491ce3..4b89efdacfe 100644 --- a/packages/orchestration/src/exos/icqConnectionKit.js +++ b/packages/orchestration/src/exos/icqConnectionKit.js @@ -1,7 +1,7 @@ /** @file ICQConnection Exo */ import { NonNullish } from '@agoric/assert'; import { makeTracer } from '@agoric/internal'; -import { V as E } from '@agoric/vow/vat.js'; +import { E } from '@endo/far'; import { M } from '@endo/patterns'; import { makeQueryPacket, parseQueryPacket } from '../utils/packet.js'; import { ConnectionHandlerI } from '../typeGuards.js'; @@ -9,7 +9,7 @@ import { ConnectionHandlerI } from '../typeGuards.js'; /** * @import {Zone} from '@agoric/base-zone'; * @import {Connection, Port} from '@agoric/network'; - * @import {Remote} from '@agoric/vow'; + * @import {Remote, VowTools} from '@agoric/vow'; * @import {JsonSafe} from '@agoric/cosmic-proto'; * @import {RequestQuery, ResponseQuery} from '@agoric/cosmic-proto/tendermint/abci/types.js'; * @import {LocalIbcAddress, RemoteIbcAddress} from '@agoric/vats/tools/ibc-utils.js'; @@ -29,6 +29,12 @@ export const ICQConnectionI = M.interface('ICQConnection', { query: M.call(M.arrayOf(ICQMsgShape)).returns(M.promise()), }); +const HandleQueryWatcherI = M.interface('HandleQueryWatcher', { + onFulfilled: M.call(M.string()) + .optional(M.arrayOf(M.undefined())) // does not need watcherContext + .returns(M.arrayOf(M.record())), +}); + /** * @typedef {{ * port: Port; @@ -52,11 +58,16 @@ export const ICQConnectionI = M.interface('ICQConnection', { * sending queries and handling connection events. * * @param {Zone} zone + * @param {VowTools} vowTools */ -export const prepareICQConnectionKit = zone => +export const prepareICQConnectionKit = (zone, { watch, when }) => zone.exoClassKit( 'ICQConnectionKit', - { connection: ICQConnectionI, connectionHandler: ConnectionHandlerI }, + { + connection: ICQConnectionI, + handleQueryWatcher: HandleQueryWatcherI, + connectionHandler: ConnectionHandlerI, + }, /** * @param {Port} port */ @@ -89,13 +100,18 @@ export const prepareICQConnectionKit = zone => query(msgs) { const { connection } = this.state; if (!connection) throw Fail`connection not available`; - return E.when( - E(connection).send(makeQueryPacket(msgs)), - // if parseTxPacket cannot find a `result` key, it throws - ack => parseQueryPacket(ack), + return when( + watch( + E(connection).send(makeQueryPacket(msgs)), + this.facets.handleQueryWatcher, + ), ); }, }, + handleQueryWatcher: { + /** @param {string} ack packet acknowledgement string */ + onFulfilled: ack => parseQueryPacket(ack), + }, connectionHandler: { /** * @param {Remote} connection diff --git a/packages/orchestration/src/service.js b/packages/orchestration/src/service.js index 6435e07713a..74c32958039 100644 --- a/packages/orchestration/src/service.js +++ b/packages/orchestration/src/service.js @@ -15,6 +15,7 @@ import { * @import {Remote} from '@agoric/internal'; * @import {Port, PortAllocator} from '@agoric/network'; * @import {IBCConnectionID} from '@agoric/vats'; + * @import {VowTools} from '@agoric/vow'; * @import {ICQConnection, IcaAccount, ICQConnectionKit} from './types.js'; */ @@ -104,7 +105,7 @@ const prepareOrchestrationKit = ( }, async allocateICQControllerPort() { const portAllocator = getPower(this.state.powers, 'portAllocator'); - return E(portAllocator).allocateICAControllerPort(); + return E(portAllocator).allocateICQControllerPort(); }, }, public: { @@ -163,10 +164,13 @@ const prepareOrchestrationKit = ( }, ); -/** @param {Zone} zone */ -export const prepareOrchestrationTools = zone => { +/** + * @param {Zone} zone + * @param {VowTools} vowTools + */ +export const prepareOrchestrationTools = (zone, vowTools) => { const makeChainAccountKit = prepareChainAccountKit(zone); - const makeICQConnectionKit = prepareICQConnectionKit(zone); + const makeICQConnectionKit = prepareICQConnectionKit(zone, vowTools); const makeOrchestrationKit = prepareOrchestrationKit( zone, makeChainAccountKit, diff --git a/packages/orchestration/src/vat-orchestration.js b/packages/orchestration/src/vat-orchestration.js index 675911ab7c5..8f3de0b1cc2 100644 --- a/packages/orchestration/src/vat-orchestration.js +++ b/packages/orchestration/src/vat-orchestration.js @@ -1,4 +1,5 @@ import { Far } from '@endo/far'; +import { prepareVowTools } from '@agoric/vow/vat.js'; import { makeDurableZone } from '@agoric/zone/durable.js'; import { prepareOrchestrationTools } from './service.js'; @@ -6,8 +7,10 @@ import { prepareOrchestrationTools } from './service.js'; export const buildRootObject = (_vatPowers, _args, baggage) => { const zone = makeDurableZone(baggage); + const vowTools = prepareVowTools(zone.subZone('VowTools')); const { makeOrchestrationKit } = prepareOrchestrationTools( zone.subZone('orchestration'), + vowTools, ); return Far('OrchestrationVat', { diff --git a/packages/orchestration/test/network-fakes.ts b/packages/orchestration/test/network-fakes.ts index 92a2f77ac68..079761b2ae1 100644 --- a/packages/orchestration/test/network-fakes.ts +++ b/packages/orchestration/test/network-fakes.ts @@ -92,19 +92,19 @@ export const prepareProtocolHandler = ( }; export const fakeNetworkEchoStuff = (zone: Zone) => { - const powers = prepareVowTools(zone); - const { makeVowKit, when } = powers; + const vowTools = prepareVowTools(zone); + const { makeVowKit, when } = vowTools; - const makeNetworkProtocol = prepareNetworkProtocol(zone, powers); + const makeNetworkProtocol = prepareNetworkProtocol(zone, vowTools); const makeEchoConnectionHandler = prepareEchoConnectionKit(zone); const makeProtocolHandler = prepareProtocolHandler( zone, makeEchoConnectionHandler, - powers, + vowTools, ); const protocol = makeNetworkProtocol(makeProtocolHandler()); - const makePortAllocator = preparePortAllocator(zone, powers); + const makePortAllocator = preparePortAllocator(zone, vowTools); const portAllocator = makePortAllocator({ protocol }); return { @@ -113,5 +113,6 @@ export const fakeNetworkEchoStuff = (zone: Zone) => { portAllocator, protocol, when, + vowTools, }; }; diff --git a/packages/orchestration/test/service.test.ts b/packages/orchestration/test/service.test.ts new file mode 100644 index 00000000000..a91f427a591 --- /dev/null +++ b/packages/orchestration/test/service.test.ts @@ -0,0 +1,50 @@ +import { test } from '@agoric/zoe/tools/prepare-test-env-ava.js'; +import { toRequestQueryJson } from '@agoric/cosmic-proto'; +import { QueryBalanceRequest } from '@agoric/cosmic-proto/cosmos/bank/v1beta1/query.js'; +import { E } from '@endo/far'; +import { commonSetup } from './supports.js'; + +test('makeICQConnection returns an ICQConnection', async t => { + const { + bootstrap: { orchestration }, + } = await commonSetup(t); + + const CONNECTION_ID = 'connection-0'; + + const icqConnection = + await E(orchestration).provideICQConnection(CONNECTION_ID); + const [localAddr, remoteAddr] = await Promise.all([ + E(icqConnection).getLocalAddress(), + E(icqConnection).getRemoteAddress(), + ]); + t.log(icqConnection, { + localAddr, + remoteAddr, + }); + t.regex(localAddr, /ibc-port\/icqcontroller-\d+/); + t.regex( + remoteAddr, + new RegExp(`/ibc-hop/${CONNECTION_ID}`), + 'remote address contains provided connectionId', + ); + t.regex( + remoteAddr, + /icqhost\/unordered\/icq-1/, + 'remote address contains icqhost port, unordered ordering, and icq-1 version string', + ); + + await t.throwsAsync( + E(icqConnection).query([ + toRequestQueryJson( + QueryBalanceRequest.toProtoMsg({ + address: 'cosmos1test', + denom: 'uatom', + }), + ), + ]), + { message: /"data":"(.*)"memo":""/ }, + 'TODO do not use echo connection', + ); +}); + +test.todo('makeAccount'); diff --git a/packages/orchestration/test/supports.ts b/packages/orchestration/test/supports.ts index d13338b260b..1fa4d067536 100644 --- a/packages/orchestration/test/supports.ts +++ b/packages/orchestration/test/supports.ts @@ -46,11 +46,14 @@ export const commonSetup = async t => { sequence: false, }); + const { portAllocator, vowTools } = fakeNetworkEchoStuff( + rootZone.subZone('network'), + ); + const { makeOrchestrationKit } = prepareOrchestrationTools( rootZone.subZone('orchestration'), + vowTools, ); - - const { portAllocator } = fakeNetworkEchoStuff(rootZone.subZone('network')); const { public: orchestration } = makeOrchestrationKit({ portAllocator }); return { @@ -62,6 +65,7 @@ export const commonSetup = async t => { orchestration, rootZone, storage, + vowTools, }, brands: { // TODO consider omitting `issuer` to prevent minting, which the bank can't observe diff --git a/packages/vow/src/tools.js b/packages/vow/src/tools.js index 1abfe599b37..c99b540698d 100644 --- a/packages/vow/src/tools.js +++ b/packages/vow/src/tools.js @@ -24,3 +24,5 @@ export const prepareVowTools = (zone, powers = {}) => { return harden({ when, watch, makeVowKit, allVows }); }; harden(prepareVowTools); + +/** @typedef { ReturnType} VowTools */