Skip to content

Commit

Permalink
feat(orchestration): return vows
Browse files Browse the repository at this point in the history
- builds off of previous commits, which preserved functionality of returning promises, to return vows
0xpatrickdev committed Jun 19, 2024
1 parent f3f87b1 commit 24b0f0f
Showing 11 changed files with 140 additions and 117 deletions.
4 changes: 3 additions & 1 deletion packages/boot/test/bootstrapTests/orchestration.test.ts
Original file line number Diff line number Diff line change
@@ -72,7 +72,7 @@ test.serial('config', async t => {
}
});

test.serial('stakeOsmo - queries', async t => {
test.serial.failing('stakeOsmo - queries', async t => {
const {
buildProposal,
evalProposal,
@@ -98,9 +98,11 @@ test.serial('stakeOsmo - queries', async t => {
t.truthy(account, 'makeAccount returns an account on OSMO connection');

const queryRes = await EV(account).getBalance('uatom');
// FIXME, unwrap `queryRes` Vow
t.deepEqual(queryRes, { value: 0n, denom: 'uatom' });

const queryUnknownDenom = await EV(account).getBalance('some-invalid-denom');
// FIXME, unwrap `queryUnknownDenom` Vow
t.deepEqual(
queryUnknownDenom,
{ value: 0n, denom: 'some-invalid-denom' },
29 changes: 16 additions & 13 deletions packages/orchestration/src/exos/chain-account-kit.js
Original file line number Diff line number Diff line change
@@ -2,6 +2,7 @@
import { NonNullish } from '@agoric/assert';
import { PurseShape } from '@agoric/ertp';
import { makeTracer } from '@agoric/internal';
import { Shape as NetworkShape } from '@agoric/network';
import { E } from '@endo/far';
import { M } from '@endo/patterns';
import {
@@ -15,7 +16,7 @@ import { makeTxPacket, parseTxPacket } from '../utils/packet.js';
/**
* @import {Zone} from '@agoric/base-zone';
* @import {Connection, Port} from '@agoric/network';
* @import {Remote, VowTools} from '@agoric/vow';
* @import {PromiseVow, Remote, VowTools} from '@agoric/vow';
* @import {AnyJson} from '@agoric/cosmic-proto';
* @import {TxBody} from '@agoric/cosmic-proto/cosmos/tx/v1beta1/tx.js';
* @import {LocalIbcAddress, RemoteIbcAddress} from '@agoric/vats/tools/ibc-utils.js';
@@ -36,10 +37,10 @@ export const ChainAccountI = M.interface('ChainAccount', {
getRemoteAddress: M.call().returns(M.string()),
getPort: M.call().returns(M.remotable('Port')),
executeTx: M.call(M.arrayOf(M.record())).returns(M.promise()),
executeEncodedTx: M.call(M.arrayOf(Proto3Shape))
executeEncodedTx: M.callWhen(M.arrayOf(Proto3Shape))
.optional(M.record())
.returns(M.promise()),
close: M.callWhen().returns(M.undefined()),
.returns(NetworkShape.Vow$(M.string())),
close: M.callWhen().returns(NetworkShape.Vow$(M.undefined())),
getPurse: M.callWhen().returns(PurseShape),
});

@@ -59,7 +60,7 @@ export const ChainAccountI = M.interface('ChainAccount', {
* @param {Zone} zone
* @param {VowTools} vowTools
*/
export const prepareChainAccountKit = (zone, { watch, when }) =>
export const prepareChainAccountKit = (zone, { watch }) =>
zone.exoClassKit(
'ChainAccountKit',
{
@@ -133,7 +134,7 @@ export const prepareChainAccountKit = (zone, { watch, when }) =>
*
* @param {AnyJson[]} msgs
* @param {Omit<TxBody, 'messages'>} [opts]
* @returns {Promise<string>} - base64 encoded bytes string. Can be
* @returns {PromiseVow<string>} - base64 encoded bytes string. Can be
* decoded using the corresponding `Msg*Response` object.
* @throws {Error} if packet fails to send or an error is returned
*/
@@ -142,14 +143,16 @@ export const prepareChainAccountKit = (zone, { watch, when }) =>
// 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(
E(connection).send(makeTxPacket(msgs, opts)),
this.facets.parseTxPacketWatcher,
),
return watch(
E(connection).send(makeTxPacket(msgs, opts)),
this.facets.parseTxPacketWatcher,
);
},
/** Close the remote account */
/**
* Close the remote account
*
* @returns {PromiseVow<void>}
*/
async close() {
/// TODO #9192 what should the behavior be here? and `onClose`?
// - retrieve assets?
@@ -158,7 +161,7 @@ export const prepareChainAccountKit = (zone, { watch, when }) =>
// 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(E(connection).close()));
return watch(E(connection).close());
},
/**
* get Purse for a brand to .withdraw() a Payment from the account
54 changes: 31 additions & 23 deletions packages/orchestration/src/exos/cosmos-orchestration-account.js
Original file line number Diff line number Diff line change
@@ -16,6 +16,7 @@ import {
} from '@agoric/cosmic-proto/cosmos/staking/v1beta1/tx.js';
import { Any } from '@agoric/cosmic-proto/google/protobuf/any.js';
import { makeTracer } from '@agoric/internal';
import { Shape as NetworkShape } from '@agoric/network';
import { M } from '@agoric/vat-data';
import { TopicsRecordShape } from '@agoric/zoe/src/contractSupport/index.js';
import { InvitationShape } from '@agoric/zoe/src/typeGuards.js';
@@ -39,7 +40,7 @@ import { dateInSeconds } from '../utils/time.js';
* @import {Delegation} from '@agoric/cosmic-proto/cosmos/staking/v1beta1/staking.js';
* @import {Remote} from '@agoric/internal';
* @import {TimerService} from '@agoric/time';
* @import {VowTools} from '@agoric/vow';
* @import {PromiseVow, VowTools} from '@agoric/vow';
* @import {Zone} from '@agoric/zone';
* @import {ResponseQuery} from '@agoric/cosmic-proto/tendermint/abci/types.js';
* @import {JsonSafe} from '@agoric/cosmic-proto';
@@ -74,18 +75,22 @@ export const IcaAccountHolderI = M.interface('IcaAccountHolder', {
}),
getPublicTopics: M.call().returns(TopicsRecordShape),
delegate: M.callWhen(ChainAddressShape, AmountArgShape).returns(
M.undefined(),
NetworkShape.Vow$(M.undefined()),
),
redelegate: M.callWhen(
ChainAddressShape,
ChainAddressShape,
AmountArgShape,
).returns(M.undefined()),
).returns(NetworkShape.Vow$(M.undefined())),
withdrawReward: M.callWhen(ChainAddressShape).returns(
M.arrayOf(ChainAmountShape),
NetworkShape.Vow$(M.arrayOf(ChainAmountShape)),
),
withdrawRewards: M.callWhen().returns(
NetworkShape.Vow$(M.arrayOf(ChainAmountShape)),
),
undelegate: M.callWhen(M.arrayOf(DelegationShape)).returns(
NetworkShape.Vow$(M.undefined()),
),
withdrawRewards: M.callWhen().returns(M.arrayOf(ChainAmountShape)),
undelegate: M.callWhen(M.arrayOf(DelegationShape)).returns(M.undefined()),
});

/** @type {{ [name: string]: [description: string, valueShape: Pattern] }} */
@@ -105,7 +110,7 @@ const toDenomAmount = c => ({ denom: c.denom, value: BigInt(c.amount) });
export const prepareCosmosOrchestrationAccountKit = (
zone,
makeRecorderKit,
{ when, watch },
{ watch },
zcf,
) => {
const makeCosmosOrchestrationAccountKit = zone.exoClassKit(
@@ -349,13 +354,14 @@ export const prepareCosmosOrchestrationAccountKit = (
*
* @param {CosmosValidatorAddress} validator
* @param {AmountArg} amount
* @returns {PromiseVow<void>}
*/
async delegate(validator, amount) {
trace('delegate', validator, amount);
const { helper } = this.facets;
const { chainAddress } = this.state;

const results = E(helper.owned()).executeEncodedTx([
const resultsV = E(helper.owned()).executeEncodedTx([
Any.toJSON(
MsgDelegate.toProtoMsg({
delegatorAddress: chainAddress.address,
@@ -364,7 +370,7 @@ export const prepareCosmosOrchestrationAccountKit = (
}),
),
]);
return when(watch(results, this.facets.returnVoidWatcher));
return watch(resultsV, this.facets.returnVoidWatcher);
},
async deposit(payment) {
trace('deposit', payment);
@@ -381,13 +387,14 @@ export const prepareCosmosOrchestrationAccountKit = (
* @param {CosmosValidatorAddress} srcValidator
* @param {CosmosValidatorAddress} dstValidator
* @param {AmountArg} amount
* @returns {PromiseVow<void>}
*/
async redelegate(srcValidator, dstValidator, amount) {
trace('redelegate', srcValidator, dstValidator, amount);
const { helper } = this.facets;
const { chainAddress } = this.state;

const results = E(helper.owned()).executeEncodedTx([
const resultsV = E(helper.owned()).executeEncodedTx([
Any.toJSON(
MsgBeginRedelegate.toProtoMsg({
delegatorAddress: chainAddress.address,
@@ -398,17 +405,15 @@ export const prepareCosmosOrchestrationAccountKit = (
),
]);

return when(
watch(
results,
// NOTE: response, including completionTime, is currently discarded.
this.facets.returnVoidWatcher,
),
return watch(
resultsV,
// NOTE: response, including completionTime, is currently discarded.
this.facets.returnVoidWatcher,
);
},
/**
* @param {CosmosValidatorAddress} validator
* @returns {Promise<DenomAmount[]>}
* @returns {PromiseVow<DenomAmount[]>}
*/
async withdrawReward(validator) {
trace('withdrawReward', validator);
@@ -421,11 +426,11 @@ export const prepareCosmosOrchestrationAccountKit = (
const account = helper.owned();

const results = E(account).executeEncodedTx([Any.toJSON(msg)]);
return when(watch(results, this.facets.withdrawRewardWatcher));
return watch(results, this.facets.withdrawRewardWatcher);
},
/**
* @param {DenomArg} denom
* @returns {Promise<DenomAmount>}
* @returns {PromiseVow<DenomAmount>}
*/
async getBalance(denom) {
const { chainAddress, icqConnection } = this.state;
@@ -435,15 +440,15 @@ export const prepareCosmosOrchestrationAccountKit = (
// TODO #9211 lookup denom from brand
assert.typeof(denom, 'string');

const results = E(icqConnection).query([
const resultsV = E(icqConnection).query([
toRequestQueryJson(
QueryBalanceRequest.toProtoMsg({
address: chainAddress.address,
denom,
}),
),
]);
return when(watch(results, this.facets.balanceQueryWatcher));
return watch(resultsV, this.facets.balanceQueryWatcher);
},

send(toAccount, amount) {
@@ -465,7 +470,10 @@ export const prepareCosmosOrchestrationAccountKit = (
throw assert.error('Not implemented');
},

/** @param {Omit<Delegation, 'delegatorAddress'>[]} delegations */
/**
* @param {Omit<Delegation, 'delegatorAddress'>[]} delegations
* @returns {PromiseVow<void>}
*/
async undelegate(delegations) {
trace('undelegate', delegations);
const { helper } = this.facets;
@@ -485,7 +493,7 @@ export const prepareCosmosOrchestrationAccountKit = (
),
this.facets.undelegateWatcher,
);
return when(watch(undelegateV, this.facets.returnVoidWatcher));
return watch(undelegateV, this.facets.returnVoidWatcher);
},
},
},
20 changes: 11 additions & 9 deletions packages/orchestration/src/exos/icq-connection-kit.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/** @file ICQConnection Exo */
import { NonNullish } from '@agoric/assert';
import { makeTracer } from '@agoric/internal';
import { Shape as NetworkShape } from '@agoric/network';
import { E } from '@endo/far';
import { M } from '@endo/patterns';
import { makeQueryPacket, parseQueryPacket } from '../utils/packet.js';
@@ -9,7 +10,7 @@ import { ConnectionHandlerI } from '../typeGuards.js';
/**
* @import {Zone} from '@agoric/base-zone';
* @import {Connection, Port} from '@agoric/network';
* @import {Remote, VowTools} from '@agoric/vow';
* @import {PromiseVow, 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';
@@ -26,7 +27,9 @@ export const ICQMsgShape = M.splitRecord(
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.callWhen(M.arrayOf(ICQMsgShape)).returns(
NetworkShape.Vow$(M.arrayOf(M.record())),
),
});

/**
@@ -54,7 +57,7 @@ export const ICQConnectionI = M.interface('ICQConnection', {
* @param {Zone} zone
* @param {VowTools} vowTools
*/
export const prepareICQConnectionKit = (zone, { watch, when }) =>
export const prepareICQConnectionKit = (zone, { watch }) =>
zone.exoClassKit(
'ICQConnectionKit',
{
@@ -90,19 +93,18 @@ export const prepareICQConnectionKit = (zone, { watch, when }) =>
},
/**
* @param {JsonSafe<RequestQuery>[]} msgs
* @returns {Promise<JsonSafe<ResponseQuery>[]>}
* @returns {PromiseVow<JsonSafe<ResponseQuery>[]>}
* @throws {Error} if packet fails to send or an error is returned
*/
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(
E(connection).send(makeQueryPacket(msgs)),
this.facets.parseQueryPacketWatcher,
),
// @ts-expect-error Type 'Vow<JsonSafe<ResponseQuery>[]>' is missing the following properties from type 'Promise<JsonSafe<ResponseQuery>[] | Vow<JsonSafe<ResponseQuery>[]>>': then, catch, finally ts(2739)
return watch(
E(connection).send(makeQueryPacket(msgs)),
this.facets.parseQueryPacketWatcher,
);
},
},
Loading

0 comments on commit 24b0f0f

Please sign in to comment.