Skip to content

Commit

Permalink
test: upgrade from buggy sendAnywhere
Browse files Browse the repository at this point in the history
  • Loading branch information
turadg committed Sep 17, 2024
1 parent dd61e16 commit 11879f7
Show file tree
Hide file tree
Showing 4 changed files with 355 additions and 1 deletion.
78 changes: 78 additions & 0 deletions packages/boot/test/orchestration/contract-upgrade.test.ts
Original file line number Diff line number Diff line change
@@ -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<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 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',
]);
});
134 changes: 134 additions & 0 deletions packages/builders/scripts/testing/fix-buggy-sendAnywhere.js
Original file line number Diff line number Diff line change
@@ -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';

/// <reference types="@agoric/vats/src/core/types-ambient"/>
/**
* @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<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();

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

/// <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,
}),
);

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);
};
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ harden(SingleAmountRecord);
* @param {Zone} zone
* @param {OrchestrationTools} tools
*/
const contract = async (
export const contract = async (
zcf,
privateArgs,
zone,
Expand Down Expand Up @@ -78,6 +78,7 @@ const contract = async (

return { publicFacet, creatorFacet };
};
harden(contract);

export const start = withOrchestration(contract);
harden(start);

0 comments on commit 11879f7

Please sign in to comment.