Skip to content

Commit

Permalink
test: restarting orchestration contracts
Browse files Browse the repository at this point in the history
  • Loading branch information
turadg committed Jul 25, 2024
1 parent 4184ba2 commit d374cd2
Show file tree
Hide file tree
Showing 4 changed files with 488 additions and 0 deletions.
169 changes: 169 additions & 0 deletions packages/boot/test/orchestration/restart-contracts.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
/** @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 type { CosmosValidatorAddress } from '@agoric/orchestration';
import type { UpdateRecord } from '@agoric/smart-wallet/src/smartWallet.js';
import {
makeWalletFactoryContext,
type WalletFactoryTestContext,
} from '../bootstrapTests/walletFactory.ts';

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?.());

// Not interesting because it doesn't wait on other chains. Leaving here because maybe it will before it's done.
test.serial('sendAnywhere', async t => {
const {
walletFactoryDriver,
buildProposal,
evalProposal,
flushInboundQueue,
} = t.context;

const { IST } = t.context.agoricNamesRemotes.brand;

t.log('start sendAnywhere');
await evalProposal(
buildProposal('@agoric/builders/scripts/testing/start-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: {
// meaningless address
destAddr: 'cosmos1qy352eufjjmc9c',
chainName: 'cosmoshub',
},
});
// no errors and no resolution
const beforeFlush = wallet.getLatestUpdateRecord();
t.like(wallet.getLatestUpdateRecord(), {
updated: 'offerStatus',
status: {
id: 'send-somewhere',
error: undefined,
},
numWantsSatisfied: undefined,
payouts: undefined,
result: undefined,
});

t.is(await flushInboundQueue(), 0);
t.deepEqual(wallet.getLatestUpdateRecord(), beforeFlush);

t.log('restart sendAnywhere');
await evalProposal(
buildProposal('@agoric/builders/scripts/testing/restart-sendAnywhere.js'),
);

const conclusion = wallet.getLatestUpdateRecord();
console.log('conclusion', conclusion);
t.like(conclusion, {
updated: 'offerStatus',
status: {
id: 'send-somewhere',
error: undefined,
},
numWantsSatisfied: undefined,
payouts: undefined,
result: undefined,
});

await flushInboundQueue();

// Nothing interesting to confirm here.
});

const validatorAddress: CosmosValidatorAddress = {
value: 'cosmosvaloper1test',
chainId: 'gaiatest',
encoding: 'bech32',
};

// check for key because the value will be 'undefined' when the result is provided
// TODO should it be something truthy?
const hasResult = (r: UpdateRecord) => {
assert(r.updated === 'offerStatus');
return 'result' in r.status;
};

// Tests restart but not of an orchestration() flow
test('stakeAtom', async t => {
const {
buildProposal,
evalProposal,
agoricNamesRemotes,
flushInboundQueue,
readLatest,
} = t.context;

await evalProposal(
buildProposal('@agoric/builders/scripts/orchestration/init-stakeAtom.js'),
);

const wd = await t.context.walletFactoryDriver.provideSmartWallet(
'agoric1testStakAtom',
);

await wd.sendOffer({
id: 'request-account',
invitationSpec: {
source: 'agoricContract',
instancePath: ['stakeAtom'],
callPipe: [['makeAccountInvitationMaker']],
},
proposal: {},
});
// cosmos1test is from ibc/mocks.js
const accountPath = 'published.stakeAtom.accounts.cosmos1test';
t.throws(() => readLatest(accountPath));
t.is(await flushInboundQueue(), 1);
t.is(readLatest(accountPath), '');
// request-account is complete

const { ATOM } = agoricNamesRemotes.brand;
assert(ATOM);

await wd.sendOffer({
id: 'request-delegate',
invitationSpec: {
source: 'continuing',
previousOffer: 'request-account',
invitationMakerName: 'Delegate',
invitationArgs: [validatorAddress, { brand: ATOM, value: 10n }],
},
proposal: {},
});
// no result yet because the IBC incoming messages haven't arrived
// and won't until we flush.
t.false(hasResult(wd.getLatestUpdateRecord()));

t.log('restart stakeAtom');
await evalProposal(
buildProposal('@agoric/builders/scripts/testing/restart-stakeAtom.js'),
);

t.is(await flushInboundQueue(), 1);
t.true(hasResult(wd.getLatestUpdateRecord()));
});
99 changes: 99 additions & 0 deletions packages/builders/scripts/testing/restart-sendAnywhere.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
/**
* @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"/>

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

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

/**
* @param {BootstrapPowers} powers
*/
export const restartSendAnywhere = async ({
consume: {
agoricNames,
board,
chainStorage,
chainTimerService,
cosmosInterchainService,
localchain,

contractKits,
},
instance: instances,
}) => {
trace(restartSendAnywhere.name);

// @ts-expect-error unknown instance
const instance = await instances.consume.sendAnywhere;
trace('instance', instance);
const kit = await E(contractKits).get(instance);

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

await E(kit.adminFacet).restartContract(privateArgs);
trace('done');
};
harden(restartSendAnywhere);

export const getManifest = () => {
return {
manifest: {
[restartSendAnywhere.name]: {
consume: {
agoricNames: true,
board: true,
chainStorage: true,
chainTimerService: true,
cosmosInterchainService: true,
localchain: true,

contractKits: true,
},
instance: {
consume: { sendAnywhere: true },
},
},
},
};
};

/** @type {import('@agoric/deploy-script-support/src/externalTypes.js').CoreEvalBuilder} */
export const defaultProposalBuilder = async () =>
harden({
// Somewhat unorthodox, source the exports from this builder module
sourceSpec: '@agoric/builders/scripts/testing/restart-sendAnywhere.js',
getManifestCall: [getManifest.name],
});

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(restartSendAnywhere.name, defaultProposalBuilder);
};
92 changes: 92 additions & 0 deletions packages/builders/scripts/testing/restart-stakeAtom.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
/**
* @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,

Check failure on line 8 in packages/builders/scripts/testing/restart-stakeAtom.js

View workflow job for this annotation

GitHub Actions / lint-rest

'NonNullish' is defined but never used. Allowed unused vars must match /^_/u
} from '@agoric/internal';
import { makeStorageNodeChild } from '@agoric/internal/src/lib-chainStorage.js';
import { E } from '@endo/far';

/// <reference types="@agoric/vats/src/core/types-ambient"/>

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

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

/**
* @param {BootstrapPowers} powers
*/
export const restartStakeAtom = async ({
consume: {
board,
chainStorage,
chainTimerService,
cosmosInterchainService,

contractKits,
},
instance: instances,
}) => {
trace(restartStakeAtom.name);

const instance = await instances.consume.stakeAtom;
trace('instance', instance);
/** @type {StartedInstanceKit<StartFn>} */
const kit = await E(contractKits).get(instance);

const marshaller = await E(board).getReadonlyMarshaller();

const privateArgs = await deeplyFulfilledObject(
harden({
cosmosInterchainService: cosmosInterchainService,

Check failure on line 46 in packages/builders/scripts/testing/restart-stakeAtom.js

View workflow job for this annotation

GitHub Actions / lint-rest

Expected property shorthand
storageNode: makeStorageNodeChild(chainStorage, 'stakeAtom'),
marshaller,
timer: chainTimerService,
}),
);

await E(kit.adminFacet).restartContract(privateArgs);
trace('done');
};
harden(restartStakeAtom);

export const getManifest = () => {
return {
manifest: {
[restartStakeAtom.name]: {
consume: {
board: true,
chainStorage: true,
chainTimerService: true,
cosmosInterchainService: true,

contractKits: true,
},
instance: {
consume: { stakeAtom: true },
},
},
},
};
};

/** @type {import('@agoric/deploy-script-support/src/externalTypes.js').CoreEvalBuilder} */
export const defaultProposalBuilder = async () =>
harden({
// Somewhat unorthodox, source the exports from this builder module
sourceSpec: '@agoric/builders/scripts/testing/restart-stakeAtom.js',
getManifestCall: [getManifest.name],
});

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(restartStakeAtom.name, defaultProposalBuilder);
};
Loading

0 comments on commit d374cd2

Please sign in to comment.