From 307700ab740f67091aa27ce9e8355b72545223a6 Mon Sep 17 00:00:00 2001 From: Ikenna Omekam Date: Thu, 14 Nov 2024 10:06:52 -0500 Subject: [PATCH] test: send-anywhere contract upgrade - no longer relies on buggy agoricNames - continues to test async-flow resumability and cross-chain vow settlement across upgrade Co-authored-by: 0xPatrick --- .../orchestration/contract-upgrade.test.ts | 52 ++++--- .../testing/start-buggy-sendAnywhere.js | 143 ------------------ ...ndAnywhere.js => upgrade-send-anywhere.js} | 32 ++-- 3 files changed, 44 insertions(+), 183 deletions(-) delete mode 100644 packages/builders/scripts/testing/start-buggy-sendAnywhere.js rename packages/builders/scripts/testing/{fix-buggy-sendAnywhere.js => upgrade-send-anywhere.js} (81%) diff --git a/packages/boot/test/orchestration/contract-upgrade.test.ts b/packages/boot/test/orchestration/contract-upgrade.test.ts index ada1a4a4144..b059c1829c3 100644 --- a/packages/boot/test/orchestration/contract-upgrade.test.ts +++ b/packages/boot/test/orchestration/contract-upgrade.test.ts @@ -4,6 +4,8 @@ import type { TestFn } from 'ava'; import { BridgeId } from '@agoric/internal'; import { buildVTransferEvent } from '@agoric/orchestration/tools/ibc-mocks.js'; +import fetchedChainInfo from '@agoric/orchestration/src/fetched-chain-info.js'; +import { withChainCapabilities } from '@agoric/orchestration'; import { makeWalletFactoryContext, type WalletFactoryTestContext, @@ -19,17 +21,13 @@ test.before(async t => { 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 which - * returns a promise that never resolves. + * This test core-evals an installation of the sendAnywhere contract that + * initiates an IBC Transfer. Since that goes over a bridge and is tracked + * by a vow, we can restart the contract and see that the vow settles. We + * can manually trigger a bridge event in the testing context. * - * 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. (The lookup call is not made - * directly in a flow, but instead from a host API which uses the retryable - * helper. As such it tests both the idempotent retry mechanism of retryable on - * upgrades, and the ability to resume an async-flow for which a host vow - * settles after an upgrade.) + * As such, this demonstrates the ability to resume an async-flow for for which + * a host vow settles after an upgrade. */ test('resume', async t => { const { @@ -44,9 +42,21 @@ test('resume', async t => { t.log('start sendAnywhere'); await evalProposal( - buildProposal( - '@agoric/builders/scripts/testing/start-buggy-sendAnywhere.js', - ), + buildProposal('@agoric/builders/scripts/testing/init-send-anywhere.js', [ + '--chainInfo', + JSON.stringify(withChainCapabilities(fetchedChainInfo)), + '--assetInfo', + JSON.stringify([ + [ + 'uist', + { + baseDenom: 'uist', + baseName: 'agoric', + chainName: 'agoric', + }, + ], + ]), + ]), ); t.log('making offer'); @@ -70,16 +80,9 @@ test('resume', async t => { // 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'), - ); + JSON.parse(storage.data.get('published.send-anywhere.log')!).values; + // This log shows the flow started, but didn't get past the IBC Transfer settlement t.deepEqual(getLogged(), [ 'sending {0} from cosmoshub to cosmos1whatever', 'got info for denoms: ibc/FE98AAD68F02F03565E9FA39A5E627946699B2B07115889ED812D8BA639576A9, ibc/toyatom, ibc/toyusdc, ubld, uist', @@ -87,6 +90,11 @@ test('resume', async t => { 'completed transfer to localAccount', ]); + t.log('upgrade sendAnywhere with fix'); + await evalProposal( + buildProposal('@agoric/builders/scripts/testing/upgrade-send-anywhere.js'), + ); + // simulate ibc/MsgTransfer ack from remote chain, enabling `.transfer()` promise // to resolve await runInbound( diff --git a/packages/builders/scripts/testing/start-buggy-sendAnywhere.js b/packages/builders/scripts/testing/start-buggy-sendAnywhere.js deleted file mode 100644 index 6f5ac66ee1d..00000000000 --- a/packages/builders/scripts/testing/start-buggy-sendAnywhere.js +++ /dev/null @@ -1,143 +0,0 @@ -/** - * @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, - }), - ); - - /** @type {import('@agoric/vats').NameHub} */ - // @ts-expect-error intentional fake - const agoricNamesHangs = Far('agoricNames that hangs', { - 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', - ), - ), - }, - ], - }); - -/** @type {import('@agoric/deploy-script-support/src/externalTypes.js').DeployScriptFunction} */ -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/builders/scripts/testing/fix-buggy-sendAnywhere.js b/packages/builders/scripts/testing/upgrade-send-anywhere.js similarity index 81% rename from packages/builders/scripts/testing/fix-buggy-sendAnywhere.js rename to packages/builders/scripts/testing/upgrade-send-anywhere.js index 8daa2dbf516..4acfaf5f8ad 100644 --- a/packages/builders/scripts/testing/fix-buggy-sendAnywhere.js +++ b/packages/builders/scripts/testing/upgrade-send-anywhere.js @@ -7,14 +7,14 @@ import { makeTracer, NonNullish, } from '@agoric/internal'; -import { E, Far } from '@endo/far'; +import { E } from '@endo/far'; /// /** * @import {Installation, Instance} from '@agoric/zoe/src/zoeService/utils.js'; */ -const trace = makeTracer('FixBuggySA', true); +const trace = makeTracer('UpgradeSA', true); /** * @import {start as StartFn} from '@agoric/orchestration/src/examples/send-anywhere.contract.js'; @@ -30,7 +30,7 @@ const trace = makeTracer('FixBuggySA', true); * }} powers * @param {...any} rest */ -export const fixSendAnywhere = async ( +export const upgradeSendAnywhere = async ( { consume: { agoricNames, @@ -45,7 +45,7 @@ export const fixSendAnywhere = async ( }, { options: { sendAnywhereRef } }, ) => { - trace(fixSendAnywhere.name); + trace(upgradeSendAnywhere.name); const saInstance = await instances.consume.sendAnywhere; trace('saInstance', saInstance); @@ -53,28 +53,24 @@ 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: agoricNamesResolves, + agoricNames, localchain, marshaller, orchestrationService: cosmosInterchainService, storageNode: E(NonNullish(await chainStorage)).makeChildNode( - 'sendAnywhere', + 'send-anywhere', ), timerService: chainTimerService, + // undefined so `registerKnownChainsAndAssets` does not run again + chainInfo: undefined, + assetInfo: undefined, }), ); trace('upgrading...'); + trace('ref', sendAnywhereRef); await E(saKit.adminFacet).upgradeContract( sendAnywhereRef.bundleID, privateArgs, @@ -82,13 +78,13 @@ export const fixSendAnywhere = async ( trace('done'); }; -harden(fixSendAnywhere); +harden(upgradeSendAnywhere); export const getManifestForValueVow = ({ restoreRef }, { sendAnywhereRef }) => { console.log('sendAnywhereRef', sendAnywhereRef); return { manifest: { - [fixSendAnywhere.name]: { + [upgradeSendAnywhere.name]: { consume: { agoricNames: true, board: true, @@ -120,7 +116,7 @@ export const getManifestForValueVow = ({ restoreRef }, { sendAnywhereRef }) => { 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', + sourceSpec: '@agoric/builders/scripts/testing/upgrade-send-anywhere.js', getManifestCall: [ 'getManifestForValueVow', { @@ -139,5 +135,5 @@ export default async (homeP, endowments) => { const dspModule = await import('@agoric/deploy-script-support'); const { makeHelpers } = dspModule; const { writeCoreEval } = await makeHelpers(homeP, endowments); - await writeCoreEval(fixSendAnywhere.name, defaultProposalBuilder); + await writeCoreEval(upgradeSendAnywhere.name, defaultProposalBuilder); };