Skip to content

Commit

Permalink
orchestrate() returns vow (#9701)
Browse files Browse the repository at this point in the history
follow up on #9449 

## Description

- removes the temporary accommodation in which `orchestrate` returns a Promise instead of a Vow.
- removes `holder` from ResolvedContinuingInvitation b/c the `invitationMakers` provide the ocaps
- fixes `orchestrate` typedef to infer the return type from its arguments


### Security Considerations
none

### Scaling Considerations
none

### Documentation Considerations
none

### Testing Considerations
CI


### Upgrade Considerations
not yet deployed
  • Loading branch information
mergify[bot] authored Jul 16, 2024
2 parents 76f3e6f + 59229fa commit ab4941a
Show file tree
Hide file tree
Showing 27 changed files with 109 additions and 68 deletions.
2 changes: 1 addition & 1 deletion packages/agoric-cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,6 @@
"workerThreads": false
},
"typeCoverage": {
"atLeast": 77.3
"atLeast": 77.36
}
}
2 changes: 1 addition & 1 deletion packages/async-flow/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,6 @@
"workerThreads": false
},
"typeCoverage": {
"atLeast": 76.95
"atLeast": 76.94
}
}
22 changes: 13 additions & 9 deletions packages/async-flow/src/async-flow.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { LogEntryShape, FlowStateShape } from './type-guards.js';
/**
* @import {WeakMapStore} from '@agoric/store'
* @import {Zone} from '@agoric/base-zone'
* @import {FlowState, GuestAsyncFunc, HostAsyncFuncWrapper, PreparationOptions} from '../src/types.js'
* @import {FlowState, GuestAsyncFunc, HostAsyncFuncWrapper, HostOf, PreparationOptions} from '../src/types.js'
* @import {ReplayMembrane} from '../src/replay-membrane.js'
*/

Expand Down Expand Up @@ -434,21 +434,25 @@ export const prepareAsyncFlowTools = (outerZone, outerOptions = {}) => {
};

/**
* @template {GuestAsyncFunc} F
* @param {Zone} zone
* @param {string} tag
* @param {GuestAsyncFunc} guestFunc
* @param {F} guestFunc
* @param {{ startEager?: boolean }} [options]
* @returns {HostAsyncFuncWrapper}
* @returns {HostOf<F>}
*/
const asyncFlow = (zone, tag, guestFunc, options = undefined) => {
const makeAsyncFlowKit = prepareAsyncFlowKit(zone, tag, guestFunc, options);
const hostFuncName = `${tag}_hostFlow`;
const wrapperFunc = {
[hostFuncName](...args) {
const { flow } = makeAsyncFlowKit(args);
return flow.getOutcome();
},
}[hostFuncName];

const wrapperFunc = /** @type {HostOf<F>} */ (
{
[hostFuncName](...args) {
const { flow } = makeAsyncFlowKit(args);
return flow.getOutcome();
},
}[hostFuncName]
);
defineProperties(wrapperFunc, {
length: { value: guestFunc.length },
});
Expand Down
9 changes: 9 additions & 0 deletions packages/async-flow/src/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,13 @@ type HostInterface<T> = {
[K in keyof T]: HostOf<T[K]>;
};

/**
* Convert an entire Host interface into what the Guest will receive.
*/
type GuestInterface<T> = {
[K in keyof T]: GuestOf<T[K]>;
};

/**
* The function the host must provide to match an interface the guest expects.
*
Expand All @@ -60,6 +67,8 @@ export type HostOf<F> = F extends (...args: infer A) => Promise<infer R>
? (...args: A) => Vow<R extends Passable ? R : HostInterface<R>>
: F;

export type HostArgs<GA extends any[]> = { [K in keyof GA]: HostOf<GA[K]> };

export type PreparationOptions = {
vowTools?: VowTools;
makeLogStore?: (() => LogStore) | undefined;
Expand Down
2 changes: 1 addition & 1 deletion packages/boot/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,6 @@
"workerThreads": false
},
"typeCoverage": {
"atLeast": 86.66
"atLeast": 86.64
}
}
2 changes: 1 addition & 1 deletion packages/internal/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,6 @@
"access": "public"
},
"typeCoverage": {
"atLeast": 93.78
"atLeast": 93.82
}
}
2 changes: 1 addition & 1 deletion packages/orchestration/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,6 @@
"access": "public"
},
"typeCoverage": {
"atLeast": 97.99
"atLeast": 98.04
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { provideOrchestration } from '../utils/start-helper.js';
/**
* @import {Baggage} from '@agoric/vat-data';
* @import {Orchestrator} from '@agoric/orchestration';
* @import {Vow, VowTools} from '@agoric/vow';
* @import {OrchestrationPowers} from '../utils/start-helper.js';
*/

Expand Down Expand Up @@ -44,7 +45,6 @@ export const start = async (zcf, privateArgs, baggage) => {
privateArgs.marshaller,
);

/** @type {OfferHandler} */
const makeOrchAccount = orchestrate(
'makeOrchAccount',
undefined,
Expand Down
7 changes: 0 additions & 7 deletions packages/orchestration/src/examples/swapExample.contract.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import { orcUtils } from '../utils/orc.js';
import { withOrchestration } from '../utils/start-helper.js';

/**
* @import {GuestInterface, GuestOf} from '@agoric/async-flow';
* @import {LocalTransfer} from '../utils/zoe-tools.js';
* @import {Orchestrator, CosmosValidatorAddress} from '../types.js'
* @import {TimerService} from '@agoric/time';
Expand Down Expand Up @@ -102,12 +101,6 @@ const contract = async (zcf, privateArgs, zone, { orchestrate, zoeTools }) => {
const { brands } = zcf.getTerms();

/** deprecated historical example */
/**
* @type {OfferHandler<
* unknown,
* { staked: Amount<'nat'>; validator: CosmosValidatorAddress }
* >}
*/
const swapAndStakeHandler = orchestrate(
'LSTTia',
{ zcf, localTransfer: zoeTools.localTransfer },
Expand Down
2 changes: 0 additions & 2 deletions packages/orchestration/src/examples/unbondExample.contract.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { withOrchestration } from '../utils/start-helper.js';
/**
* @import {Orchestrator, IcaAccount, CosmosValidatorAddress} from '../types.js'
* @import {TimerService} from '@agoric/time';
* @import {Baggage} from '@agoric/vat-data';
* @import {LocalChain} from '@agoric/vats/src/localchain.js';
* @import {NameHub} from '@agoric/vats';
* @import {Remote} from '@agoric/internal';
Expand Down Expand Up @@ -59,7 +58,6 @@ const unbondAndLiquidStakeFn = async (orch, { zcf }, _seat, _offerArgs) => {
* @param {OrchestrationTools} tools
*/
const contract = async (zcf, privateArgs, zone, { orchestrate }) => {
/** @type {OfferHandler} */
const unbondAndLiquidStake = orchestrate(
'LSTTia',
{ zcf },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,6 @@ export const prepareCosmosOrchestrationAccountKit = (
// expect this complete in the same run
publicSubscribers: await when(holder.getPublicTopics()),
invitationMakers,
holder,
});
});
},
Expand Down
1 change: 1 addition & 0 deletions packages/orchestration/src/exos/local-chain-facade.js
Original file line number Diff line number Diff line change
Expand Up @@ -134,3 +134,4 @@ export const prepareLocalChainFacade = (zone, powers) => {
harden(prepareLocalChainFacade);

/** @typedef {ReturnType<typeof prepareLocalChainFacade>} MakeLocalChainFacade */
/** @typedef {ReturnType<MakeLocalChainFacade>} LocalChainFacade */
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,6 @@ export const prepareLocalOrchestrationAccountKit = (
// expect this complete in the same run
publicSubscribers: await when(holder.getPublicTopics()),
invitationMakers,
holder,
});
});
},
Expand Down
1 change: 1 addition & 0 deletions packages/orchestration/src/exos/remote-chain-facade.js
Original file line number Diff line number Diff line change
Expand Up @@ -176,3 +176,4 @@ export const prepareRemoteChainFacade = (zone, powers) => {
harden(prepareRemoteChainFacade);

/** @typedef {ReturnType<typeof prepareRemoteChainFacade>} MakeRemoteChainFacade */
/** @typedef {ReturnType<MakeRemoteChainFacade>} RemoteChainFacade */
35 changes: 14 additions & 21 deletions packages/orchestration/src/facade.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import { assertAllDefined } from '@agoric/internal';

/**
* @import {AsyncFlowTools} from '@agoric/async-flow';
* @import {AsyncFlowTools, GuestInterface, HostArgs} from '@agoric/async-flow';
* @import {Zone} from '@agoric/zone';
* @import {Vow, VowTools} from '@agoric/vow';
* @import {TimerService} from '@agoric/time';
Expand Down Expand Up @@ -52,27 +52,19 @@ export const makeOrchestrationFacade = ({

const { prepareEndowment, asyncFlow, adminAsyncFlow } = asyncFlowTools;

const { when } = vowTools;

/**
* @template GuestReturn
* @template HostReturn
* @template GuestContext
* @template HostContext
* @template {any[]} GuestArgs
* @template {any[]} HostArgs
* @template GR - return type
* @template HC - host context
* @template {any[]} GA - guest args
* @param {string} durableName - the orchestration flow identity in the zone
* (to resume across upgrades)
* @param {HostContext} hostCtx - values to pass through the async flow
* membrane
* @param {HC} hostCtx - values to pass through the async flow membrane
* @param {(
* guestOrc: Orchestrator,
* guestCtx: GuestContext,
* ...args: GuestArgs
* ) => Promise<GuestReturn>} guestFn
* @returns {(...args: HostArgs) => Promise<HostReturn>} TODO returns a
* Promise for now for compat before use of asyncFlow. But really should be
* `Vow<HostReturn>`
* guestCtx: GuestInterface<HC>,
* ...args: GA
* ) => Promise<GR>} guestFn
* @returns {(...args: HostArgs<GA>) => Vow<GR>}
*/
const orchestrate = (durableName, hostCtx, guestFn) => {
const subZone = zone.subZone(durableName);
Expand All @@ -86,10 +78,11 @@ export const makeOrchestrationFacade = ({

const hostFn = asyncFlow(subZone, 'asyncFlow', guestFn);

const orcFn = (...args) =>
// TODO remove the `when` after fixing the return type
// to `Vow<HostReturn>`
when(hostFn(wrappedOrc, wrappedCtx, ...args));
// cast because return could be arbitrary subtype
const orcFn = /** @type {(...args: HostArgs<GA>) => Vow<GR>} */ (
(...args) => hostFn(wrappedOrc, wrappedCtx, ...args)
);

return harden(orcFn);
};

Expand Down
8 changes: 7 additions & 1 deletion packages/orchestration/src/typeGuards.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import { M } from '@endo/patterns';

/**
* @import {TypedPattern} from '@agoric/internal';
* @import {ChainInfo, CosmosChainInfo} from './types.js';
* @import {ChainAddress, ChainInfo, CosmosChainInfo, DenomAmount} from './types.js';
* @import {Delegation} from '@agoric/cosmic-proto/cosmos/staking/v1beta1/staking.js';
*/

/**
Expand All @@ -22,6 +23,7 @@ export const OutboundConnectionHandlerI = M.interface(
},
);

/** @type {TypedPattern<ChainAddress>} */
export const ChainAddressShape = {
chainId: M.string(),
encoding: M.string(),
Expand All @@ -33,12 +35,15 @@ export const Proto3Shape = {
value: M.string(),
};

// XXX same as ChainAmountShape and DenomAmount type
export const CoinShape = { value: M.bigint(), denom: M.string() };

export const ChainAmountShape = harden({ denom: M.string(), value: M.nat() });

export const AmountArgShape = M.or(AmountShape, ChainAmountShape);

// FIXME missing `delegatorAddress` from the type
/** @type {TypedPattern<Delegation>} */
export const DelegationShape = harden({
validatorAddress: M.string(),
shares: M.string(), // TODO: bigint?
Expand Down Expand Up @@ -103,6 +108,7 @@ export const DenomShape = M.string();
// TODO define for #9211
export const BrandInfoShape = M.any();

/** @type {TypedPattern<DenomAmount>} */
export const DenomAmountShape = { denom: DenomShape, value: M.bigint() };

/** @see {Chain} */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,19 +61,22 @@ const orchestrationAccountScenario = test.macro({
return t.fail(`Unknown chain: ${chainName}`);
}

const { zoe, instance } = t.context;
const {
bootstrap: { vowTools: vt },
zoe,
instance,
} = t.context;
const publicFacet = await E(zoe).getPublicFacet(instance);
const inv = E(publicFacet).makeOrchAccountInvitation();
const userSeat = E(zoe).offer(inv, {}, undefined, { chainName });
// @ts-expect-error TODO: type expected offer result
const { holder, invitationMakers, publicSubscribers } =
await E(userSeat).getOfferResult();
const { invitationMakers, publicSubscribers } = await vt.when(
E(userSeat).getOfferResult(),
);

t.regex(getInterfaceOf(holder)!, /Orchestration (.*) holder/);
t.regex(getInterfaceOf(invitationMakers)!, /invitationMakers/);

const { description, storagePath, subscriber } = publicSubscribers.account;
t.regex(description, /Account holder/);
t.regex(description!, /Account holder/);

const expectedStoragePath = `mockChainStorageRoot.basic-flows.${config.addressPrefix}`;
t.is(storagePath, expectedStoragePath);
Expand Down
7 changes: 4 additions & 3 deletions packages/orchestration/test/examples/sendAnywhere.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ test('send using arbitrary chain info', async t => {
brands: { ist },
utils: { inspectLocalBridge, pourPayment },
} = await commonSetup(t);
const vt = bootstrap.vowTools;

const { zoe, bundleAndInstall } = await setUpZoeForTest();

Expand Down Expand Up @@ -121,7 +122,7 @@ test('send using arbitrary chain info', async t => {
{ Send },
{ destAddr: 'hot1destAddr', chainName },
);
await E(userSeat).getOfferResult();
await vt.when(E(userSeat).getOfferResult());

const history = inspectLocalBridge();
t.like(history, [
Expand Down Expand Up @@ -154,7 +155,7 @@ test('send using arbitrary chain info', async t => {
{ Send },
{ destAddr: 'cosmos1destAddr', chainName: 'cosmoshub' },
);
await E(userSeat).getOfferResult();
await vt.when(E(userSeat).getOfferResult());
const history = inspectLocalBridge();
const { messages, address: execAddr } = history.at(-1);
t.is(messages.length, 1);
Expand Down Expand Up @@ -200,7 +201,7 @@ test('send using arbitrary chain info', async t => {
{ Send },
{ destAddr: 'hot1destAddr', chainName: 'hot' },
);
await E(userSeat).getOfferResult();
await vt.when(E(userSeat).getOfferResult());
const history = inspectLocalBridge();
const { messages, address: execAddr } = history.at(-1);
t.is(messages.length, 1);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,6 @@ test('makeAccountInvitationMaker', async t => {

const userSeat = await E(zoe).offer(inv);
const offerResult = await E(userSeat).getOfferResult();
t.true('holder' in offerResult, 'received account holder');
t.truthy('invitationMakers' in offerResult, 'received continuing invitation');
t.like(offerResult.publicSubscribers, {
account: {
Expand Down
3 changes: 2 additions & 1 deletion packages/orchestration/test/examples/swapExample.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,8 @@ test.skip('start', async t => {
} as const,
},
);
const result = await E(userSeat).getOfferResult();
const vt = bootstrap.vowTools;
const result = await vt.when(E(userSeat).getOfferResult());
t.is(result, undefined);

// bank purse now has the 10 IST
Expand Down
3 changes: 2 additions & 1 deletion packages/orchestration/test/examples/unbondExample.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ type StartFn =

test('start', async t => {
const {
bootstrap: { vowTools: vt },
brands: { ist },
commonPrivateArgs,
} = await commonSetup(t);
Expand Down Expand Up @@ -47,7 +48,7 @@ test('start', async t => {
{},
{ validator: 'agoric1valopsfufu' },
);
const result = await E(userSeat).getOfferResult();
const result = await vt.when(E(userSeat).getOfferResult());
t.is(result, undefined);

const tree = inspectMapStore(contractBaggage);
Expand Down
Loading

0 comments on commit ab4941a

Please sign in to comment.