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

orchestrate() returns vow #9701

Merged
merged 6 commits into from
Jul 16, 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
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]> };
mhofman marked this conversation as resolved.
Show resolved Hide resolved

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 }
* >}
*/
0xpatrickdev marked this conversation as resolved.
Show resolved Hide resolved
const swapAndStakeHandler = orchestrate(
'LSTTia',
{ zcf, localTransfer: zoeTools.localTransfer },
Expand Down
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,
0xpatrickdev marked this conversation as resolved.
Show resolved Hide resolved
});
});
},
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());
Copy link
Member

Choose a reason for hiding this comment

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

I forget where we landed on aliasing heapVowE to E in tests. These are tests that we expect developers to write and vt.when(E(x).foo()) might be confusing.

Copy link
Member Author

Choose a reason for hiding this comment

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

My gut says papering over this difference will be riskier. Let's see how it plays out


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
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
Loading