From ba0e92cd046efbf9599a8b79d39d20f0e5e8cbea Mon Sep 17 00:00:00 2001 From: Mathieu Hofman Date: Tue, 23 Jul 2024 01:45:32 +0000 Subject: [PATCH 1/9] feat(vow): abandoned errors are retriable --- packages/internal/src/upgrade-api.js | 20 +++++++++++++++++++- packages/internal/test/upgrade-api.test.js | 15 +++++++++++++++ packages/vow/vat.js | 12 ++++++++++-- 3 files changed, 44 insertions(+), 3 deletions(-) diff --git a/packages/internal/src/upgrade-api.js b/packages/internal/src/upgrade-api.js index c2cadd7be16..a1b0fa9c640 100644 --- a/packages/internal/src/upgrade-api.js +++ b/packages/internal/src/upgrade-api.js @@ -45,5 +45,23 @@ harden(makeUpgradeDisconnection); * @returns {reason is UpgradeDisconnection} */ export const isUpgradeDisconnection = reason => - isFrozen(reason) && matches(reason, UpgradeDisconnectionShape); + reason != null && // eslint-disable-line eqeqeq + isFrozen(reason) && + matches(reason, UpgradeDisconnectionShape); harden(isUpgradeDisconnection); + +/** + * Returns whether a reason is a 'vat terminated' error generated when an object + * is abandoned by a vat during an upgrade. + * + * @param {any} reason + * @returns {reason is Error} + */ +export const isAbandonedError = reason => + reason != null && // eslint-disable-line eqeqeq + isFrozen(reason) && + matches(reason, M.error()) && + // We're not using a constant here since this special value is already + // sprinkled throughout the SDK + reason.message === 'vat terminated'; +harden(isAbandonedError); diff --git a/packages/internal/test/upgrade-api.test.js b/packages/internal/test/upgrade-api.test.js index 5ffc38f9b29..f8eee5d2d92 100644 --- a/packages/internal/test/upgrade-api.test.js +++ b/packages/internal/test/upgrade-api.test.js @@ -1,8 +1,11 @@ // @ts-check import test from 'ava'; +import { makeMarshal } from '@endo/marshal'; + import { makeUpgradeDisconnection, isUpgradeDisconnection, + isAbandonedError, } from '../src/upgrade-api.js'; test('isUpgradeDisconnection must recognize disconnection objects', t => { @@ -18,3 +21,15 @@ test('isUpgradeDisconnection must recognize original-format disconnection object }); t.true(isUpgradeDisconnection(disconnection)); }); + +test('isAbandonedError recognizes marshalled vat terminated errors', t => { + const { fromCapData, toCapData } = makeMarshal(undefined, undefined, { + serializeBodyFormat: 'smallcaps', + errorIdNum: 70_000, + marshalSaveError: () => {}, + }); + const error = harden(Error('vat terminated')); + const remoteError = fromCapData(toCapData(error)); + + t.true(isAbandonedError(remoteError)); +}); diff --git a/packages/vow/vat.js b/packages/vow/vat.js index a486730b9c4..ad7d177d409 100644 --- a/packages/vow/vat.js +++ b/packages/vow/vat.js @@ -5,7 +5,10 @@ /* global globalThis */ // @ts-check -import { isUpgradeDisconnection } from '@agoric/internal/src/upgrade-api.js'; +import { + isUpgradeDisconnection, + isAbandonedError, +} from '@agoric/internal/src/upgrade-api.js'; import { makeHeapZone } from '@agoric/base-zone/heap.js'; import { prepareBasicVowTools } from './src/tools.js'; @@ -15,11 +18,16 @@ import makeE from './src/E.js'; const isRetryableReason = (reason, priorRetryValue) => { if ( isUpgradeDisconnection(reason) && - (!priorRetryValue || + (!isUpgradeDisconnection(priorRetryValue) || reason.incarnationNumber > priorRetryValue.incarnationNumber) ) { return reason; } + // For abandoned errors there is no way to differentiate errors from + // consecutive upgrades + if (isAbandonedError(reason) && !isAbandonedError(priorRetryValue)) { + return reason; + } return undefined; }; From 7bb54b5e83d00c5df8757ab857e8a211537a1107 Mon Sep 17 00:00:00 2001 From: Mathieu Hofman Date: Tue, 23 Jul 2024 07:36:11 +0000 Subject: [PATCH 2/9] WIP feat(vow): retriable tools --- packages/vow/src/retriable.js | 218 ++++++++++++++++++++++++++++++++++ packages/vow/src/tools.js | 22 +--- 2 files changed, 223 insertions(+), 17 deletions(-) create mode 100644 packages/vow/src/retriable.js diff --git a/packages/vow/src/retriable.js b/packages/vow/src/retriable.js new file mode 100644 index 00000000000..c58e7621cfd --- /dev/null +++ b/packages/vow/src/retriable.js @@ -0,0 +1,218 @@ +import { Fail } from '@endo/errors'; +import { M } from '@endo/patterns'; +import { PromiseWatcherI } from '@agoric/base-zone'; +import { toPassableCap, VowShape } from './vow-utils.js'; + +/** + * @import {WeakMapStore} from '@agoric/store' + * @import {Zone} from '@agoric/base-zone' + * @import {Vow, VowKit, IsRetryableReason} from './types.js' + * @import {Passable} from '@endo/pass-style' + */ + +/** + * @typedef {object} PreparationOptions + * @property {() => VowKit} makeVowKit + * @property {IsRetryableReason} isRetryableReason + */ + +/** + * @template {Passable[]} [TArgs=Passable[]] + * @template {any} [TRet=any] + * @typedef {(...args: TArgs) => Promise} RetriableFunc + */ + +const { defineProperties } = Object; + +const RetriableFlowIKit = harden({ + flow: M.interface('Flow', { + restart: M.call().returns(), + getOutcome: M.call().returns(VowShape), + }), + resultWatcher: PromiseWatcherI, +}); + +const AdminRetriableFlowI = M.interface('RetriableFlowAdmin', { + getFlowForOutcomeVow: M.call(VowShape).returns(M.opt(M.remotable('flow'))), +}); + +/** + * @param {Zone} outerZone + * @param {PreparationOptions} [outerOptions] + */ +export const prepareRetriableTools = (outerZone, outerOptions = {}) => { + const { makeVowKit, isRetryableReason } = outerOptions; + + /** + * So we can give out wrapper functions easily and recover flow objects + * for their activations later. + */ + const flowForOutcomeVowKey = outerZone.mapStore( + 'retriableFlowForOutcomeVow', + { + keyShape: M.remotable('toPassableCap'), + valueShape: M.remotable('flow'), // isDone === false + }, + ); + + /** + * @param {Zone} zone + * @param {string} tag + * @param {RetriableFunc} retriableFunc + */ + const prepareRetriableFlowKit = (zone, tag, retriableFunc) => { + typeof retriableFunc === 'function' || + Fail`retriableFunc must be a callable function ${retriableFunc}`; + + const internalMakeRetriableFlowKit = zone.exoClassKit( + tag, + RetriableFlowIKit, + activationArgs => { + harden(activationArgs); + + return { + activationArgs, // restarting the retriable function uses the original args + outcomeKit: makeVowKit(), // outcome of activation as vow + lastRetryReason: undefined, + runs: 0n, + isDone: false, // persistently done + }; + }, + { + flow: { + /** + * Calls the retriable function, either for the initial run or when + * the result of the previous run fails with a retriable reason. + */ + restart() { + const { state, facets } = this; + const { activationArgs, isDone } = state; + const { flow, resultWatcher } = facets; + + !isDone || + // separate line so I can set a breakpoint + Fail`Cannot restart a done retriable flow ${flow}`; + + const runId = state.runs + 1n; + state.runs = runId; + + let resultP; + try { + resultP = Promise.resolve(retriableFunc(...activationArgs)); + } catch (err) { + resultP = Promise.resolve(() => Promise.reject(err)); + } + + outerZone.watchPromise(harden(resultP), resultWatcher, runId); + }, + getOutcome() { + const { state } = this; + const { outcomeKit } = state; + return outcomeKit.vow; + }, + }, + resultWatcher: { + onFulfilled(value, runId) { + const { state } = this; + const { runs, outcomeKit } = state; + if (runId !== runs) return; + !state.isDone || + Fail`Cannot resolve a done retriable flow ${this.facets.flow}`; + outcomeKit.resolver.resolve(value); + flowForOutcomeVowKey.delete(toPassableCap(outcomeKit.vow)); + state.isDone = true; + }, + onRejected(reason, runId) { + const { state } = this; + const { runs, outcomeKit } = state; + if (runId !== runs) return; + !state.isDone || + Fail`Cannot reject a done retriable flow ${this.facets.flow}`; + const retryReason = isRetryableReason( + reason, + state.lastRetryReason, + ); + if (retryReason) { + state.lastRetryReason = retryReason; + this.facets.flow.restart(); + } else { + outcomeKit.resolver.reject(reason); + flowForOutcomeVowKey.delete(toPassableCap(outcomeKit.vow)); + state.isDone = true; + } + }, + }, + }, + ); + const makeRetriableFlowKit = activationArgs => { + const retriableKit = internalMakeRetriableFlowKit(activationArgs); + const { flow } = retriableKit; + + const vow = flow.getOutcome(); + flowForOutcomeVowKey.init(toPassableCap(vow), flow); + flow.restart(); + return retriableKit; + }; + return harden(makeRetriableFlowKit); + }; + + /** + * @template {RetriableFunc} F + * @param {Zone} zone + * @param {string} tag + * @param {F} retriableFunc + */ + const retriable = (zone, tag, retriableFunc) => { + const makeRetriableKit = prepareRetriableFlowKit(zone, tag, retriableFunc); + const wrapperFuncName = `${tag}_retriable`; + + const wrapperFunc = { + /** @type {(...args: Parameters) => Vow>>} */ + [wrapperFuncName](...args) { + const { flow } = makeRetriableKit(args); + return flow.getOutcome(); + }, + }[wrapperFuncName]; + defineProperties(wrapperFunc, { + length: { value: retriableFunc.length }, + }); + return harden(wrapperFunc); + }; + + const adminRetriableFlow = outerZone.exo( + 'AdminRetriableFlow', + AdminRetriableFlowI, + { + getFlowForOutcomeVow(outcomeVow) { + return flowForOutcomeVowKey.get(toPassableCap(outcomeVow)); + }, + }, + ); + + return harden({ + prepareRetriableFlowKit, + adminRetriableFlow, + retriable, + }); +}; +harden(prepareRetriableTools); + +/** + * @typedef {ReturnType} RetriableTools + */ + +/** + * @typedef {RetriableTools['adminRetriableFlow']} AdminRetriableFlow + */ + +/** + * @typedef {ReturnType} MakeRetriableFlowKit + */ + +/** + * @typedef {ReturnType} RetriableFlowKit + */ + +/** + * @typedef {RetriableFlowKit['flow']} RetriableFlow + */ diff --git a/packages/vow/src/tools.js b/packages/vow/src/tools.js index ff35539726c..432d2ffbb83 100644 --- a/packages/vow/src/tools.js +++ b/packages/vow/src/tools.js @@ -3,6 +3,7 @@ import { makeAsVow } from './vow-utils.js'; import { prepareVowKit } from './vow.js'; import { prepareWatchUtils } from './watch-utils.js'; import { prepareWatch } from './watch.js'; +import { prepareRetriableTools } from './retriable.js'; import { makeWhen } from './when.js'; /** @@ -33,23 +34,10 @@ export const prepareBasicVowTools = (zone, powers = {}) => { const watchUtils = makeWatchUtils(); const asVow = makeAsVow(makeVowKit); - /** - * TODO FIXME make this real - * Create a function that retries the given function if the underlying - * functions rejects due to upgrade disconnection. - * - * @template {(...args: any[]) => Promise} F - * @param {Zone} fnZone - the zone for the named function - * @param {string} name - * @param {F} fn - * @returns {F extends (...args: infer Args) => Promise ? (...args: Args) => Vow : never} - */ - const retriable = - (fnZone, name, fn) => - // @ts-expect-error cast - (...args) => { - return watch(fn(...args)); - }; + const { retriable } = prepareRetriableTools(zone, { + makeVowKit, + isRetryableReason, + }); /** * Vow-tolerant implementation of Promise.all. From 816952d2756894e443f0646bf50e0a9adc65a76f Mon Sep 17 00:00:00 2001 From: Mathieu Hofman Date: Tue, 23 Jul 2024 08:25:52 +0000 Subject: [PATCH 3/9] WIP fix(orchestration): chainHub accepts zone --- packages/orchestration/src/examples/stakeBld.contract.js | 6 +++++- packages/orchestration/src/exos/chain-hub.js | 7 +++---- packages/orchestration/src/proposals/start-stakeAtom.js | 9 +++++++-- packages/orchestration/src/proposals/start-stakeOsmo.js | 9 +++++++-- packages/orchestration/src/utils/start-helper.js | 4 +++- packages/orchestration/test/exos/chain-hub.test.ts | 4 ++-- 6 files changed, 27 insertions(+), 12 deletions(-) diff --git a/packages/orchestration/src/examples/stakeBld.contract.js b/packages/orchestration/src/examples/stakeBld.contract.js index d55f82618f6..b3d53404aef 100644 --- a/packages/orchestration/src/examples/stakeBld.contract.js +++ b/packages/orchestration/src/examples/stakeBld.contract.js @@ -42,7 +42,11 @@ export const start = async (zcf, privateArgs, baggage) => { ); const vowTools = prepareVowTools(zone.subZone('vows')); - const chainHub = makeChainHub(privateArgs.agoricNames, vowTools); + const chainHub = makeChainHub( + zone.subZone('chainHub'), + privateArgs.agoricNames, + vowTools, + ); const { localchain, timerService } = privateArgs; const makeLocalOrchestrationAccountKit = prepareLocalOrchestrationAccountKit( diff --git a/packages/orchestration/src/exos/chain-hub.js b/packages/orchestration/src/exos/chain-hub.js index f938771a480..5a6afdb20a0 100644 --- a/packages/orchestration/src/exos/chain-hub.js +++ b/packages/orchestration/src/exos/chain-hub.js @@ -4,7 +4,6 @@ import { M } from '@endo/patterns'; import { BrandShape } from '@agoric/ertp/src/typeGuards.js'; import { VowShape } from '@agoric/vow'; -import { makeHeapZone } from '@agoric/zone'; import { CosmosChainInfoShape, IBCConnectionInfoShape } from '../typeGuards.js'; /** @@ -174,18 +173,18 @@ const ChainHubI = M.interface('ChainHub', { }); /** - * Make a new ChainHub in the zone (or in the heap if no zone is provided). + * Make a new ChainHub in the zone. * * The resulting object is an Exo singleton. It has no precious state. It's only * state is a cache of queries to agoricNames and whatever info was provided in * registration calls. When you need a newer version you can simply make a hub * hub and repeat the registrations. * + * @param {Zone} zone * @param {Remote} agoricNames * @param {VowTools} vowTools */ -export const makeChainHub = (agoricNames, vowTools) => { - const zone = makeHeapZone(); +export const makeChainHub = (zone, agoricNames, vowTools) => { /** @type {MapStore} */ const chainInfos = zone.mapStore('chainInfos', { keyShape: M.string(), diff --git a/packages/orchestration/src/proposals/start-stakeAtom.js b/packages/orchestration/src/proposals/start-stakeAtom.js index 838bacfd832..cab85042813 100644 --- a/packages/orchestration/src/proposals/start-stakeAtom.js +++ b/packages/orchestration/src/proposals/start-stakeAtom.js @@ -46,8 +46,13 @@ export const startStakeAtom = async ({ const storageNode = await makeStorageNodeChild(chainStorage, VSTORAGE_PATH); const marshaller = await E(board).getPublishingMarshaller(); - const vt = prepareVowTools(makeHeapZone()); - const chainHub = makeChainHub(await agoricNames, vt); + const zone = makeHeapZone(); + const vt = prepareVowTools(zone.subZone('vows')); + const chainHub = makeChainHub( + zone.subZone('chainHub'), + await agoricNames, + vt, + ); const [_, cosmoshub, connectionInfo] = await vt.when( chainHub.getChainsAndConnection('agoric', 'cosmoshub'), diff --git a/packages/orchestration/src/proposals/start-stakeOsmo.js b/packages/orchestration/src/proposals/start-stakeOsmo.js index 84dfadb1337..7ccff4d8658 100644 --- a/packages/orchestration/src/proposals/start-stakeOsmo.js +++ b/packages/orchestration/src/proposals/start-stakeOsmo.js @@ -51,8 +51,13 @@ export const startStakeOsmo = async ({ const storageNode = await makeStorageNodeChild(chainStorage, VSTORAGE_PATH); const marshaller = await E(board).getPublishingMarshaller(); - const vt = prepareVowTools(makeHeapZone()); - const chainHub = makeChainHub(await agoricNames, vt); + const zone = makeHeapZone(); + const vt = prepareVowTools(zone.subZone('vows')); + const chainHub = makeChainHub( + zone.subZone('chainHub'), + await agoricNames, + vt, + ); const [_, osmosis, connectionInfo] = await vt.when( chainHub.getChainsAndConnection('agoric', 'osmosis'), diff --git a/packages/orchestration/src/utils/start-helper.js b/packages/orchestration/src/utils/start-helper.js index f11da8db32c..6b003406bcf 100644 --- a/packages/orchestration/src/utils/start-helper.js +++ b/packages/orchestration/src/utils/start-helper.js @@ -61,6 +61,8 @@ export const provideOrchestration = ( asyncFlow: zone.subZone('asyncFlow'), /** system names for orchestration implementation */ orchestration: zone.subZone('orchestration'), + /** system names for chainHub */ + chainHub: zone.subZone('chainHub'), /** system names for vows */ vows: zone.subZone('vows'), /** system names for zoe */ @@ -74,7 +76,7 @@ export const provideOrchestration = ( const vowTools = prepareVowTools(zones.vows); - const chainHub = makeChainHub(agoricNames, vowTools); + const chainHub = makeChainHub(zones.chainHub, agoricNames, vowTools); const zoeTools = makeZoeTools(zones.zoe, { zcf, vowTools }); diff --git a/packages/orchestration/test/exos/chain-hub.test.ts b/packages/orchestration/test/exos/chain-hub.test.ts index bfd7fcb1dd7..aaef51a8b92 100644 --- a/packages/orchestration/test/exos/chain-hub.test.ts +++ b/packages/orchestration/test/exos/chain-hub.test.ts @@ -45,9 +45,9 @@ const connection = { // fresh state for each test const setup = () => { const zone = provideDurableZone('root'); - const vt = prepareSwingsetVowTools(zone); + const vt = prepareSwingsetVowTools(zone.subZone('vows')); const { nameHub, nameAdmin } = makeNameHubKit(); - const chainHub = makeChainHub(nameHub, vt); + const chainHub = makeChainHub(zone.subZone('chainHub'), nameHub, vt); return { chainHub, nameAdmin, vt }; }; From 57d8f9c5e0ed5def7c233f32e1d466ffccac567b Mon Sep 17 00:00:00 2001 From: Mathieu Hofman Date: Tue, 23 Jul 2024 08:27:44 +0000 Subject: [PATCH 4/9] WIP Update test snapshots --- .../snapshots/send-anywhere.test.ts.md | 54 +++++++----- .../snapshots/unbond.contract.test.ts.md | 87 ++++++++++++++++++- 2 files changed, 116 insertions(+), 25 deletions(-) diff --git a/packages/orchestration/test/examples/snapshots/send-anywhere.test.ts.md b/packages/orchestration/test/examples/snapshots/send-anywhere.test.ts.md index 506844dfddd..9cc05e0959f 100644 --- a/packages/orchestration/test/examples/snapshots/send-anywhere.test.ts.md +++ b/packages/orchestration/test/examples/snapshots/send-anywhere.test.ts.md @@ -1,6 +1,6 @@ -# Snapshot report for `test/examples/send-anywhere.test.ts` +# Snapshot report for `test/examples/sendAnywhere.test.ts` -The actual snapshot is saved in `send-anywhere.test.ts.snap`. +The actual snapshot is saved in `sendAnywhere.test.ts.snap`. Generated by [AVA](https://avajs.dev). @@ -24,24 +24,21 @@ Generated by [AVA](https://avajs.dev). flowForOutcomeVow: {}, unwrapMap: 'Alleged: weakMapStore', }, + chainHub: { + ChainHub_kindHandle: 'Alleged: kind', + ChainHub_singleton: 'Alleged: ChainHub', + chainInfos: {}, + connectionInfos: {}, + lookupChainInfo_kindHandle: 'Alleged: kind', + lookupChainsAndConnection_kindHandle: 'Alleged: kind', + lookupConnectionInfo_kindHandle: 'Alleged: kind', + }, contract: { 'ChainHub Admin_kindHandle': 'Alleged: kind', 'ChainHub Admin_singleton': 'Alleged: ChainHub Admin', 'Send PF_kindHandle': 'Alleged: kind', 'Send PF_singleton': 'Alleged: Send PF', - orchestration: { - sendIt: { - asyncFlow_kindHandle: 'Alleged: kind', - endowments: { - 0: { - contractState_kindHandle: 'Alleged: kind', - contractState_singleton: 'Alleged: contractState', - localTransfer_kindHandle: 'Alleged: kind', - localTransfer_singleton: 'Alleged: localTransfer', - }, - }, - }, - }, + findBrandInVBank_kindHandle: 'Alleged: kind', }, orchestration: { 'Cosmos Orchestration Account Holder_kindHandle': 'Alleged: kind', @@ -49,20 +46,29 @@ Generated by [AVA](https://avajs.dev). LocalChainFacade_kindHandle: 'Alleged: kind', Orchestrator_kindHandle: 'Alleged: kind', RemoteChainFacade_kindHandle: 'Alleged: kind', - chainName: {}, - ibcTools: { - IBCTransferSenderKit_kindHandle: 'Alleged: kind', - ibcResultWatcher_kindHandle: 'Alleged: kind', - ibcResultWatcher_singleton: 'Alleged: ibcResultWatcher', - }, - packetTools: { - PacketToolsKit_kindHandle: 'Alleged: kind', + sendIt: { + asyncFlow_kindHandle: 'Alleged: kind', + endowments: { + 1: { + contractState_kindHandle: 'Alleged: kind', + contractState_singleton: 'Alleged: contractState', + findBrandInVBank_kindHandle: 'Alleged: kind', + findBrandInVBank_singleton: 'Alleged: findBrandInVBank', + localTransfer_kindHandle: 'Alleged: kind', + localTransfer_singleton: 'Alleged: localTransfer', + }, + }, }, }, vows: { + AdminRetriableFlow_kindHandle: 'Alleged: kind', + AdminRetriableFlow_singleton: 'Alleged: AdminRetriableFlow', PromiseWatcher_kindHandle: 'Alleged: kind', VowInternalsKit_kindHandle: 'Alleged: kind', WatchUtils_kindHandle: 'Alleged: kind', + retriableFlowForOutcomeVow: {}, + }, + zoe: { + localTransfer_kindHandle: 'Alleged: kind', }, - zoe: {}, } diff --git a/packages/orchestration/test/examples/snapshots/unbond.contract.test.ts.md b/packages/orchestration/test/examples/snapshots/unbond.contract.test.ts.md index dc4bf4aa5a3..fd6e662260c 100644 --- a/packages/orchestration/test/examples/snapshots/unbond.contract.test.ts.md +++ b/packages/orchestration/test/examples/snapshots/unbond.contract.test.ts.md @@ -24,6 +24,86 @@ Generated by [AVA](https://avajs.dev). flowForOutcomeVow: {}, unwrapMap: 'Alleged: weakMapStore', }, + chainHub: { + ChainHub_kindHandle: 'Alleged: kind', + ChainHub_singleton: 'Alleged: ChainHub', + chainInfos: { + agoric: { + chainId: 'agoric-3', + icqEnabled: false, + stakingTokens: [ + { + denom: 'ubld', + }, + ], + }, + omniflixhub: { + chainId: 'omniflixhub-1', + icqEnabled: false, + stakingTokens: [ + { + denom: 'uflix', + }, + ], + }, + stride: { + chainId: 'stride-1', + icqEnabled: false, + stakingTokens: [ + { + denom: 'ustrd', + }, + ], + }, + }, + connectionInfos: { + 'agoric-3_omniflixhub-1': { + client_id: '07-tendermint-73', + counterparty: { + client_id: '07-tendermint-47', + connection_id: 'connection-40', + prefix: { + key_prefix: 'FIXME', + }, + }, + id: 'connection-67', + state: 3, + transferChannel: { + channelId: 'channel-58', + counterPartyChannelId: 'channel-30', + counterPartyPortId: 'transfer', + ordering: 0, + portId: 'transfer', + state: 3, + version: 'ics20-1', + }, + }, + 'agoric-3_stride-1': { + client_id: '07-tendermint-74', + counterparty: { + client_id: '07-tendermint-129', + connection_id: 'connection-118', + prefix: { + key_prefix: 'FIXME', + }, + }, + id: 'connection-68', + state: 3, + transferChannel: { + channelId: 'channel-59', + counterPartyChannelId: 'channel-148', + counterPartyPortId: 'transfer', + ordering: 0, + portId: 'transfer', + state: 3, + version: 'ics20-1', + }, + }, + }, + lookupChainInfo_kindHandle: 'Alleged: kind', + lookupChainsAndConnection_kindHandle: 'Alleged: kind', + lookupConnectionInfo_kindHandle: 'Alleged: kind', + }, contract: { orchestration: { unbondAndLiquidStake: { @@ -53,9 +133,14 @@ Generated by [AVA](https://avajs.dev). }, }, vows: { + AdminRetriableFlow_kindHandle: 'Alleged: kind', + AdminRetriableFlow_singleton: 'Alleged: AdminRetriableFlow', PromiseWatcher_kindHandle: 'Alleged: kind', VowInternalsKit_kindHandle: 'Alleged: kind', WatchUtils_kindHandle: 'Alleged: kind', + retriableFlowForOutcomeVow: {}, + }, + zoe: { + localTransfer_kindHandle: 'Alleged: kind', }, - zoe: {}, } From 10ddcc6bb7223912e86307cc561f595dca179eed Mon Sep 17 00:00:00 2001 From: Turadg Aleahmad Date: Mon, 29 Jul 2024 17:13:23 -0400 Subject: [PATCH 5/9] chore(types): relax RetriableFunc generic --- packages/vow/src/retriable.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/vow/src/retriable.js b/packages/vow/src/retriable.js index c58e7621cfd..eaa110ff292 100644 --- a/packages/vow/src/retriable.js +++ b/packages/vow/src/retriable.js @@ -17,7 +17,7 @@ import { toPassableCap, VowShape } from './vow-utils.js'; */ /** - * @template {Passable[]} [TArgs=Passable[]] + * @template {Passable[]} [TArgs=any[]] * @template {any} [TRet=any] * @typedef {(...args: TArgs) => Promise} RetriableFunc */ From 146884b575932f88b4bbc5c859a1586bf8ab6c8e Mon Sep 17 00:00:00 2001 From: Turadg Aleahmad Date: Mon, 29 Jul 2024 16:51:57 -0400 Subject: [PATCH 6/9] fixups --- .../src/examples/send-anywhere.flows.js | 7 +- .../src/examples/stakeIca.contract.js | 6 +- packages/orchestration/src/exos/chain-hub.js | 1 + packages/orchestration/src/utils/zoe-tools.js | 1 + .../snapshots/send-anywhere.test.ts.snap | Bin 1064 -> 1288 bytes .../snapshots/staking-combinations.test.ts.md | 89 +++++++++++++++++- .../staking-combinations.test.ts.snap | Bin 1624 -> 2611 bytes .../snapshots/unbond.contract.test.ts.md | 23 ++--- .../snapshots/unbond.contract.test.ts.snap | Bin 991 -> 1950 bytes .../orchestration/test/staking-ops.test.ts | 10 +- packages/orchestration/test/supports.ts | 6 +- packages/vow/src/retriable.js | 4 +- .../contracts/snapshots/valueVow.test.js.md | 3 + .../contracts/snapshots/valueVow.test.js.snap | Bin 451 -> 510 bytes 14 files changed, 128 insertions(+), 22 deletions(-) diff --git a/packages/orchestration/src/examples/send-anywhere.flows.js b/packages/orchestration/src/examples/send-anywhere.flows.js index 8573dea7b13..6c7128df268 100644 --- a/packages/orchestration/src/examples/send-anywhere.flows.js +++ b/packages/orchestration/src/examples/send-anywhere.flows.js @@ -49,7 +49,12 @@ export const sendIt = async ( const { chainId } = info; assert(typeof chainId === 'string', 'bad chainId'); - await localTransfer(seat, contractState.localAccount, give); + await localTransfer( + seat, + // @ts-expect-error Index signature for type 'string' is missing in type 'OrchestrationAccountI & LocalAccountMethods' + contractState.localAccount, + give, + ); await contractState.localAccount.transfer( { denom, value: amt.value }, diff --git a/packages/orchestration/src/examples/stakeIca.contract.js b/packages/orchestration/src/examples/stakeIca.contract.js index d6b89145368..11dde1662b2 100644 --- a/packages/orchestration/src/examples/stakeIca.contract.js +++ b/packages/orchestration/src/examples/stakeIca.contract.js @@ -84,7 +84,11 @@ export const start = async (zcf, privateArgs, baggage) => { const vowTools = prepareVowTools(zone.subZone('vows')); - const chainHub = makeChainHub(agoricNames, vowTools); + const chainHub = makeChainHub( + zone.subZone('chainHub'), + agoricNames, + vowTools, + ); const makeCosmosOrchestrationAccount = prepareCosmosOrchestrationAccount( zone, diff --git a/packages/orchestration/src/exos/chain-hub.js b/packages/orchestration/src/exos/chain-hub.js index 5a6afdb20a0..0f4356d9777 100644 --- a/packages/orchestration/src/exos/chain-hub.js +++ b/packages/orchestration/src/exos/chain-hub.js @@ -9,6 +9,7 @@ import { CosmosChainInfoShape, IBCConnectionInfoShape } from '../typeGuards.js'; /** * @import {NameHub} from '@agoric/vats'; * @import {Vow, VowTools} from '@agoric/vow'; + * @import {Zone} from '@agoric/zone'; * @import {CosmosAssetInfo, CosmosChainInfo, IBCConnectionInfo} from '../cosmos-api.js'; * @import {ChainInfo, KnownChains} from '../chain-info.js'; * @import {Denom} from '../orchestration-api.js'; diff --git a/packages/orchestration/src/utils/zoe-tools.js b/packages/orchestration/src/utils/zoe-tools.js index abf7f5bb683..dd7fd399c90 100644 --- a/packages/orchestration/src/utils/zoe-tools.js +++ b/packages/orchestration/src/utils/zoe-tools.js @@ -42,6 +42,7 @@ export const makeZoeTools = (zone, { zcf, vowTools }) => { /** * @type {LocalTransfer} */ + // @ts-expect-error XXX typedefs async (srcSeat, localAccount, give) => { !srcSeat.hasExited() || Fail`The seat cannot have exited.`; const { zcfSeat: tempSeat, userSeat: userSeatP } = zcf.makeEmptySeatKit(); diff --git a/packages/orchestration/test/examples/snapshots/send-anywhere.test.ts.snap b/packages/orchestration/test/examples/snapshots/send-anywhere.test.ts.snap index 1017ab57dbcf460571804c45ac4b9397c78be41f..db5e4fcd09358ecf830457f606c586941142ccf2 100644 GIT binary patch literal 1288 zcmV+j1^4idF^bwLi z!~s>~-HEegJ!5vqHAP%{MB)GfA#SMU&@%^mLx=+hBr0*>fK;hcC6GYkzzGQnB;s|} z-knV>Z^$Kk2RU|a=nKs8dICh7QVy$ zF$^n&Y!e`Y4I13y_KGLEA@o%Y((N7U^3_fwME*%2f=i#;GDKd=4G~lL_6`fAPo*n( z2;Ypsg`1)qgx`J{`!@fsV7>58{+t6I<$x1;faQVD^T6Xg@InFDDFF8hz=HztX91Yf zlu+lAq1H)xT>~~W;C&7FP6Hlmz`q)BvIv|l0-Yl8O;M34sxrkw2`HCcE znHYA}bicDZ+{16;2WI<}Fv)LE z9{$W7@UB0X+lp`jTyn4wv;+;f?oPCp1rA5H|G=kvziQun_8n(Osc7butgB z*`X^U^KBM4SxZQk?f42IpT!bkgspmswpa+=k3qWIu&)T=^;`7U7&6hO_AZnApb1h` zJUbEA*(Q^|+gr_!@)9l{AdxDxh2(3PV5`lx;ft0Vu3F&omsB{ z&r}rJ)W|buQX3@+)6Xiy^cMrhoi!PvdG$G3JGUGqx;Jc@f0QxiHtbCnbUb;~?nnuw zT6Jz~=n0Y0q&KdLz}Q&G;T3p35IvR4TCBbJ{sDg;HEPz?S7Z&il1)QoXT2@M{piY4356o~0&ipDRty8%? yJ}^!Y|L+t^`jE69;X{JRJlbI1JByKH_5FWL)akZlem#)Xm;V86r0Pq(5C8yUduA5^ literal 1064 zcmV+@1lRjPRzVSI=)7MHqc!d%a%&Hc4922I>nJBz{Op95~?yEJvgyiu;2i zm1?{@vA15&ShKS=g;OrvkPs3g+@Nsc#D#wVB&1$|Qzhu(Nwp0J1h(L<(O<*|N`_WQKYT%XgiKM=}4dgwS*(c9&(voJCJkpd~S z0qg?!48S7*e**Xqz;gt6jR0#T-yr$D-aTe3LgtV4dL5(!VuIALMl$Op3t}|L^KS)` z_B__y4|<;K51Q||>h93xPKR>GV-c*<@;ugO&Ku2XOw(99V78DBlPeIbgmefHg)Qno z;C9;+lQ8t+ znVg-EPnJ-PIjT)vFOX~o`ED6<=!r>N$h|<>V#IDHx@84nme=Swdb2T4ZNljA8Xbov z2pwnW``jK-mv@5R%n_*_r_s-FTpX$CJkw>|Y3{evuHT4wiG}NP+6g2q>`#T$FSN?^ zz{cfY-WL8y_|3g#WV2=4BH*go5#DOoZ%p&WU13x26v=bUfy<#@Slu?Ysgv637Z<6B z)VtX_U?ZVey5*~c{I%2w%bv2|r6U%GOlxTzxjp;35Z>%qyuK7Ejj26k>Lggf^mW|X z`%;WtpWUL$9;9#6rJG{1!xfX9dj2`uvJtL(L%E*60`+847%eI31CouNYPDKoisa6m zF@`>{GIuP%wzNTIo+(mJOudgS;K%}gu>iBEiB%G7c8kDm9ac?@KN5EJxVY$Q-e=s| zQUBY}D?3-WuS?4PLnfzZn#uE=7Pn^~FhB6rd3})(NL)RebH0v^cC8t8;pEkJR3O=( zi@>8IkS%FdE+nd)(Wg62@qJMxXdJM zUILCwz~d6IRR-QKYg)yGR!$#5(HCntu~T1uadx7Y7v|&`P|v}e-ocSMQ|Az8pOHfh i$UNF(-iKRJV$GBPGEI7@m~8rrO7#>4aHNLt3;+O(aSQnX diff --git a/packages/orchestration/test/examples/snapshots/staking-combinations.test.ts.md b/packages/orchestration/test/examples/snapshots/staking-combinations.test.ts.md index 8fa76401b61..f58f00f86e4 100644 --- a/packages/orchestration/test/examples/snapshots/staking-combinations.test.ts.md +++ b/packages/orchestration/test/examples/snapshots/staking-combinations.test.ts.md @@ -24,6 +24,88 @@ Generated by [AVA](https://avajs.dev). flowForOutcomeVow: {}, unwrapMap: 'Alleged: weakMapStore', }, + chainHub: { + ChainHub_kindHandle: 'Alleged: kind', + ChainHub_singleton: 'Alleged: ChainHub', + brandDenom: { + 'Alleged: BLD brand': 'ubld', + }, + chainInfos: { + agoric: { + chainId: 'agoric-3', + icqEnabled: false, + stakingTokens: [ + { + denom: 'ubld', + }, + ], + }, + cosmoshub: { + chainId: 'cosmoshub-4', + icqEnabled: false, + stakingTokens: [ + { + denom: 'uatom', + }, + ], + }, + }, + connectionInfos: { + 'agoric-3_cosmoshub-4': { + client_id: '07-tendermint-6', + counterparty: { + client_id: '07-tendermint-927', + connection_id: 'connection-649', + prefix: { + key_prefix: 'FIXME', + }, + }, + id: 'connection-8', + state: 3, + transferChannel: { + channelId: 'channel-5', + counterPartyChannelId: 'channel-405', + counterPartyPortId: 'transfer', + ordering: 0, + portId: 'transfer', + state: 3, + version: 'ics20-1', + }, + }, + 'cosmoshub-4_osmosis-1': { + client_id: '07-tendermint-259', + counterparty: { + client_id: '07-tendermint-1', + connection_id: 'connection-1', + prefix: { + key_prefix: 'FIXME', + }, + }, + id: 'connection-257', + state: 3, + transferChannel: { + channelId: 'channel-141', + counterPartyChannelId: 'channel-0', + counterPartyPortId: 'transfer', + ordering: 0, + portId: 'transfer', + state: 3, + version: 'ics20-1', + }, + }, + }, + denom: { + ubld: { + baseDenom: 'ubld', + baseName: 'agoric', + brand: Object @Alleged: BLD brand {}, + chainName: 'agoric', + }, + }, + lookupChainInfo_kindHandle: 'Alleged: kind', + lookupChainsAndConnection_kindHandle: 'Alleged: kind', + lookupConnectionInfo_kindHandle: 'Alleged: kind', + }, contract: { 'ChainHub Admin_kindHandle': 'Alleged: kind', 'ChainHub Admin_singleton': 'Alleged: ChainHub Admin', @@ -129,9 +211,14 @@ Generated by [AVA](https://avajs.dev). }, }, vows: { + AdminRetriableFlow_kindHandle: 'Alleged: kind', + AdminRetriableFlow_singleton: 'Alleged: AdminRetriableFlow', PromiseWatcher_kindHandle: 'Alleged: kind', VowInternalsKit_kindHandle: 'Alleged: kind', WatchUtils_kindHandle: 'Alleged: kind', + retriableFlowForOutcomeVow: {}, + }, + zoe: { + localTransfer_kindHandle: 'Alleged: kind', }, - zoe: {}, } diff --git a/packages/orchestration/test/examples/snapshots/staking-combinations.test.ts.snap b/packages/orchestration/test/examples/snapshots/staking-combinations.test.ts.snap index 0419690848faed7bf50e70dc8e24ce45df0f447f..a511de873e187dfa503735f00c43428f4290a1ca 100644 GIT binary patch literal 2611 zcmV-33e5FERzV_HPDYV zZPy~iqU-Au=g!yny7yi;?Ya&P_`@_{o8SlR53Pi{A+-J)1Aa`?U>YEGXsTeH3KDD* zP&5glF~L5>hwrto?>)y&?En>%nqRTs=Y7t5-t(UKea|_*u#%P(A*Vfhk?5i>5u)k5 zs*9S;7qnGH*B-r?$?!V=nY*T&;6>?=^KmVo0j16*_#*%p2Xu45ZVouY0THgb zgKIvVevk+{$65cjx3`091Lp%5LWFZgxh8O)4z71^QRUMTNi7!BlBlhwJ}v6YIZ@8+ z=jDtn}ADAz>Owgmm4U!fy-{-mu}$iZXoHQT2>Ex?N{z=tiseSYAiA5i@i#a%Ro_iKLO8~=u1^8;f6AR7QK1%S5#lI$|3 z(N+pJ%|vB@im0yHOkR{{E3wgNK*lL`O_Z||(G|G@*koxtkrYpKk65x&ROCi2=$HA;~f@(@0nZ44B@c2bAnlT~SG+W^^6Zd7U&eiy+4>wN`gm)ZF1k zS=h&CiF$(15!Kv-vJKT?q2_r}DyqbUJZ^`qNXpv0q8={lf|4iqGpp57G^{+x7b@Sx zEYsEqpF7AG3>O#?*FrmJuAFTebx_z+WR|DH(wnoVvhUg^l=3Z_%S5WHfx!D==lTjmrkuV|^mb~{tE zf}j*-J+)tv>gz8YoSP3Q0x#W!as!P{(%>_~V4fHFOvBl9Q`Hou;n{RAk-Val#)c1a z+<99Vv=}+%5T7T8=oy>HC#HpCiXxf0{WCmH5Hhk*A&z`gCjxpoS^g|YY5cHjyP+q$mDw=uml9Svb?vG*L< zn=<+f>->PD>>0zz%d4g4#Zr8*!`|Q-ij&)yE#%~k zfx2Kr#caZ5<6lvm+*X-DkmIh|z*Re&3|Sytm4!2;plG5#TWXsmlI3+XmF=`)0(m}1 z%6Z-p@-|_tC}+xwvvQ`gYbuj-xU%e%%Ey%DS-K9Cs~g>rT63;^({C{_x!j^KXYK5k z7WT|l3_VKZ3{f{yyu`GyM6{x$Z!{+|1WbbNvh-KZNJNILZl_}uyfdYDZtiA2_yMQg~=)UHY0*oApf)+cuwtXFNUtw!P@YJpevvlN}H z%F|js!%f~z!8}^&*lm1rmy@7tlkNDYm$Sj#Dlf}Ks?+igr{&W-?lG8WH#KZ~&}RGe zj$OC5zHlq+-L&;-UL(fAn5qg6l&^#;wX?O#*k#+89-|O49;$T1jCze~5ak=Vj|y_! zo3`QoM9wH{c_QoDe}x>rfmx7KfvM^gH`U&h#-^TTrOtg&;(Og^9Ow)LO| zm8^(oYF_h{_G&3vR8xQDo5>yY;j5g(7E3v7j#dj))Mln=#jss*QBieURnx6T(V2jZ zhYwNCn%NF?2U)0#g-)2xl6RV@npkbJyrQ5@PnDbJTdx&WuC~>+Quf-k%~+=|Y_^*?B$a&W0L~hC^c;{w~;NgDYm42XqfT9>;D0UA3vjf2A2Y^=wfL{*)V}rnBgTU26VCxV? zF@DQawRxY;9#GfEd5XMVx`nR+^ykRgWpf`{==p_{2ls6NP2Z))D-Wc`<4wbtlum8t zkk@G`JAg*GE)3aPuF`{zMv;${j^^d7^8fUve0V)B#_S=BY8^HDQojVh0|RPc&S<6Aia18vgV#h=$nkooKi{(XcpNGa7uH z_uMe>=rHh;5@G~c908sh0j`Zu6*0wJ5p5X-W<~*Zl!Ep%KN$JJDDdJaz>NV%#(=Mk z0lydnV&jzRFf&SS95^)&T&AHT%qZ`T10Rk9pV$n1bu;ku%|Pn}urxubZe~V#W&(I_ z0(ggpPB4uC^VY@QKR5i=RfA%kx2{?j-p=P++jV!t4^?*Eb)Ih>H|h?CRL%SJhJO%H zn}xBc*CMI^8o%N9F=xtiYOj;>9Kbulb0>K21kat|x&6UIBtA`~Gu@RAES2W7Sw2fr V{EAN0l<`aBe*vfOW?z6T001)D6m0+i literal 1624 zcmV-e2B-N!RzVZt$XPfnowYzTG9xC-vkbnc6%8j;v!?fNucY2JjbvQ35t#c?7m6?G&WT0c)TXGZ8LSCQlnjGanD%_JVG}qq z2bjt&jaln(OcF9hfTlS{?K7r2Y49c2bkSo{RBk#JRUOxI(_=|+OI={LV^K%vrfbfJ z+UCe9zGN@2*~b zKcNayeux0KBEV=Dz`B4ByMRy>7>@$Sqk?->bdS9o1+GSc`%z%J8+g4NxX}&76kwYI zyr2jaLn1}wm;z7*xTF9-iSU8|_ghCKeZ+;@=1W>tH%rYb1(illaJO}{VlaoBO~6pg z+m94@vb*#~lsY$qMldp2H(kd==8{EgwRNe~$54rV5 zLmd|6Es}&3V(kvdF85m9RQJ#dvyRY3W-a&0a~@Q>g_@+gQMcGKtesamnR8|4hGHJ=Q$LR(^Vafa616m;B?@)HYGH>c|5Wkm#5oZ!BO;`~T~XX^y7 zmUWY9)8;ANad(;-H)BBqdV0>Gi@I4^=5vYc4R9t2v16<6fxaaNYmLoZoun!YyqOPp zeWm1OTdp3MTSi5E?FnvIxm}#~##t<>DzBSPaf%!3n-@OL?fbY&ja4W&Ft`dXpDTk& zs!}a*PY$(1;ep>~x^eyQImwwFh{z2bk#rPWA|swOfRT-|hh}_5e3UXij3z$APgppv8f! zaRHte;h|sSz&#N*EDi6lTk4&??Naw^tj2BKDVf@CX0Qr%mW4t+n0S>gvXw4!jc`9l5Zvfyk0yZ=A}lGvtR!$+gk_{c5I3whJYkU?9>e#m z8PX}gK_Rh&Dd3e9@J&ZG0i{0Ry*}VhA25{`qUn+DJ>@j8kOr=$fjem++z-t5 z1E2H*5Bh<_8G#}tQPeWPsSNOG26&JG(plhm7Wgs?C3Yv~rfS_A%(uQN!&`H9RCWj14~eYS=rtsfLH6h6|n5 zppfp52Z7H9f%}8N^bl}i2)Ho>Byz%#iA#qiH3#T9;C&I=D?+<}$pLqBz|K7IY99D1 z4@8E6y~BcQpR~%QVc^;@a9f0?rB${UfE@+EE&yK_fWHesX#{v}L~tFDR{3!R_;~~v z9~Gd3(rk?i)f$zrB7twr9Vnzy7g`LR?mqtSNjx{7z?W37?;78e_%2sIhGNI7RnR!b zIMaMi-#Gs%#70`%@bI$Oe+;y WVMRLcFstbPJo^td|1&qW9RL6d>i?nu diff --git a/packages/orchestration/test/examples/snapshots/unbond.contract.test.ts.md b/packages/orchestration/test/examples/snapshots/unbond.contract.test.ts.md index fd6e662260c..7533e219092 100644 --- a/packages/orchestration/test/examples/snapshots/unbond.contract.test.ts.md +++ b/packages/orchestration/test/examples/snapshots/unbond.contract.test.ts.md @@ -27,6 +27,7 @@ Generated by [AVA](https://avajs.dev). chainHub: { ChainHub_kindHandle: 'Alleged: kind', ChainHub_singleton: 'Alleged: ChainHub', + brandDenom: {}, chainInfos: { agoric: { chainId: 'agoric-3', @@ -37,12 +38,12 @@ Generated by [AVA](https://avajs.dev). }, ], }, - omniflixhub: { - chainId: 'omniflixhub-1', - icqEnabled: false, + osmosis: { + chainId: 'osmosis-1', + icqEnabled: true, stakingTokens: [ { - denom: 'uflix', + denom: 'uosmo', }, ], }, @@ -57,20 +58,20 @@ Generated by [AVA](https://avajs.dev). }, }, connectionInfos: { - 'agoric-3_omniflixhub-1': { - client_id: '07-tendermint-73', + 'agoric-3_osmosis-1': { + client_id: '07-tendermint-1', counterparty: { - client_id: '07-tendermint-47', - connection_id: 'connection-40', + client_id: '07-tendermint-2109', + connection_id: 'connection-1649', prefix: { key_prefix: 'FIXME', }, }, - id: 'connection-67', + id: 'connection-1', state: 3, transferChannel: { - channelId: 'channel-58', - counterPartyChannelId: 'channel-30', + channelId: 'channel-1', + counterPartyChannelId: 'channel-320', counterPartyPortId: 'transfer', ordering: 0, portId: 'transfer', diff --git a/packages/orchestration/test/examples/snapshots/unbond.contract.test.ts.snap b/packages/orchestration/test/examples/snapshots/unbond.contract.test.ts.snap index 26978e4b049fdfe5a9797712e619755a2f1735e8..4e6f357856ff7f0f355b5371515d22faad996890 100644 GIT binary patch literal 1950 zcmV;P2VwX@RzVzN|Of#9M+Efr4_QX^N<{ak9#iglv=a zf>4_t&z$uR9-nb%jIsf;6|9{T;ZytYVvu#>h*SUF{2}78SIfB~4a5(KdTb6Kc z-qv+0=;s@EqDW%+At{pnEPxFFw*b5W;2i)F0xS{W(*$^k0P`eKB8iLbFEULKGX7dD z7C~|#DUbr@Nu)xeAn_8}e|likw#k|sgSKfnTg`KZxY{+iewK3GWIimnYMQLW^aq+l znZhxDiD{OtGkXffJRzF|@WpBBT<6-FX>GfrmnBh3+oA?PJ7~L-_W}}&3!7@fm3$&H zk>o5}+hUHesW2?=in%0?U9+|wcl8I-YV?N1hR2(|69Ik~0jg2p@hI?(DDYMkScw5o z#(-yIz^`J!J27A`t{inFc+@G9ITZ)a#(|r0;1_Y=tvK+{I8aLfhY~<90sJ(f&?Hov z*n0^ek_48Mz~e~;zNEs_&nAKIC4qO7zbNZtxz0LDUx4E1M~y< zchkT>(!j9{;ADUwW)#(rsH%}aWPm@Zu#bkKa88M!#;V>k_^KZZvj*UTqV5>HV=`fJ zA5b4_2ayU-<&-h>(y;if9xSa5xU2Xu-?nMLKUFRS81{u3Lfcpz6NJEON}jhmmxX1s zS>0${7F4iVc9AD!|J)>-DqcnpxORqinEfd2GTY0!2P9N!j9R0HIk1@rc}#|En%4H3 zWnUZ!&FZm7LbsJ3xL#hMeSf7oc5R!{?giR+kHC$%w0c0TQFWR1fXZXV(DjtYdM&wh zU6VGIY1@?Rrx~|;uI!d9a|_-&-?Y5Lctw&GG>daDG$(++N`ScTaRz){G%c$;=(l{{ zS@(KS(sx$5-WoeGE7O4yQ(zZUkmN8FdY03?DErx5)ii6sMf0p>PS+~~oF6}LY1I4>$~`pgeJ-DSwQE$P`plK0 zK5@mGc{fX!SkDq{HuHHxj!9vVcJt+Pw8vc06Ot%pw6!aiWqNt?RY@|}r&^bZkuV36 zP&~WG?GTrku#Mqdw3l|Yv;##bn)U@-QUI;iE84i1t3T(ygV2m&)Ig6&zt3y4T(#uJkoYTK4D;9lJWxKXk;!M2hkh;$u9jA;gQx zJRv6~U}eDD7S~s~e%^R$VCa_x?T!rx;@9`qe_kGkYpCLl+K56h+SM%j-f;%|?fR*f zpG21#*O|SS7GbotOUxOVVy~4aL=Y;2jj_Ip-LX70HYVkJx*erR?8zdqSp>dT1b$Rh z0yiB>hhGgzu;vKbHF)QW)n(k7%34~=*xk()TytOk-^`^Z$v@5Jp5NVCT>X`znFizH zsxh`#IDF!eaC5R}a4{-NcO=%VJJd0|Pi=8sp^Hp-vXkEgA>ZT9@MoO+6dTpr_azGj zF{%x%Ey_7Fr}VrS^!&teuZUM>c0DJ#e&79f_q|uSi|=ZksL&bSC8S8?g$nSK3Iel4 zCOAuw$j>XlFBMQu1-)JY-c&$E1r+;Z1$esx)T+R_s*;`+HH%YP6)>v6tt#+F6+jI* zPy^O#fL2q~YoQADObz&Y4S1yn{7Z!|gsc_nK(!96)`1&!;Ke%ddL8(C9Vjj+>We}3 zjC&Pxgq&D#Us{fzpHQB^*F|AI_xVHg(Ooin`uyYOIAr)2%!9)YNp~9*m z*zFIDeqV((LX(MF*uD2${!?twHuWiR@xgGUmmtDW=1>qRIw*J_KGqa(Y kN!!ePdddF}quHjN4(+fe-4x7jy8i?H7v#T(J02PU0MlK#@Bjb+ literal 991 zcmV<510eiCRzVRy~g!MHqf&cWv+ba$mYT!X=mB6o5d2h6d3f=Yn-nE?jh% zTm(Xj#F}K1>&J#do(5B?I8);LD zq*J>l?47Pq@g7yNd2HI&&5`8kIyYr{G`v4&cqRR%}i6b?SqgCtvhH@E74uK6&4XO2DK6A$>>1OD`Y z3qEki2fp`#zkT4<0Js(aDgYh^PC&icfQBUaB>;X4fM)^lM#-U=q%M`hsmGVo6ss8$?_sIcSf72vH3AS=LkE`8ad&!Q`ZdRTIDxm}NYLeh zcFYr@+LBwX#)PvI>sh9wvw2wzy%bt4>cLJURhi)}H88Z_pQ|co6wVhF3dcA%5h#$| z=GfQmBdZNx)Q#;UOD$jI6f1;0txPL^bJSa@Vjs{pH@9h*n^9l*;gq#LVKu0bso^8a zr97o2^`N26L28-q@#8|aqBQgJI_-~FwkEC(IPG4i{VW7o#rc{3`{9fGc}DxC{%|m{ z24#HV!L*gPE#z=>Vw$66E{2ZI+q*jH>7;g$_p?@KOsC4$_OzUD_RVO%xvv?OFOWRN z5I&#j?RKb84a#V|aJ`+Kx}q08&CVh3Y0DSeeua=$t_&hJwEmFxcm_SpL7`}}n_A0J zZT^@;mim-+xjg|#2@k zUaLCvrGf*d3fyyPX9^b*$x{47H8;mn)zm7kt9W1BO+|cUX?HYAJSW(dtW8BS{(or_ z-a>hv^t>ZbPOK3zU&Q`7CHKnJ%eyxXRY{ARBd%g@PSek#$qspvN_*PMEeI4Q;P%Ac z<&IO~w9>TK<~vbHyg#bI6F2SQ^U^;10HAX}LAaz%F7NG(-;1>-ZMSKg*J#UfQ_Eh} N{{dGF4^C_g000tN<{AJ1 diff --git a/packages/orchestration/test/staking-ops.test.ts b/packages/orchestration/test/staking-ops.test.ts index d70287b1874..fb659dd5ed2 100644 --- a/packages/orchestration/test/staking-ops.test.ts +++ b/packages/orchestration/test/staking-ops.test.ts @@ -256,7 +256,7 @@ test('makeAccount() writes to storage', async t => { zone, } = s; const make = prepareCosmosOrchestrationAccountKit(zone, { - chainHub: makeChainHub(agoricNames, vowTools), + chainHub: makeChainHub(zone.subZone('chainHub'), agoricNames, vowTools), makeRecorderKit, timerService: timer, vowTools, @@ -304,7 +304,7 @@ test('withdrawRewards() on StakingAccountHolder formats message correctly', asyn zone, } = s; const make = prepareCosmosOrchestrationAccountKit(zone, { - chainHub: makeChainHub(agoricNames, vowTools), + chainHub: makeChainHub(zone.subZone('chainHub'), agoricNames, vowTools), makeRecorderKit, timerService: timer, vowTools, @@ -349,7 +349,7 @@ test(`delegate; redelegate using invitationMakers`, async t => { zone, } = s; const makeAccountKit = prepareCosmosOrchestrationAccountKit(zone, { - chainHub: makeChainHub(agoricNames, vowTools), + chainHub: makeChainHub(zone.subZone('chainHub'), agoricNames, vowTools), makeRecorderKit, timerService: timer, vowTools, @@ -439,7 +439,7 @@ test(`withdraw rewards using invitationMakers`, async t => { zone, } = s; const makeAccountKit = prepareCosmosOrchestrationAccountKit(zone, { - chainHub: makeChainHub(agoricNames, vowTools), + chainHub: makeChainHub(zone.subZone('chainHub'), agoricNames, vowTools), makeRecorderKit, timerService: timer, vowTools, @@ -487,7 +487,7 @@ test(`undelegate waits for unbonding period`, async t => { zone, } = s; const makeAccountKit = prepareCosmosOrchestrationAccountKit(zone, { - chainHub: makeChainHub(agoricNames, vowTools), + chainHub: makeChainHub(zone.subZone('chainHub'), agoricNames, vowTools), makeRecorderKit, timerService: timer, vowTools, diff --git a/packages/orchestration/test/supports.ts b/packages/orchestration/test/supports.ts index e5832ebe2a5..da33a5cddfa 100644 --- a/packages/orchestration/test/supports.ts +++ b/packages/orchestration/test/supports.ts @@ -149,7 +149,11 @@ export const commonSetup = async (t: ExecutionContext) => { await eventLoopIteration(); }; - const chainHub = makeChainHub(agoricNames, vowTools); + const chainHub = makeChainHub( + rootZone.subZone('chainHub'), + agoricNames, + vowTools, + ); /** * Register BLD if it's not already registered. diff --git a/packages/vow/src/retriable.js b/packages/vow/src/retriable.js index eaa110ff292..b49f2109784 100644 --- a/packages/vow/src/retriable.js +++ b/packages/vow/src/retriable.js @@ -38,9 +38,9 @@ const AdminRetriableFlowI = M.interface('RetriableFlowAdmin', { /** * @param {Zone} outerZone - * @param {PreparationOptions} [outerOptions] + * @param {PreparationOptions} outerOptions */ -export const prepareRetriableTools = (outerZone, outerOptions = {}) => { +export const prepareRetriableTools = (outerZone, outerOptions) => { const { makeVowKit, isRetryableReason } = outerOptions; /** diff --git a/packages/zoe/test/unitTests/contracts/snapshots/valueVow.test.js.md b/packages/zoe/test/unitTests/contracts/snapshots/valueVow.test.js.md index 0d19048e384..40361190c32 100644 --- a/packages/zoe/test/unitTests/contracts/snapshots/valueVow.test.js.md +++ b/packages/zoe/test/unitTests/contracts/snapshots/valueVow.test.js.md @@ -9,11 +9,14 @@ Generated by [AVA](https://avajs.dev). > contract baggage after start { + AdminRetriableFlow_kindHandle: 'Alleged: kind', + AdminRetriableFlow_singleton: 'Alleged: AdminRetriableFlow', PromiseWatcher_kindHandle: 'Alleged: kind', VowInternalsKit_kindHandle: 'Alleged: kind', WatchUtils_kindHandle: 'Alleged: kind', publicFacet_kindHandle: 'Alleged: kind', publicFacet_singleton: 'Alleged: publicFacet', + retriableFlowForOutcomeVow: {}, vowResolver: { resolver: Object @Alleged: VowInternalsKit resolver {}, vow: Object @Vow { diff --git a/packages/zoe/test/unitTests/contracts/snapshots/valueVow.test.js.snap b/packages/zoe/test/unitTests/contracts/snapshots/valueVow.test.js.snap index 756b545154ffce69e085fbb02d6bd01a36320434..171dc6777c11e354760e4ab5645af8186182ab79 100644 GIT binary patch literal 510 zcmVOC=^o=5}Cb;_qOG75)um(-_x=AvamN@BO~-JwM&~B+@26cBdEYmDij` zGEHU5%4KryrpCL|izJa=z82pFx+o6y=*BYuF93W3@C(3W0wx5U6Yzt8?Ep9kfDP&l zsB;*-<=7KdpJ5n+)9D%G2=eb zSxtL$Ve%t(MlaZcdRO-*xVkqz5?=z~YXE#}u9#@8SWtTXta?S&9WPZK$9K@Lfc!TS)n zZR}LH!XJwW00000000A(lg)0^Koo_~9g`mkCD=+@sR$wS2CP`HK!OEI)ddw&Q?(ly z+c%Es;E6m_o9voZp8~1Pg16|ptauLgtSeP9I2cDPGOLm9J>NN_qq&|0N{44=v1FTA z#c?3xSjL=~RL;y)TeDb3k+kwe{BqF2>QI+DZvcz{TmrZSAP9Ixzy$#VYJ1dv7kuK- z60NVU>wbJrTj}oXJWxsaR))O2ZCz`UG*)bNT1|&*X>ASEC;Cc2 zwe>L9#d|hd<(#cralRAqgMgpS7Q{viqNLr`Xx(g$>Jz;Xe!M0TrSH7v+nLF^(lRO$R|5FfM6@>E^_y~2qqK5ejGu4h tZ+rCgzxhjv>$ Date: Tue, 17 Sep 2024 13:35:49 -0700 Subject: [PATCH 7/9] feat(examples): logging progress --- .../src/examples/send-anywhere.contract.js | 11 ++++- .../src/examples/send-anywhere.flows.js | 10 +++-- .../snapshots/send-anywhere.test.ts.md | 42 +++++++++++------- .../snapshots/send-anywhere.test.ts.snap | Bin 1288 -> 1309 bytes 4 files changed, 44 insertions(+), 19 deletions(-) diff --git a/packages/orchestration/src/examples/send-anywhere.contract.js b/packages/orchestration/src/examples/send-anywhere.contract.js index ebe77f1af7d..b05bab026a9 100644 --- a/packages/orchestration/src/examples/send-anywhere.contract.js +++ b/packages/orchestration/src/examples/send-anywhere.contract.js @@ -1,12 +1,15 @@ import { makeSharedStateRecord } from '@agoric/async-flow'; + import { AmountShape } from '@agoric/ertp'; import { InvitationShape } from '@agoric/zoe/src/typeGuards.js'; import { M } from '@endo/patterns'; +import { E } from '@endo/far'; import { withOrchestration } from '../utils/start-helper.js'; import * as flows from './send-anywhere.flows.js'; import { prepareChainHubAdmin } from '../exos/chain-hub-admin.js'; /** + * @import {Vow} from '@agoric/vow'; * @import {Zone} from '@agoric/zone'; * @import {OrchestrationPowers, OrchestrationTools} from '../utils/start-helper.js'; */ @@ -33,7 +36,7 @@ const contract = async ( zcf, privateArgs, zone, - { chainHub, orchestrateAll, zoeTools }, + { chainHub, orchestrateAll, vowTools, zoeTools }, ) => { const contractState = makeSharedStateRecord( /** @type {{ account: OrchestrationAccount | undefined }} */ { @@ -43,10 +46,16 @@ const contract = async ( const creatorFacet = prepareChainHubAdmin(zone, chainHub); + // UNTIL https://github.com/Agoric/agoric-sdk/issues/9066 + const logNode = E(privateArgs.storageNode).makeChildNode('log'); + /** @type {(msg: string) => Vow} */ + const log = msg => vowTools.watch(E(logNode).setValue(msg)); + // orchestrate uses the names on orchestrationFns to do a "prepare" of the associated behavior const orchFns = orchestrateAll(flows, { zcf, contractState, + log, localTransfer: zoeTools.localTransfer, }); diff --git a/packages/orchestration/src/examples/send-anywhere.flows.js b/packages/orchestration/src/examples/send-anywhere.flows.js index 6c7128df268..191109d025b 100644 --- a/packages/orchestration/src/examples/send-anywhere.flows.js +++ b/packages/orchestration/src/examples/send-anywhere.flows.js @@ -3,6 +3,7 @@ import { M, mustMatch } from '@endo/patterns'; /** * @import {GuestOf} from '@agoric/async-flow'; + * @import {Vow} from '@agoric/vow'; * @import {ZoeTools} from '../utils/zoe-tools.js'; * @import {Orchestrator, LocalAccountMethods, OrchestrationAccountI, OrchestrationFlow} from '../types.js'; */ @@ -18,12 +19,13 @@ const { entries } = Object; * @param {object} ctx * @param {{ localAccount?: OrchestrationAccountI & LocalAccountMethods }} ctx.contractState * @param {GuestOf} ctx.localTransfer + * @param {GuestOf<(msg: string) => Vow>} ctx.log * @param {ZCFSeat} seat * @param {{ chainName: string; destAddr: string }} offerArgs */ export const sendIt = async ( orch, - { contractState, localTransfer }, + { contractState, localTransfer, log }, seat, offerArgs, ) => { @@ -32,8 +34,10 @@ export const sendIt = async ( // NOTE the proposal shape ensures that the `give` is a single asset const { give } = seat.getProposal(); const [[_kw, amt]] = entries(give); + void log(`sending {${amt.value}} from ${chainName} to ${destAddr}`); const agoric = await orch.getChain('agoric'); const assets = await agoric.getVBankAssetInfo(); + void log(`got info for denoms: ${assets.map(a => a.denom).join(', ')}`); const { denom } = NonNullish( assets.find(a => a.brand === amt.brand), `${amt.brand} not registered in vbank`, @@ -41,8 +45,7 @@ export const sendIt = async ( const chain = await orch.getChain(chainName); if (!contractState.localAccount) { - const agoricChain = await orch.getChain('agoric'); - contractState.localAccount = await agoricChain.makeAccount(); + contractState.localAccount = await agoric.makeAccount(); } const info = await chain.getChainInfo(); @@ -65,5 +68,6 @@ export const sendIt = async ( }, ); seat.exit(); + void log(`transfer complete, seat exited`); }; harden(sendIt); diff --git a/packages/orchestration/test/examples/snapshots/send-anywhere.test.ts.md b/packages/orchestration/test/examples/snapshots/send-anywhere.test.ts.md index 9cc05e0959f..874ee76b50c 100644 --- a/packages/orchestration/test/examples/snapshots/send-anywhere.test.ts.md +++ b/packages/orchestration/test/examples/snapshots/send-anywhere.test.ts.md @@ -1,6 +1,6 @@ -# Snapshot report for `test/examples/sendAnywhere.test.ts` +# Snapshot report for `test/examples/send-anywhere.test.ts` -The actual snapshot is saved in `sendAnywhere.test.ts.snap`. +The actual snapshot is saved in `send-anywhere.test.ts.snap`. Generated by [AVA](https://avajs.dev). @@ -27,8 +27,10 @@ Generated by [AVA](https://avajs.dev). chainHub: { ChainHub_kindHandle: 'Alleged: kind', ChainHub_singleton: 'Alleged: ChainHub', + brandDenom: {}, chainInfos: {}, connectionInfos: {}, + denom: {}, lookupChainInfo_kindHandle: 'Alleged: kind', lookupChainsAndConnection_kindHandle: 'Alleged: kind', lookupConnectionInfo_kindHandle: 'Alleged: kind', @@ -38,7 +40,21 @@ Generated by [AVA](https://avajs.dev). 'ChainHub Admin_singleton': 'Alleged: ChainHub Admin', 'Send PF_kindHandle': 'Alleged: kind', 'Send PF_singleton': 'Alleged: Send PF', - findBrandInVBank_kindHandle: 'Alleged: kind', + orchestration: { + sendIt: { + asyncFlow_kindHandle: 'Alleged: kind', + endowments: { + 0: { + contractState_kindHandle: 'Alleged: kind', + contractState_singleton: 'Alleged: contractState', + localTransfer_kindHandle: 'Alleged: kind', + localTransfer_singleton: 'Alleged: localTransfer', + log_kindHandle: 'Alleged: kind', + log_singleton: 'Alleged: log', + }, + }, + }, + }, }, orchestration: { 'Cosmos Orchestration Account Holder_kindHandle': 'Alleged: kind', @@ -46,18 +62,14 @@ Generated by [AVA](https://avajs.dev). LocalChainFacade_kindHandle: 'Alleged: kind', Orchestrator_kindHandle: 'Alleged: kind', RemoteChainFacade_kindHandle: 'Alleged: kind', - sendIt: { - asyncFlow_kindHandle: 'Alleged: kind', - endowments: { - 1: { - contractState_kindHandle: 'Alleged: kind', - contractState_singleton: 'Alleged: contractState', - findBrandInVBank_kindHandle: 'Alleged: kind', - findBrandInVBank_singleton: 'Alleged: findBrandInVBank', - localTransfer_kindHandle: 'Alleged: kind', - localTransfer_singleton: 'Alleged: localTransfer', - }, - }, + chainName: {}, + ibcTools: { + IBCTransferSenderKit_kindHandle: 'Alleged: kind', + ibcResultWatcher_kindHandle: 'Alleged: kind', + ibcResultWatcher_singleton: 'Alleged: ibcResultWatcher', + }, + packetTools: { + PacketToolsKit_kindHandle: 'Alleged: kind', }, }, vows: { diff --git a/packages/orchestration/test/examples/snapshots/send-anywhere.test.ts.snap b/packages/orchestration/test/examples/snapshots/send-anywhere.test.ts.snap index db5e4fcd09358ecf830457f606c586941142ccf2..e29cb8eb0e236ac0f9b1c6e76002f7fa3332e0c7 100644 GIT binary patch literal 1309 zcmV+&1>*WaRzVmgOD0om5@h0u1h$j#BCgQ<^V8MfjLZMJl@Zdo&f}mh_HnThP zCRujkUUr$^|Nndc@Asd*z1?tywd>!y!=xi!)@;yblQx;-bK3TIg!FISv27~p>$Qh@ zl+pbVjp%CtHUNAA;68xg0OSa;K!BGC@EQRoNoj(V)*EjzOA^xmS*z7R3=j>(#3ad0 zkvvFof}FY#c(mcN`bN-j9e=0(sw3arb-2Akx$Uw@%vf|?)@1fe^)9AxOl~qucsBD! zP^=QNO@Ij2sehAO%dY5z(AP0Yvv#P%SAs@}{F6Wgmmalbh`g8^AcpX)9p+1qN=NVz zz7c~9H$=w|pMDj4ntxZYZg|HZ^RvqDsi^ zu|Zc&^*TG?)+@Bhyz6wAdA;8ICMGq}ms+Nd8+fcI@-Qy4?TXH_@YVxqi59z-%9b94 zR$ij*=*e{7v<{=YmuNc-L0ECR8la{9DU&ro$NGw?^i1^imf|Dq@0e4ShDW)5fpO6a zv6=B077o5uzAbveI2XfAOK{#R&4IvHOrU7@I1{$cxkBs)?WKq}tFKSR^!-I{FZGSc z%JjrOQ^Jc8NDPpoTA@*Jd%c`_Rw(L2W<^p`t%EJb?fS-Y*7GMKz0|?AzS_P>nB=#| z55MLPxYwUw5`Ihg_4T-9ebKT+z-4_!xT9Uy3C`0Og+<*%D37o=%!hnobeE|`?aV`J zW?+iQ?9IX^YYEA+9bYBnvsfbZuvIV977L+!F-UV7))gV#UW@)3L&n^ha!PWH-~yCyoTTr!VS*FQp~ zOo!)Qm5%F=mZWiG5brrJ?wE3u7$8KQ3~sk9uXvRE+atNE_12u?qUX_@Bp>}UO|y?h zD^Nk%l#*z1C|VwhYC}J65tfB00<3mX%RFcAq0bJfA`Pcw{R>_W~&a78~ zr>lzCG3n#`pQ=DxEKxnJM^8>CLR TCG+aOq@Mf_P3!k0D-i$y6kdoU literal 1288 zcmV+j1^4idF^bwLi z!~s>~-HEegJ!5vqHAP%{MB)GfA#SMU&@%^mLx=+hBr0*>fK;hcC6GYkzzGQnB;s|} z-knV>Z^$Kk2RU|a=nKs8dICh7QVy$ zF$^n&Y!e`Y4I13y_KGLEA@o%Y((N7U^3_fwME*%2f=i#;GDKd=4G~lL_6`fAPo*n( z2;Ypsg`1)qgx`J{`!@fsV7>58{+t6I<$x1;faQVD^T6Xg@InFDDFF8hz=HztX91Yf zlu+lAq1H)xT>~~W;C&7FP6Hlmz`q)BvIv|l0-Yl8O;M34sxrkw2`HCcE znHYA}bicDZ+{16;2WI<}Fv)LE z9{$W7@UB0X+lp`jTyn4wv;+;f?oPCp1rA5H|G=kvziQun_8n(Osc7butgB z*`X^U^KBM4SxZQk?f42IpT!bkgspmswpa+=k3qWIu&)T=^;`7U7&6hO_AZnApb1h` zJUbEA*(Q^|+gr_!@)9l{AdxDxh2(3PV5`lx;ft0Vu3F&omsB{ z&r}rJ)W|buQX3@+)6Xiy^cMrhoi!PvdG$G3JGUGqx;Jc@f0QxiHtbCnbUb;~?nnuw zT6Jz~=n0Y0q&KdLz}Q&G;T3p35IvR4TCBbJ{sDg;HEPz?S7Z&il1)QoXT2@M{piY4356o~0&ipDRty8%? yJ}^!Y|L+t^`jE69;X{JRJlbI1JByKH_5FWL)akZlem#)Xm;V86r0Pq(5C8yUduA5^ From 11879f7096a31b9ef0bc1e02af15b683d469788a Mon Sep 17 00:00:00 2001 From: Turadg Aleahmad Date: Tue, 16 Jul 2024 14:16:02 -0700 Subject: [PATCH 8/9] test: upgrade from buggy sendAnywhere --- .../orchestration/contract-upgrade.test.ts | 78 ++++++++++ .../scripts/testing/fix-buggy-sendAnywhere.js | 134 +++++++++++++++++ .../testing/start-buggy-sendAnywhere.js | 141 ++++++++++++++++++ .../src/examples/send-anywhere.contract.js | 3 +- 4 files changed, 355 insertions(+), 1 deletion(-) create mode 100644 packages/boot/test/orchestration/contract-upgrade.test.ts create mode 100644 packages/builders/scripts/testing/fix-buggy-sendAnywhere.js create mode 100644 packages/builders/scripts/testing/start-buggy-sendAnywhere.js diff --git a/packages/boot/test/orchestration/contract-upgrade.test.ts b/packages/boot/test/orchestration/contract-upgrade.test.ts new file mode 100644 index 00000000000..514652175ee --- /dev/null +++ b/packages/boot/test/orchestration/contract-upgrade.test.ts @@ -0,0 +1,78 @@ +/** @file Bootstrap test of restarting contracts using orchestration */ +import { test as anyTest } from '@agoric/zoe/tools/prepare-test-env-ava.js'; +import { TestFn } from 'ava'; + +import { + makeWalletFactoryContext, + type WalletFactoryTestContext, +} from '../bootstrapTests/walletFactory.js'; + +const test: TestFn = anyTest; +test.before(async t => { + t.context = await makeWalletFactoryContext( + t, + '@agoric/vm-config/decentral-itest-orchestration-config.json', + ); +}); +test.after.always(t => t.context.shutdown?.()); + +/** + * This test core-evals a buggy installation of the sendAnywhere contract by + * giving it a faulty `agoricNames` service with a lookup() function returns a + * promise that never resolves. + * + * Because the send-anywhere flow requires a lookup(), it waits forever. This + * gives us a point at which we can upgrade the vat with a working agoricNames + * and see that the flow continues from that point. + */ +test('resume', async t => { + const { walletFactoryDriver, buildProposal, evalProposal, storage } = + t.context; + + const { IST } = t.context.agoricNamesRemotes.brand; + + t.log('start sendAnywhere'); + await evalProposal( + buildProposal( + '@agoric/builders/scripts/testing/start-buggy-sendAnywhere.js', + ), + ); + + t.log('making offer'); + const wallet = await walletFactoryDriver.provideSmartWallet('agoric1test'); + // no money in wallet to actually send + const zero = { brand: IST, value: 0n }; + // send because it won't resolve + await wallet.sendOffer({ + id: 'send-somewhere', + invitationSpec: { + source: 'agoricContract', + instancePath: ['sendAnywhere'], + callPipe: [['makeSendInvitation']], + }, + proposal: { + // @ts-expect-error XXX BoardRemote + give: { Send: zero }, + }, + offerArgs: { destAddr: 'cosmos1whatever', chainName: 'cosmoshub' }, + }); + + // XXX golden test + const getLogged = () => + JSON.parse(storage.data.get('published.sendAnywhere.log')!).values; + + // This log shows the flow started, but didn't get past the name lookup + t.deepEqual(getLogged(), ['sending {0} from cosmoshub to cosmos1whatever']); + + t.log('upgrade sendAnywhere with fix'); + await evalProposal( + buildProposal('@agoric/builders/scripts/testing/fix-buggy-sendAnywhere.js'), + ); + + t.deepEqual(getLogged(), [ + 'sending {0} from cosmoshub to cosmos1whatever', + // XXX this denom list may be wrong + 'got info for denoms: ubld, uist', + 'transfer complete, seat exited', + ]); +}); diff --git a/packages/builders/scripts/testing/fix-buggy-sendAnywhere.js b/packages/builders/scripts/testing/fix-buggy-sendAnywhere.js new file mode 100644 index 00000000000..a6024d8a8bb --- /dev/null +++ b/packages/builders/scripts/testing/fix-buggy-sendAnywhere.js @@ -0,0 +1,134 @@ +/** + * @file This is for use in tests in a3p-integration + * Unlike most builder scripts, this one includes the proposal exports as well. + */ +import { + deeplyFulfilledObject, + makeTracer, + NonNullish, +} from '@agoric/internal'; +import { E } from '@endo/far'; + +/// +/** + * @import {Installation, Instance} from '@agoric/zoe/src/zoeService/utils.js'; + */ + +const trace = makeTracer('StartBuggySA', true); + +/** + * @import {start as StartFn} from '@agoric/orchestration/src/examples/send-anywhere.contract.js'; + */ + +/** + * @param {BootstrapPowers & { + * instance: { + * consume: { + * sendAnywhere: Instance; + * }; + * }; + * }} powers + * @param {...any} rest + */ +export const fixSendAnywhere = async ( + { + consume: { + agoricNames, + board, + chainStorage, + chainTimerService, + contractKits, + cosmosInterchainService, + localchain, + }, + instance: instances, + }, + { options: { sendAnywhereRef } }, +) => { + trace(fixSendAnywhere.name); + + const saInstance = await instances.consume.sendAnywhere; + trace('saInstance', saInstance); + const saKit = await E(contractKits).get(saInstance); + + const marshaller = await E(board).getReadonlyMarshaller(); + + const privateArgs = await deeplyFulfilledObject( + harden({ + agoricNames, + localchain, + marshaller, + orchestrationService: cosmosInterchainService, + storageNode: E(NonNullish(await chainStorage)).makeChildNode( + 'sendAnywhere', + ), + timerService: chainTimerService, + }), + ); + + trace('upgrading...'); + await E(saKit.adminFacet).upgradeContract( + sendAnywhereRef.bundleID, + privateArgs, + ); + + trace('done'); +}; +harden(fixSendAnywhere); + +export const getManifestForValueVow = ({ restoreRef }, { sendAnywhereRef }) => { + console.log('sendAnywhereRef', sendAnywhereRef); + return { + manifest: { + [fixSendAnywhere.name]: { + consume: { + agoricNames: true, + board: true, + chainStorage: true, + chainTimerService: true, + cosmosInterchainService: true, + localchain: true, + + contractKits: true, + }, + installation: { + consume: { sendAnywhere: true }, + }, + instance: { + consume: { sendAnywhere: true }, + }, + }, + }, + installations: { + sendAnywhere: restoreRef(sendAnywhereRef), + }, + options: { + sendAnywhereRef, + }, + }; +}; + +/** @type {import('@agoric/deploy-script-support/src/externalTypes.js').CoreEvalBuilder} */ +export const defaultProposalBuilder = async ({ publishRef, install }) => + harden({ + // Somewhat unorthodox, source the exports from this builder module + sourceSpec: '@agoric/builders/scripts/testing/fix-buggy-sendAnywhere.js', + getManifestCall: [ + 'getManifestForValueVow', + { + sendAnywhereRef: publishRef( + install( + '@agoric/orchestration/src/examples/send-anywhere.contract.js', + ), + ), + }, + ], + }); + +export default async (homeP, endowments) => { + // import dynamically so the module can work in CoreEval environment + const dspModule = await import('@agoric/deploy-script-support'); + const { makeHelpers } = dspModule; + const { writeCoreEval } = await makeHelpers(homeP, endowments); + await writeCoreEval(fixSendAnywhere.name, defaultProposalBuilder); +}; diff --git a/packages/builders/scripts/testing/start-buggy-sendAnywhere.js b/packages/builders/scripts/testing/start-buggy-sendAnywhere.js new file mode 100644 index 00000000000..e726e3bf9eb --- /dev/null +++ b/packages/builders/scripts/testing/start-buggy-sendAnywhere.js @@ -0,0 +1,141 @@ +/** + * @file This is for use in tests in a3p-integration + * Unlike most builder scripts, this one includes the proposal exports as well. + */ +import { + deeplyFulfilledObject, + makeTracer, + NonNullish, +} from '@agoric/internal'; +import { E, Far } from '@endo/far'; + +/// +/** + * @import {Installation} from '@agoric/zoe/src/zoeService/utils.js'; + */ + +const trace = makeTracer('StartBuggySA', true); + +/** + * @import {start as StartFn} from '@agoric/orchestration/src/examples/send-anywhere.contract.js'; + */ + +/** + * @param {BootstrapPowers & { + * installation: { + * consume: { + * sendAnywhere: Installation; + * }; + * }; + * }} powers + */ +export const startSendAnywhere = async ({ + consume: { + agoricNames, + board, + chainStorage, + chainTimerService, + cosmosInterchainService, + localchain, + startUpgradable, + }, + installation: { + consume: { sendAnywhere }, + }, + instance: { + // @ts-expect-error unknown instance + produce: { sendAnywhere: produceInstance }, + }, +}) => { + trace(startSendAnywhere.name); + + const marshaller = await E(board).getReadonlyMarshaller(); + + const privateArgs = await deeplyFulfilledObject( + harden({ + agoricNames, + localchain, + marshaller, + orchestrationService: cosmosInterchainService, + storageNode: E(NonNullish(await chainStorage)).makeChildNode( + 'sendAnywhere', + ), + timerService: chainTimerService, + }), + ); + + const agoricNamesHangs = Far('agoricNames that hangs', { + // ...privateArgs.agoricNames, + lookup: async () => { + trace('agoricNames.lookup being called that will never resolve'); + // BUG: this never resolves + return new Promise(() => {}); + }, + }); + + const { instance } = await E(startUpgradable)({ + label: 'sendAnywhere', + installation: sendAnywhere, + privateArgs: { + ...privateArgs, + agoricNames: agoricNamesHangs, + }, + }); + produceInstance.resolve(instance); + trace('done'); +}; +harden(startSendAnywhere); + +export const getManifestForValueVow = ({ restoreRef }, { sendAnywhereRef }) => { + trace('sendAnywhereRef', sendAnywhereRef); + return { + manifest: { + [startSendAnywhere.name]: { + consume: { + agoricNames: true, + board: true, + chainStorage: true, + chainTimerService: true, + cosmosInterchainService: true, + localchain: true, + + startUpgradable: true, + }, + installation: { + consume: { sendAnywhere: true }, + }, + instance: { + produce: { sendAnywhere: true }, + }, + }, + }, + installations: { + sendAnywhere: restoreRef(sendAnywhereRef), + }, + }; +}; + +/** @type {import('@agoric/deploy-script-support/src/externalTypes.js').CoreEvalBuilder} */ +export const defaultProposalBuilder = async ({ publishRef, install }) => + harden({ + // Somewhat unorthodox, source the exports from this builder module + sourceSpec: '@agoric/builders/scripts/testing/start-buggy-sendAnywhere.js', + getManifestCall: [ + 'getManifestForValueVow', + { + sendAnywhereRef: publishRef( + install( + '@agoric/orchestration/src/examples/send-anywhere.contract.js', + ), + ), + }, + ], + }); + +export default async (homeP, endowments) => { + // import dynamically so the module can work in CoreEval environment + const dspModule = await import('@agoric/deploy-script-support'); + const { makeHelpers } = dspModule; + const { writeCoreEval } = await makeHelpers(homeP, endowments); + await writeCoreEval(startSendAnywhere.name, defaultProposalBuilder); +}; diff --git a/packages/orchestration/src/examples/send-anywhere.contract.js b/packages/orchestration/src/examples/send-anywhere.contract.js index b05bab026a9..40c5e434fc0 100644 --- a/packages/orchestration/src/examples/send-anywhere.contract.js +++ b/packages/orchestration/src/examples/send-anywhere.contract.js @@ -32,7 +32,7 @@ harden(SingleAmountRecord); * @param {Zone} zone * @param {OrchestrationTools} tools */ -const contract = async ( +export const contract = async ( zcf, privateArgs, zone, @@ -78,6 +78,7 @@ const contract = async ( return { publicFacet, creatorFacet }; }; +harden(contract); export const start = withOrchestration(contract); harden(start); From 78fb22ddbafbf8abfb53ef6bc776c14f52171433 Mon Sep 17 00:00:00 2001 From: Turadg Aleahmad Date: Tue, 17 Sep 2024 14:26:55 -0700 Subject: [PATCH 9/9] fixup! test: upgrade from buggy sendAnywhere --- .../scripts/testing/fix-buggy-sendAnywhere.js | 14 +++++++++++--- .../scripts/testing/start-buggy-sendAnywhere.js | 1 - 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/packages/builders/scripts/testing/fix-buggy-sendAnywhere.js b/packages/builders/scripts/testing/fix-buggy-sendAnywhere.js index a6024d8a8bb..fdb31f11d44 100644 --- a/packages/builders/scripts/testing/fix-buggy-sendAnywhere.js +++ b/packages/builders/scripts/testing/fix-buggy-sendAnywhere.js @@ -7,14 +7,14 @@ import { makeTracer, NonNullish, } from '@agoric/internal'; -import { E } from '@endo/far'; +import { E, Far } from '@endo/far'; /// /** * @import {Installation, Instance} from '@agoric/zoe/src/zoeService/utils.js'; */ -const trace = makeTracer('StartBuggySA', true); +const trace = makeTracer('FixBuggySA', true); /** * @import {start as StartFn} from '@agoric/orchestration/src/examples/send-anywhere.contract.js'; @@ -53,9 +53,17 @@ export const fixSendAnywhere = async ( const marshaller = await E(board).getReadonlyMarshaller(); + // This apparently pointless wrapper is to maintain structural parity + // with the buggy core-eval's wrapper to make lookup() hang. + const agoricNamesResolves = Far('agoricNames that resolves', { + lookup: async (...args) => { + return E(agoricNames).lookup(...args); + }, + }); + const privateArgs = await deeplyFulfilledObject( harden({ - agoricNames, + agoricNames: agoricNamesResolves, localchain, marshaller, orchestrationService: cosmosInterchainService, diff --git a/packages/builders/scripts/testing/start-buggy-sendAnywhere.js b/packages/builders/scripts/testing/start-buggy-sendAnywhere.js index e726e3bf9eb..6bfb9933643 100644 --- a/packages/builders/scripts/testing/start-buggy-sendAnywhere.js +++ b/packages/builders/scripts/testing/start-buggy-sendAnywhere.js @@ -65,7 +65,6 @@ export const startSendAnywhere = async ({ ); const agoricNamesHangs = Far('agoricNames that hangs', { - // ...privateArgs.agoricNames, lookup: async () => { trace('agoricNames.lookup being called that will never resolve'); // BUG: this never resolves