Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test: send-anywhere pfm scenarios #10591

Merged
merged 4 commits into from
Dec 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/workflows/multichain-e2e-template.yml
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,10 @@ jobs:
run: make override-chain-registry
working-directory: ./agoric-sdk/multichain-testing

- name: Register Interchain Bank Assets
run: make register-bank-assets
working-directory: ./agoric-sdk/multichain-testing

- name: Run @agoric/multichain-testing E2E Tests
run: yarn ${{ inputs.test_command }}
working-directory: ./agoric-sdk/multichain-testing
Expand Down
5 changes: 3 additions & 2 deletions multichain-testing/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
!.yarn/patches/*
# fetched chain info from running starship
starship-chain-info.js
# output of build script to get update running chain info
revise-chain-info*
# builder prefix for contract starters
start*
# builder prefix for core evals
eval-*
7 changes: 6 additions & 1 deletion multichain-testing/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,11 @@ override-chain-registry:
scripts/fetch-starship-chain-info.ts && \
scripts/deploy-cli.ts src/revise-chain-info.builder.js

register-bank-assets:
scripts/fetch-starship-chain-info.ts && \
scripts/deploy-cli.ts src/register-interchain-bank-assets.builder.js \
assets="$$(scripts/make-bank-asset-info.ts)"

ADDR=agoric1ldmtatp24qlllgxmrsjzcpe20fvlkp448zcuce
COIN=1000000000uist

Expand All @@ -101,5 +106,5 @@ wait-for-pods:
scripts/pod-readiness.ts

.PHONY: start
start: install wait-for-pods port-forward fund-provision-pool override-chain-registry
start: install wait-for-pods port-forward fund-provision-pool override-chain-registry register-bank-assets
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does deploy-cli.ts know how to run multiple evals in one chain governance proposal? How about combining override-chain-registry and register-bank-assets?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea. I won't tackle that here but I'll hope to make this update soon


2 changes: 1 addition & 1 deletion multichain-testing/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ make wait-for-pods
make port-forward

# set up Agoric testing environment
make fund-provision-pool override-chain-registry
make fund-provision-pool override-chain-registry register-bank-assets
```

If you get an error like "connection refused", you need to wait longer, until all the pods are Running.
Expand Down
17 changes: 14 additions & 3 deletions multichain-testing/scripts/deploy-cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,28 @@ import { makeAgdTools } from '../tools/agd-tools.js';
import { makeDeployBuilder } from '../tools/deploy.js';

async function main() {
const builder = process.argv[2];
const [builder, ...rawArgs] = process.argv.slice(2);

// Parse builder options from command line arguments
const builderOpts: Record<string, string> = {};
for (const arg of rawArgs) {
const [key, value] = arg.split('=');
if (key && value) {
builderOpts[key] = value;
}
}

if (!builder) {
console.error('USAGE: deploy-cli.ts <builder script>');
console.error(
'USAGE: deploy-cli.ts <builder script> [key1=value1] [key2=value2]',
);
process.exit(1);
}

try {
const agdTools = await makeAgdTools(console.log, childProcess);
const deployBuilder = makeDeployBuilder(agdTools, fse.readJSON, execa);
await deployBuilder(builder);
await deployBuilder(builder, builderOpts);
} catch (err) {
console.error(err);
process.exit(1);
Expand Down
30 changes: 30 additions & 0 deletions multichain-testing/scripts/make-bank-asset-info.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#!/usr/bin/env -S node --import ts-blank-space/register
/* eslint-env node */

import '@endo/init';
import starshipChainInfo from '../starship-chain-info.js';
import { makeAssetInfo } from '../tools/asset-info.ts';

const main = () => {
if (!starshipChainInfo) {
throw new Error(
'starshipChainInfo not found. run `./scripts/fetch-starship-chain-info.ts` first.',
);
}

const assetInfo = makeAssetInfo(starshipChainInfo)
.filter(
([_, { chainName, baseName }]) =>
chainName === 'agoric' && baseName !== 'agoric',
)
.map(([denom, { baseDenom }]) => ({
denom,
issuerName: baseDenom.replace(/^u/, '').toUpperCase(),
decimalPlaces: 6, // TODO do not assume 6
}));

// Directly output JSON string for proposal builder options
process.stdout.write(JSON.stringify(assetInfo));
};

main();
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/* global harden */
/// <reference types="ses" />
import { makeHelpers } from '@agoric/deploy-script-support';
import { parseArgs } from 'node:util';

/**
* @import {ParseArgsConfig} from 'node:util';
* @import {CoreEvalBuilder, DeployScriptFunction} from '@agoric/deploy-script-support/src/externalTypes.js';
*/

/** @type {ParseArgsConfig['options']} */
const parserOpts = {
assets: { type: 'string' },
};

/** @type {CoreEvalBuilder} */
export const defaultProposalBuilder = async (_, options) => {
return harden({
sourceSpec:
'@agoric/builders/scripts/testing/register-interchain-bank-assets.js',
getManifestCall: ['getManifestCall', options],
});
};

/** @type {DeployScriptFunction} */
export default async (homeP, endowments) => {
const { scriptArgs } = endowments;

const {
values: { assets },
} = parseArgs({
args: scriptArgs,
options: parserOpts,
});

const parseAssets = () => {
if (typeof assets !== 'string') {
throw Error(
'must provide --assets=JSON.stringify({ denom: Denom; issuerName: string; decimalPlaces: number; }[])',
);
}
return JSON.parse(assets);
};

const opts = harden({ assets: parseAssets() });

const { writeCoreEval } = await makeHelpers(homeP, endowments);
await writeCoreEval('eval-register-interchain-bank-assets', utils =>
defaultProposalBuilder(utils, opts),
);
};
2 changes: 1 addition & 1 deletion multichain-testing/src/revise-chain-info.builder.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,5 @@ export const defaultProposalBuilder = async () =>
/** @type {import('@agoric/deploy-script-support/src/externalTypes.js').DeployScriptFunction} */
export default async (homeP, endowments) => {
const { writeCoreEval } = await makeHelpers(homeP, endowments);
await writeCoreEval('revise-chain-info', defaultProposalBuilder);
await writeCoreEval('eval-revise-chain-info', defaultProposalBuilder);
};
60 changes: 7 additions & 53 deletions multichain-testing/test/auto-stake-it.test.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
import anyTest from '@endo/ses-ava/prepare-endo.js';
import type { ExecutionContext, TestFn } from 'ava';
import type { TestFn } from 'ava';
import starshipChainInfo from '../starship-chain-info.js';
import { makeDoOffer } from '../tools/e2e-tools.js';
import {
createFundedWalletAndClient,
makeIBCTransferMsg,
} from '../tools/ibc-transfer.js';
import { makeFundAndTransfer } from '../tools/ibc-transfer.js';
import { makeQueryClient } from '../tools/query.js';
import type { SetupContextWithWallets } from './support.js';
import { chainConfig, commonSetup } from './support.js';
Expand Down Expand Up @@ -37,53 +34,6 @@ test.after(async t => {
deleteTestKeys(accounts);
});

const makeFundAndTransfer = (t: ExecutionContext<SetupContextWithWallets>) => {
const { retryUntilCondition, useChain } = t.context;
return async (chainName: string, agoricAddr: string, amount = 100n) => {
const { staking } = useChain(chainName).chainInfo.chain;
const denom = staking?.staking_tokens?.[0].denom;
if (!denom) throw Error(`no denom for ${chainName}`);

const { client, address, wallet } = await createFundedWalletAndClient(
t,
chainName,
useChain,
);
const balancesResult = await retryUntilCondition(
() => client.getAllBalances(address),
coins => !!coins?.length,
`Faucet balances found for ${address}`,
);

console.log('Balances:', balancesResult);

const transferArgs = makeIBCTransferMsg(
{ denom, value: amount },
{ address: agoricAddr, chainName: 'agoric' },
{ address: address, chainName },
Date.now(),
useChain,
);
console.log('Transfer Args:', transferArgs);
// TODO #9200 `sendIbcTokens` does not support `memo`
// @ts-expect-error spread argument for concise code
const txRes = await client.sendIbcTokens(...transferArgs);
if (txRes && txRes.code !== 0) {
console.error(txRes);
throw Error(`failed to ibc transfer funds to ${chainName}`);
}
const { events: _events, ...txRest } = txRes;
console.log(txRest);
t.is(txRes.code, 0, `Transaction succeeded`);
t.log(`Funds transferred to ${agoricAddr}`);
return {
client,
address,
wallet,
};
};
};

const autoStakeItScenario = test.macro({
title: (_, chainName: string) => `auto-stake-it on ${chainName}`,
exec: async (t, chainName: string) => {
Expand All @@ -96,7 +46,11 @@ const autoStakeItScenario = test.macro({
useChain,
} = t.context;

const fundAndTransfer = makeFundAndTransfer(t);
const fundAndTransfer = makeFundAndTransfer(
t,
retryUntilCondition,
useChain,
);

// 2. Find 'stakingDenom' denom on agoric
const remoteChainInfo = starshipChainInfo[chainName];
Expand Down
30 changes: 30 additions & 0 deletions multichain-testing/test/scripts/make-bank-asset-info.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import test from 'ava';
import { execFileSync } from 'node:child_process';

test('make-bank-asset-info', async t => {
const stdout = execFileSync('./scripts/make-bank-asset-info.ts', {
encoding: 'utf8',
});

const assetInfo = JSON.parse(stdout);

t.like(assetInfo, [
{
issuerName: 'ATOM',
decimalPlaces: 6,
},
{
issuerName: 'OSMO',
decimalPlaces: 6,
},
{
issuerName: 'ION',
decimalPlaces: 6,
},
]);

for (const { denom } of assetInfo) {
t.regex(denom, /^ibc\//);
t.is(denom.length, 68);
}
});
50 changes: 29 additions & 21 deletions multichain-testing/test/send-anywhere.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ const contractBuilder =

test.before(async t => {
const { setupTestKeys, ...common } = await commonSetup(t);
const { assetInfo, chainInfo, deleteTestKeys, startContract } = common;
const { assetInfo, chainInfo, deleteTestKeys, faucetTools, startContract } =
common;
deleteTestKeys(accounts).catch();
const wallets = await setupTestKeys(accounts);
t.context = { ...common, wallets };
Expand All @@ -30,19 +31,26 @@ test.before(async t => {
chainInfo,
assetInfo,
});

await faucetTools.fundFaucet([
['cosmoshub', 'uatom'],
['osmosis', 'uosmo'],
]);
});

test.after(async t => {
const { deleteTestKeys } = t.context;
deleteTestKeys(accounts);
});

type BrandKW = 'IST' | 'OSMO' | 'ATOM';

const sendAnywhereScenario = test.macro({
title: (_, chainName: string, acctIdx: number) =>
`send-anywhere ${chainName}${acctIdx}`,
exec: async (t, chainName: string, acctIdx: number) => {
const config = chainConfig[chainName];
if (!config) return t.fail(`Unknown chain: ${chainName}`);
title: (_, destChainName: string, acctIdx: number, brandKw: BrandKW) =>
`send-anywhere ${brandKw} from agoric to ${destChainName}${acctIdx}`,
exec: async (t, destChainName: string, acctIdx: number, brandKw: BrandKW) => {
const config = chainConfig[destChainName];
if (!config) return t.fail(`Unknown chain: ${destChainName}`);

const {
wallets,
Expand All @@ -53,13 +61,13 @@ const sendAnywhereScenario = test.macro({
} = t.context;

t.log('Create a receiving wallet for the send-anywhere transfer');
const chain = useChain(chainName).chain;
const chain = useChain(destChainName).chain;

t.log('Create an agoric smart wallet to initiate send-anywhere transfer');
const agoricAddr = wallets[`${chainName}${acctIdx}`];
const agoricAddr = wallets[`${destChainName}${acctIdx}`];
const wdUser1 = await provisionSmartWallet(agoricAddr, {
BLD: 100_000n,
IST: 100_000n,
BLD: 1_000n,
[brandKw]: 1_000n,
});
t.log(`provisioning agoric smart wallet for ${agoricAddr}`);

Expand All @@ -68,11 +76,11 @@ const sendAnywhereScenario = test.macro({
const brands = await vstorageClient.queryData(
'published.agoricNames.brand',
);
const istBrand = Object.fromEntries(brands).IST;
const brand = Object.fromEntries(brands)[brandKw];

const apiUrl = await useChain(chainName).getRestEndpoint();
const apiUrl = await useChain(destChainName).getRestEndpoint();
const queryClient = makeQueryClient(apiUrl);
t.log(`Made ${chainName} query client`);
t.log(`Made ${destChainName} query client`);

const doSendAnywhere = async (amount: Amount) => {
t.log(`Sending ${amount.value} ${amount.brand}.`);
Expand All @@ -83,16 +91,16 @@ const sendAnywhereScenario = test.macro({
encoding: 'bech32',
};
t.log('Will send payment to:', receiver);
t.log(`${chainName} offer`);
const offerId = `${chainName}-makeSendInvitation-${Date.now()}`;
t.log(`${destChainName} offer`);
const offerId = `${destChainName}-makeSendInvitation-${Date.now()}`;
await doOffer({
id: offerId,
invitationSpec: {
source: 'agoricContract',
instancePath: [contractName],
callPipe: [['makeSendInvitation']],
},
offerArgs: { destAddr: receiver.value, chainName },
offerArgs: { destAddr: receiver.value, chainName: destChainName },
proposal: { give: { Send: amount } },
});

Expand Down Expand Up @@ -123,12 +131,12 @@ const sendAnywhereScenario = test.macro({
console.log(`${agoricAddr} offer amounts:`, offerAmounts);

for (const value of offerAmounts) {
await doSendAnywhere(AmountMath.make(istBrand, value));
await doSendAnywhere(AmountMath.make(brand, value));
}
},
});

test.serial(sendAnywhereScenario, 'osmosis', 1);
test.serial(sendAnywhereScenario, 'osmosis', 2);
test.serial(sendAnywhereScenario, 'cosmoshub', 1);
test.serial(sendAnywhereScenario, 'cosmoshub', 2);
test.serial(sendAnywhereScenario, 'osmosis', 1, 'IST');
test.serial(sendAnywhereScenario, 'osmosis', 2, 'ATOM'); // exercises PFM (agoric -> cosmoshub -> osmosis)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it took me a while to grok what this test does, but I think I get it now.

For ref:
https://github.com/Agoric/agoric-sdk/actions/runs/12123427233/job/33798929261?pr=10591#step:13:3141

test.serial(sendAnywhereScenario, 'cosmoshub', 1, 'IST');
test.serial(sendAnywhereScenario, 'cosmoshub', 2, 'OSMO'); // exercises PFM (agoric -> osmosis -> cosmoshub)
Loading
Loading