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

Dt/retriable spike3 #9657

Draft
wants to merge 7 commits into
base: master
Choose a base branch
from
Draft
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
3 changes: 2 additions & 1 deletion packages/async-flow/src/replay-membrane.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
watchWake,
panic,
}) => {
const { when, watch, makeVowKit } = vowTools;

Check failure on line 46 in packages/async-flow/src/replay-membrane.js

View workflow job for this annotation

GitHub Actions / lint-rest

'watch' is assigned a value but never used. Allowed unused vars must match /^_/u

const equate = makeEquate(bijection);

Expand Down Expand Up @@ -156,12 +156,13 @@
case 'promise': {
const e = Error('where warning happened');
console.log('Warning for now: vow expected, not promise', h, e);
throw e;
// TODO remove this stopgap. Here for now because host-side
// promises are everywhere!
// Note: A good place to set a breakpoint, or to uncomment the
// `debugger;` line, to work around bundling.
// debugger;
return watch(h);
// return watch(h);
}
case 'copyRecord': {
const o = /** @type {object} */ (h);
Expand Down
27 changes: 27 additions & 0 deletions packages/orchestration/src/Zone architecture.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
Zone architecture

Guide
- raw names are the name of the node
- <names> are developer-supplied names for some purposes


- top level
- vows - system zone for vowtools
- asyncflow - system zone for asyncflow
- orchestration - system zone for orch
- retriable - TODO - system zone for `retriable` infra

- contract - TODO - system zone under which developer-controlled data is stored
- orchestration - MISSING intermediary to have a function namespace. should they be a separate top-level in `contract`
- <durableOrchestrationName> - name of dev-supplied orchFn
- endowments -
- <endowmentname> - ??
- asyncflow
- <contract zones> - ???
- <name of retriablefn> -- TODO should this be under `retriable` above?



TODO
- offers - TODO
- <offerID> - TODO
210 changes: 83 additions & 127 deletions packages/orchestration/src/examples/sendAnywhere.contract.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
import { withdrawFromSeat } from '@agoric/zoe/src/contractSupport/zoeHelpers.js';
import { withdrawFromSeat as wFS } from '@agoric/zoe/src/contractSupport/zoeHelpers.js';
import { InvitationShape } from '@agoric/zoe/src/typeGuards.js';
import { M, mustMatch } from '@endo/patterns';
import { E } from '@endo/far';
import { heapVowE } from '@agoric/vow/vat.js';
import { makeStateRecord } from '@agoric/async-flow';
import { AmountShape } from '@agoric/ertp';
import { Fail } from '@endo/errors';
import { CosmosChainInfoShape } from '../typeGuards.js';
import { provideOrchestration } from '../utils/start-helper.js';
import { makeResumableAgoricNamesHack } from '../exos/agoric-names-tools.js';

const { entries } = Object;
import { withOrchestration } from '../utils/start-helper.js';
import { orchestrationFns } from './sendAnywhereOrchestration.js';

/**
* @import {Baggage} from '@agoric/vat-data';
Expand All @@ -33,141 +32,98 @@ const { entries } = Object;
* }} OrchestrationPowers
*/

/**
* @param {Orchestrator} orch
* @param {object} ctx
* @param {ZCF} ctx.zcf
* @param {any} ctx.agoricNamesTools TODO Give this a better type
* @param {{ account: OrchestrationAccount<any> | undefined }} ctx.contractState
* @param {ZCFSeat} seat
* @param {object} offerArgs
* @param {string} offerArgs.chainName
* @param {string} offerArgs.destAddr
*/
const sendItFn = async (
orch,
{ zcf, agoricNamesTools, contractState },
seat,
offerArgs,
) => {
mustMatch(offerArgs, harden({ chainName: M.scalar(), destAddr: M.string() }));
const { chainName, destAddr } = offerArgs;
const { give } = seat.getProposal();
const [[kw, amt]] = entries(give);
const { denom } = await agoricNamesTools.findBrandInVBank(amt.brand);
const chain = await orch.getChain(chainName);

if (!contractState.account) {
const agoricChain = await orch.getChain('agoric');
contractState.account = await agoricChain.makeAccount();
console.log('contractState.account', contractState.account);
}

const info = await chain.getChainInfo();
console.log('info', info);
const { chainId } = info;
assert(typeof chainId === 'string', 'bad chainId');
const { [kw]: pmtP } = await withdrawFromSeat(zcf, seat, give);
// #9212 types for chain account helpers
// @ts-expect-error LCA should have .deposit() method
await E.when(pmtP, pmt => contractState.account?.deposit(pmt));
await contractState.account?.transfer(
{ denom, value: amt.value },
{
address: destAddr,
addressEncoding: 'bech32',
chainId,
},
);
};

export const SingleAmountRecord = M.and(
M.recordOf(M.string(), AmountShape, {
numPropertiesLimit: 1,
}),
M.not(harden({})),
);

/**
* @param {ZCF} zcf
* @param {OrchestrationPowers & {
* marshaller: Marshaller;
* }} privateArgs
* @param {Baggage} baggage
*/
export const start = async (zcf, privateArgs, baggage) => {
const { chainHub, orchestrate, vowTools, zone } = provideOrchestration(
export const start = withOrchestration(
async (
zcf,
baggage,
privateArgs,
privateArgs.marshaller,
);
const agoricNamesTools = makeResumableAgoricNamesHack(zone, {
agoricNames: privateArgs.agoricNames,
vowTools,
});
zone,
{ vowTools, orchestrateAll, chainHub, zoeTools },
) => {
const contractState = makeStateRecord(
/** @type {{ account: OrchestrationAccount<any> | undefined }} */ {
account: undefined,
},
);

const contractState = makeStateRecord(
/** @type {{ account: OrchestrationAccount<any> | undefined }} */ {
account: undefined,
},
);
// TODO should be a provided helper
const findBrandInVBank = vowTools.retriable(
zone,
'findBrandInVBank',
async (_subzone, brand) => {
const agoricNames = privateArgs.agoricNames;
const assets = await E(E(agoricNames).lookup('vbankAsset')).values();
const it = assets.find(a => a.brand === brand);
it || Fail`brand ${brand} not in agoricNames.vbankAsset`;
return it;
},
);

/** @type {OfferHandler} */
const sendIt = orchestrate(
'sendIt',
{ zcf, agoricNamesTools, contractState },
sendItFn,
);
// orchestrate uses the names on orchestrationFns to do a "prepare" of the associated behavior
// TODO should orchestrateAll have `zone` passed in? how does that relate to
// the zone passed to orchestrationFns?
const orchFns = orchestrateAll(/* zone,*/ orchestrationFns, {
zcf,
contractState,
localTransfer: zoeTools.localTransfer,
findBrandInVBank,
});

const publicFacet = zone.exo(
'Send PF',
M.interface('Send PF', {
makeSendInvitation: M.callWhen().returns(InvitationShape),
}),
{
makeSendInvitation() {
return zcf.makeInvitation(
sendIt,
'send',
undefined,
M.splitRecord({ give: SingleAmountRecord }),
);
const publicFacet = zone.exo(
'Send PF',
M.interface('Send PF', {
makeSendInvitation: M.callWhen().returns(InvitationShape),
}),
{
makeSendInvitation() {
return zcf.makeInvitation(
orchFns.sendIt,
'send',
undefined,
M.splitRecord({ give: SingleAmountRecord }),
);
},
},
},
);
);

let nonce = 0n;
const ConnectionInfoShape = M.record(); // TODO
const creatorFacet = zone.exo(
'Send CF',
M.interface('Send CF', {
addChain: M.callWhen(CosmosChainInfoShape, ConnectionInfoShape).returns(
M.scalar(),
),
}),
{
/**
* @param {CosmosChainInfo} chainInfo
* @param {IBCConnectionInfo} connectionInfo
*/
async addChain(chainInfo, connectionInfo) {
const chainKey = `${chainInfo.chainId}-${(nonce += 1n)}`;
// when() because chainHub methods return vows. If this were inside
// orchestrate() the membrane would wrap/unwrap automatically.
const agoricChainInfo = await heapVowE.when(
chainHub.getChainInfo('agoric'),
);
chainHub.registerChain(chainKey, chainInfo);
chainHub.registerConnection(
agoricChainInfo.chainId,
chainInfo.chainId,
connectionInfo,
);
return chainKey;
let nonce = 0n;
const ConnectionInfoShape = M.record(); // TODO
const creatorFacet = zone.exo(
'Send CF',
M.interface('Send CF', {
addChain: M.callWhen(CosmosChainInfoShape, ConnectionInfoShape).returns(
M.scalar(),
),
}),
{
/**
* @param {CosmosChainInfo} chainInfo
* @param {IBCConnectionInfo} connectionInfo
*/
async addChain(chainInfo, connectionInfo) {
const chainKey = `${chainInfo.chainId}-${(nonce += 1n)}`;
// when() because chainHub methods return vows. If this were inside
// orchestrate() the membrane would wrap/unwrap automatically.
const agoricChainInfo = await heapVowE.when(
chainHub.getChainInfo('agoric'),
);
chainHub.registerChain(chainKey, chainInfo);
chainHub.registerConnection(
agoricChainInfo.chainId,
chainInfo.chainId,
connectionInfo,
);
return chainKey;
},
},
},
);
);

return { publicFacet, creatorFacet };
};
return { publicFacet, creatorFacet };
},
);
64 changes: 64 additions & 0 deletions packages/orchestration/src/examples/sendAnywhereOrchestration.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { M, mustMatch } from '@endo/patterns';

const { entries } = Object;

// in guest file (the orchestration functions)
// the second argument is all the endowments provided

/** @type {OfferHandler} */

export const orchestrationFns = harden({
/**
* @param {Orchestrator} orch
* @param {object} ctx
* @param {ZCF} ctx.zcf
* @param {{ account: OrchestrationAccount<any> }} ctx.contractState
* @param {any} ctx.localTransfer
* @param {any} ctx.findBrandInVBank
* @param {ZCFSeat} seat
* @param offerArgs

Check warning on line 19 in packages/orchestration/src/examples/sendAnywhereOrchestration.js

View workflow job for this annotation

GitHub Actions / lint-rest

Missing JSDoc @param "offerArgs" type
*/
async sendIt(
orch,
{ zcf, contractState, localTransfer, findBrandInVBank },
seat,
offerArgs,
) {
mustMatch(
offerArgs,
harden({ chainName: M.scalar(), destAddr: M.string() }),
);
const { chainName, destAddr } = offerArgs;
// NOTE the proposal shape ensures that the `give` is a single asset
const { give } = seat.getProposal();
const [[_kw, amt]] = entries(give);
const { denom } = await findBrandInVBank(amt.brand);
const chain = await orch.getChain(chainName);

if (!contractState.account) {
const agoricChain = await orch.getChain('agoric');
contractState.account = await agoricChain.makeAccount();
console.log('contractState.account', contractState.account);
}

const info = await chain.getChainInfo();
console.log('info', info);
const { chainId } = info;
assert(typeof chainId === 'string', 'bad chainId');

await localTransfer(seat, contractState.account, give);

await contractState.account.transfer(
{ denom, value: amt.value },
{
address: destAddr,
addressEncoding: 'bech32',
chainId,
},
);
},

fooFn(a) {
return bar();
},
});
Loading
Loading