Skip to content

Commit

Permalink
9303 test upgrading send-anywhere (#9719)
Browse files Browse the repository at this point in the history
refs: #9303

## Description
This adds a test for upgrading send-anywhere. It doesn't yet pass without these in-flight PRs,
- #9736
- #9785

We've agreed to land this without those to reduce work-in-flight.

### Security Considerations
none

### Scaling Considerations
none

### Documentation Considerations
none

### Testing Considerations
The failing test has a link to the issue to make it pass.

### Upgrade Considerations
none
  • Loading branch information
mergify[bot] authored Sep 28, 2024
2 parents 6909994 + b9c469b commit e3b41dc
Show file tree
Hide file tree
Showing 7 changed files with 402 additions and 9 deletions.
87 changes: 87 additions & 0 deletions packages/boot/test/orchestration/contract-upgrade.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/** @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<WalletFactoryTestContext> = 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 which
* 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. (The lookup call is not made
* directly in a flow, but instead from a host API which uses the retriable
* helper. As such it tests both the idempotent retry mechanism of retriable on
* upgrades, and the ability to resume an async-flow for which a host vow
* settles after an upgrade.)
*/
test.failing('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'),
);

// FIXME https://github.com/Agoric/agoric-sdk/issues/9303
// This doesn't yet get past 'sending'
t.deepEqual(getLogged(), [
'sending {0} from cosmoshub to cosmos1whatever',
'got info for denoms: ibc/toyatom, ibc/toyusdc, ubld, uist',
'got info for chain: cosmoshub cosmoshub-4',
'completed transfer to localAccount',
// But does not get to a complete transaction without mocking the IBC transfer acknowledgementPacket
// TODO file a ticket for providing that and also fixing it in restart-contracts's .failing test
// 'transfer complete, seat exited',
]);
});
142 changes: 142 additions & 0 deletions packages/builders/scripts/testing/fix-buggy-sendAnywhere.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
/**
* @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';

/// <reference types="@agoric/vats/src/core/types-ambient"/>
/**
* @import {Installation, Instance} from '@agoric/zoe/src/zoeService/utils.js';
*/

const trace = makeTracer('FixBuggySA', true);

/**
* @import {start as StartFn} from '@agoric/orchestration/src/examples/send-anywhere.contract.js';
*/

/**
* @param {BootstrapPowers & {
* instance: {
* consume: {
* sendAnywhere: Instance<StartFn>;
* };
* };
* }} 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();

// 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,
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);
};
142 changes: 142 additions & 0 deletions packages/builders/scripts/testing/start-buggy-sendAnywhere.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
/**
* @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';

/// <reference types="@agoric/vats/src/core/types-ambient"/>
/**
* @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<StartFn>;
* };
* };
* }} 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',
),
),
},
],
});

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);
};
Loading

0 comments on commit e3b41dc

Please sign in to comment.