Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: plumb icqConnection through orchestrate facade #9927

Merged
merged 6 commits into from
Aug 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions packages/boot/test/orchestration/restart-contracts.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@ test.serial('basicFlows', async t => {
},
proposal: {},
offerArgs: {
chainNames: ['agoric', 'cosmoshub'],
chainNames: ['agoric', 'cosmoshub', 'osmosis'],
},
});
// no errors and no result yet
Expand All @@ -242,7 +242,8 @@ test.serial('basicFlows', async t => {
result: undefined, // no property
},
});
t.is(getInboundQueueLength(), 2);
// 3x ICA Channel Opens, 1x ICQ Channel Open
t.is(getInboundQueueLength(), 4);

t.log('restart basicFlows');
await evalProposal(
Expand All @@ -264,7 +265,7 @@ test.serial('basicFlows', async t => {
result: 'UNPUBLISHED',
},
});
t.is(await flushInboundQueue(1), 1);
t.is(await flushInboundQueue(3), 3);
t.like(wallet.getLatestUpdateRecord(), {
status: {
id: id2,
Expand Down
10 changes: 9 additions & 1 deletion packages/orchestration/src/cosmos-api.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { AnyJson, TypedJson } from '@agoric/cosmic-proto';
import type { AnyJson, TypedJson, JsonSafe } from '@agoric/cosmic-proto';
import type {
Delegation,
Redelegation,
Expand All @@ -11,6 +11,10 @@ import type {
Order,
} from '@agoric/cosmic-proto/ibc/core/channel/v1/channel.js';
import type { State as IBCConnectionState } from '@agoric/cosmic-proto/ibc/core/connection/v1/connection.js';
import type {
RequestQuery,
ResponseQuery,
} from '@agoric/cosmic-proto/tendermint/abci/types.js';
import type { Brand, Purse, Payment, Amount } from '@agoric/ertp/src/types.js';
import type { Port } from '@agoric/network';
import type { IBCChannelID, IBCConnectionID } from '@agoric/vats';
Expand Down Expand Up @@ -265,3 +269,7 @@ export type CosmosChainAccountMethods<CCI extends CosmosChainInfo> =
}
? StakingAccountActions
: {};

export type ICQQueryFunction = (
msgs: JsonSafe<RequestQuery>[],
) => Promise<JsonSafe<ResponseQuery>[]>;
15 changes: 15 additions & 0 deletions packages/orchestration/src/examples/basic-flows.contract.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ const contract = async (
M.interface('Basic Flows PF', {
makeOrchAccountInvitation: M.callWhen().returns(InvitationShape),
makePortfolioAccountInvitation: M.callWhen().returns(InvitationShape),
makeSendICQQueryInvitation: M.callWhen().returns(InvitationShape),
makeAccountAndSendBalanceQueryInvitation:
M.callWhen().returns(InvitationShape),
}),
{
makeOrchAccountInvitation() {
Expand All @@ -54,6 +57,18 @@ const contract = async (
'Make an Orchestration Account',
);
},
makeSendICQQueryInvitation() {
return zcf.makeInvitation(
orchFns.sendQuery,
'Submit a query to a remote chain',
);
},
makeAccountAndSendBalanceQueryInvitation() {
return zcf.makeInvitation(
orchFns.makeAccountAndSendBalanceQuery,
'Make an account and submit a balance query',
);
},
},
);

Expand Down
58 changes: 56 additions & 2 deletions packages/orchestration/src/examples/basic-flows.flows.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@
* @file Primarily a testing fixture, but also serves as an example of how to
* leverage basic functionality of the Orchestration API with async-flow.
*/
import { Fail } from '@endo/errors';
import { M, mustMatch } from '@endo/patterns';

/**
* @import {Zone} from '@agoric/zone';
* @import {OrchestrationAccount, OrchestrationFlow, Orchestrator} from '@agoric/orchestration';
* @import {DenomArg, OrchestrationAccount, OrchestrationFlow, Orchestrator} from '@agoric/orchestration';
* @import {ResolvedPublicTopic} from '@agoric/zoe/src/contractSupport/topics.js';
* @import {JsonSafe} from '@agoric/cosmic-proto';
* @import {RequestQuery} from '@agoric/cosmic-proto/tendermint/abci/types.js';
* @import {OrchestrationPowers} from '../utils/start-helper.js';
* @import {MakePortfolioHolder} from '../exos/portfolio-holder-kit.js';
* @import {OrchestrationTools} from '../utils/start-helper.js';
Expand Down Expand Up @@ -79,3 +81,55 @@ export const makePortfolioAccount = async (
return portfolioHolder.asContinuingOffer();
};
harden(makePortfolioAccount);

/**
* Send a query and get the response back in an offer result. This invitation is
* for testing only. In a real scenario it's better to use an RPC or API client
* and vstorage to retrieve data for a frontend. Queries should only be
* leveraged if contract logic requires it.
*
* @satisfies {OrchestrationFlow}
* @param {Orchestrator} orch
* @param {any} _ctx
* @param {ZCFSeat} seat
* @param {{ chainName: string; msgs: JsonSafe<RequestQuery>[] }} offerArgs
*/
export const sendQuery = async (orch, _ctx, seat, { chainName, msgs }) => {
seat.exit(); // no funds exchanged
mustMatch(chainName, M.string());
if (chainName === 'agoric') throw Fail`ICQ not supported on local chain`;
turadg marked this conversation as resolved.
Show resolved Hide resolved
const remoteChain = await orch.getChain(chainName);
const queryResponse = await remoteChain.query(msgs);
console.debug('sendQuery response:', queryResponse);
return queryResponse;
};
harden(sendQuery);

/**
* Create an account and send a query and get the response back in an offer
* result. Like `sendQuery`, this invitation is for testing only. In a real
* scenario it doesn't make much sense to send a query immediately after the
* account is created - it won't have any funds.
*
* @satisfies {OrchestrationFlow}
* @param {Orchestrator} orch
* @param {any} _ctx
* @param {ZCFSeat} seat
* @param {{ chainName: string; denom: DenomArg }} offerArgs
*/
export const makeAccountAndSendBalanceQuery = async (
orch,
_ctx,
seat,
{ chainName, denom },
) => {
seat.exit(); // no funds exchanged
mustMatch(chainName, M.string());
if (chainName === 'agoric') throw Fail`ICQ not supported on local chain`;
const remoteChain = await orch.getChain(chainName);
const orchAccount = await remoteChain.makeAccount();
const queryResponse = await orchAccount.getBalance(denom);
console.debug('getBalance response:', queryResponse);
return queryResponse;
};
harden(makeAccountAndSendBalanceQuery);
33 changes: 14 additions & 19 deletions packages/orchestration/src/exos/icq-connection-kit.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,26 @@
import { Fail } from '@endo/errors';
import { E } from '@endo/far';
import { M } from '@endo/patterns';
import { VowShape } from '@agoric/vow';
import { NonNullish, makeTracer } from '@agoric/internal';
import { makeQueryPacket, parseQueryPacket } from '../utils/packet.js';
import { OutboundConnectionHandlerI } from '../typeGuards.js';
import { ICQMsgShape, OutboundConnectionHandlerI } from '../typeGuards.js';

/**
* @import {Zone} from '@agoric/base-zone';
* @import {Connection, Port} from '@agoric/network';
* @import {Remote, VowTools} from '@agoric/vow';
* @import {Remote, Vow, 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';
*/

const trace = makeTracer('Orchestration:ICQConnection');

export const ICQMsgShape = M.splitRecord(
{ path: M.string(), data: M.string() },
{ height: M.string(), prove: M.boolean() },
);

export const ICQConnectionI = M.interface('ICQConnection', {
getLocalAddress: M.call().returns(M.string()),
getRemoteAddress: M.call().returns(M.string()),
query: M.call(M.arrayOf(ICQMsgShape)).returns(M.promise()),
query: M.call(M.arrayOf(ICQMsgShape)).returns(VowShape),
});

/**
Expand Down Expand Up @@ -53,7 +49,7 @@ export const ICQConnectionI = M.interface('ICQConnection', {
* @param {Zone} zone
* @param {VowTools} vowTools
*/
export const prepareICQConnectionKit = (zone, { watch, when }) =>
export const prepareICQConnectionKit = (zone, { watch, asVow }) =>
zone.exoClassKit(
'ICQConnectionKit',
{
Expand Down Expand Up @@ -88,21 +84,20 @@ export const prepareICQConnectionKit = (zone, { watch, when }) =>
);
},
/**
* Vow rejects if packet fails to send or an error is returned
*
* @param {JsonSafe<RequestQuery>[]} msgs
* @returns {Promise<JsonSafe<ResponseQuery>[]>}
* @throws {Error} if packet fails to send or an error is returned
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

too badd there's no @rejects or whatever. Shouldn't this doc still inform the user of that?

* @returns {Vow<JsonSafe<ResponseQuery>[]>}
*/
query(msgs) {
const { connection } = this.state;
// TODO #9281 do not throw synchronously when returning a promise; return a rejected Vow
/// see https://github.com/Agoric/agoric-sdk/pull/9454#discussion_r1626898694
if (!connection) throw Fail`connection not available`;
return when(
watch(
return asVow(() => {
const { connection } = this.state;
if (!connection) throw Fail`connection not available`;
return watch(
E(connection).send(makeQueryPacket(msgs)),
this.facets.parseQueryPacketWatcher,
),
);
);
});
},
},
parseQueryPacketWatcher: {
Expand Down
5 changes: 5 additions & 0 deletions packages/orchestration/src/exos/local-chain-facade.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { M } from '@endo/patterns';
import { pickFacet } from '@agoric/vat-data';
import { VowShape } from '@agoric/vow';

import { Fail } from '@endo/errors';
import { chainFacadeMethods } from '../typeGuards.js';

/**
Expand Down Expand Up @@ -107,6 +108,10 @@ const prepareLocalChainFacadeKit = (
this.facets.makeAccountWatcher,
);
},
query() {
// TODO https://github.com/Agoric/agoric-sdk/pull/9935
return asVow(() => Fail`not yet implemented`);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return asVow(() => Fail`not yet implemented`);
// TODO https://github.com/Agoric/agoric-sdk/pull/9935
return asVow(() => Fail`not yet implemented`);

},
/** @type {HostOf<AgoricChainMethods['getVBankAssetInfo']>} */
getVBankAssetInfo() {
return asVow(() => {
Expand Down
Loading
Loading