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

perf measurement for Fast USDC in multichain-testing #10857

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
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
195 changes: 76 additions & 119 deletions multichain-testing/test/fast-usdc/fast-usdc.test.ts
Original file line number Diff line number Diff line change
@@ -1,54 +1,46 @@
import anyTest from '@endo/ses-ava/prepare-endo.js';

import {
makeBlocksIterable,
queryBlock,
} from '@agoric/client-utils/src/chain.js';
import { makeAPI } from '@agoric/client-utils/src/grpc-rest-api.js';
import { encodeAddressHook } from '@agoric/cosmic-proto/address-hooks.js';
import type { QueryBalanceResponseSDKType } from '@agoric/cosmic-proto/cosmos/bank/v1beta1/query.js';
import { AmountMath } from '@agoric/ertp';
import type { USDCProposalShapes } from '@agoric/fast-usdc/src/pool-share-math.js';
import type {
CctpTxEvidence,
EvmAddress,
PoolMetrics,
} from '@agoric/fast-usdc/src/types.js';
import { makeTracer } from '@agoric/internal';
import type { Denom } from '@agoric/orchestration';
import type { CurrentWalletRecord } from '@agoric/smart-wallet/src/smartWallet.js';
import type { IBCChannelID } from '@agoric/vats';
import { divideBy, multiplyBy } from '@agoric/zoe/src/contractSupport/ratio.js';
import type { TestFn } from 'ava';
import { makeDenomTools } from '../../tools/asset-info.js';
import { makeDoOffer, type WalletDriver } from '../../tools/e2e-tools.js';
import { makeDoOffer } from '../../tools/e2e-tools.js';
import { makeQueryClient } from '../../tools/query.js';
import { makeRandomDigits } from '../../tools/random.js';
import { createWallet } from '../../tools/wallet.js';
import { commonSetup, type SetupContextWithWallets } from '../support.js';
import { commonSetup } from '../support.js';
import { makeFeedPolicyPartial, oracleMnemonics } from './config.js';
import { agoricNamesQ, fastLPQ, makeTxOracle } from './fu-actors.js';

const log = makeTracer('MCFU');

const { keys, values, fromEntries } = Object;
const { keys, values } = Object;
const { isGTE, isEmpty, make, subtract } = AmountMath;

const makeRandomNumber = () => Math.random();

const test = anyTest as TestFn<
SetupContextWithWallets & {
lpUser: WalletDriver;
feeUser: WalletDriver;
oracleWds: WalletDriver[];
nobleAgoricChannelId: IBCChannelID;
usdcOnOsmosis: Denom;
/** usdc on agoric */
usdcDenom: Denom;
}
>;
const test = anyTest as TestFn<Awaited<ReturnType<typeof makeTestContext>>>;

const accounts = [...keys(oracleMnemonics), 'lp', 'feeDest'];
const contractName = 'fastUsdc';
const contractBuilder =
'../packages/builders/scripts/fast-usdc/start-fast-usdc.build.js';
const LP_DEPOSIT_AMOUNT = 8_000n * 10n ** 6n;

test.before(async t => {
const makeTestContext = async t => {
const { setupTestKeys, ...common } = await commonSetup(t, {
config: '../config.fusdc.yaml',
});
Expand All @@ -62,6 +54,7 @@ test.before(async t => {
} = common;
await deleteTestKeys(accounts).catch();
const wallets = await setupTestKeys(accounts, values(oracleMnemonics));
t.log('setupTestKeys:', wallets);

// provision oracle wallets first so invitation deposits don't fail
const oracleWds = await Promise.all(
Expand Down Expand Up @@ -98,82 +91,49 @@ test.before(async t => {
});
const feeUser = await provisionSmartWallet(wallets['feeDest'], { BLD: 100n });

t.context = {
const { vstorageClient } = common;
const api = makeAPI({ fetch });
const now = () => Date.now();
const blockIter = makeBlocksIterable({
api,
setTimeout,
clearTimeout,
now,
});
const oKeys = keys(oracleMnemonics);
const txOracles = oracleWds.map((wd, ix) =>
makeTxOracle(oKeys[ix], { wd, vstorageClient, blockIter, now }),
);

return {
...common,
api,
lpUser,
feeUser,
oracleWds,
txOracles,
nobleAgoricChannelId,
usdcOnOsmosis,
usdcDenom,
wallets,
};
});
};
test.before(async t => (t.context = await makeTestContext(t)));

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

type VStorageClient = Awaited<ReturnType<typeof commonSetup>>['vstorageClient'];
const agoricNamesQ = (vsc: VStorageClient) =>
harden({
brands: <K extends AssetKind>(_assetKind: K) =>
vsc
.queryData('published.agoricNames.brand')
.then(pairs => fromEntries(pairs) as Record<string, Brand<K>>),
});
const walletQ = (vsc: VStorageClient) => {
const self = harden({
current: (addr: string) =>
vsc.queryData(
`published.wallet.${addr}.current`,
) as Promise<CurrentWalletRecord>,
findInvitationDetail: async (addr: string, description: string) => {
const { Invitation } = await agoricNamesQ(vsc).brands('set');
const current = await self.current(addr);
const { purses } = current;
const { value: details } = purses.find(p => p.brand === Invitation)!
.balance as Amount<'set', InvitationDetails>;
const detail = details.find(x => x.description === description);
return { current, detail };
},
});
return self;
};

const fastLPQ = (vsc: VStorageClient) =>
harden({
metrics: () =>
vsc.queryData(`published.fastUsdc.poolMetrics`) as Promise<PoolMetrics>,
info: () =>
vsc.queryData(`published.${contractName}`) as Promise<{
poolAccount: string;
settlementAccount: string;
}>,
});

const toOracleOfferId = (idx: number) => `oracle${idx + 1}-accept`;

test.serial('oracles accept', async t => {
const { oracleWds, retryUntilCondition, vstorageClient, wallets } = t.context;

const description = 'oracle operator invitation';
const { txOracles, retryUntilCondition } = t.context;

// ensure we have an unused (or used) oracle invitation in each purse
let hasAccepted = false;
for (const name of keys(oracleMnemonics)) {
const {
current: { offerToUsedInvitation },
detail,
} = await walletQ(vstorageClient).findInvitationDetail(
wallets[name],
description,
);
const hasInvitation = !!detail;
const usedInvitation = offerToUsedInvitation?.[0]?.[0] === `${name}-accept`;
t.log({ name, hasInvitation, usedInvitation });
t.true(hasInvitation || usedInvitation, 'has or accepted invitation');
for (const op of txOracles) {
const { detail, usedInvitation } = await op.checkInvitation();
t.log({ name: op.getName(), hasInvitation: !!detail, usedInvitation });
t.true(!!detail || usedInvitation, 'has or accepted invitation');
if (usedInvitation) hasAccepted = true;
}
// if the oracles have already accepted, skip the rest of the test this is
Expand All @@ -182,32 +142,14 @@ test.serial('oracles accept', async t => {
if (hasAccepted) return t.pass();

// accept oracle operator invitations
const instance = fromEntries(
await vstorageClient.queryData('published.agoricNames.instance'),
)[contractName];
await Promise.all(
oracleWds.map(makeDoOffer).map((doOffer, i) =>
doOffer({
id: toOracleOfferId(i),
invitationSpec: {
source: 'purse',
instance,
description,
},
proposal: {},
}),
),
);
await Promise.all(txOracles.map(op => op.acceptInvitation()));

for (const name of keys(oracleMnemonics)) {
const addr = wallets[name];
for (const op of txOracles) {
await t.notThrowsAsync(() =>
retryUntilCondition(
() => vstorageClient.queryData(`published.wallet.${addr}.current`),
({ offerToUsedInvitation }) => {
return offerToUsedInvitation[0][0] === `${name}-accept`;
},
`${name} invitation used`,
() => op.checkInvitation(),
({ usedInvitation }) => !!usedInvitation,
`${op.getName()} invitation used`,
{ log },
),
);
Expand Down Expand Up @@ -272,9 +214,10 @@ const advanceAndSettleScenario = test.macro({
`advance ${mintAmt} uusdc to ${eudChain} and settle`,
exec: async (t, mintAmt: bigint, eudChain: string) => {
const {
api,
nobleTools,
nobleAgoricChannelId,
oracleWds,
txOracles,
retryUntilCondition,
smartWalletKit,
useChain,
Expand Down Expand Up @@ -328,37 +271,50 @@ const advanceAndSettleScenario = test.macro({
chainId: 42161,
});

log('User initiates evm mint:', evidence.txHash);
log('User initiates EVM burn:', evidence.txHash);
const { block } = await queryBlock(api);
console.time(`UX->${eudChain}`);
console.timeLog(
`UX->${eudChain}`,
'initial height',
block.header.height,
block.header.time,
);

// submit evidences
await Promise.all(
oracleWds.map(makeDoOffer).map((doOffer, i) =>
doOffer({
id: `${Date.now()}-evm-evidence`,
invitationSpec: {
source: 'continuing',
previousOffer: toOracleOfferId(i),
invitationMakerName: 'SubmitEvidence',
invitationArgs: [evidence],
},
proposal: {},
}),
),
txOracles.map(async o => {
const { block } = await o.submit(evidence);
console.timeLog(`UX->${eudChain}`, o.getName(), block);
}),
);
console.timeLog(`UX->${eudChain}`, 'submitted x', txOracles.length);

const queryClient = makeQueryClient(
await useChain(eudChain).getRestEndpoint(),
);

await t.notThrowsAsync(() =>
retryUntilCondition(
await t.notThrowsAsync(async () => {
const q = await retryUntilCondition(
() => queryClient.queryBalance(EUD, usdcOnOsmosis),
({ balance }) => !!balance?.amount && BigInt(balance.amount) < mintAmt,
({ balance }) =>
!!balance?.amount &&
BigInt(balance.amount) > 0n &&
BigInt(balance.amount) < mintAmt,
`${EUD} advance available from fast-usdc`,
// this resolves quickly, so _decrease_ the interval so the timing is more apparent
{ retryIntervalMs: 500 },
),
);
{ retryIntervalMs: 500, maxRetries: 20 },
);
console.timeLog(`UX->${eudChain}`, 'rxd', q.balance?.amount);
const { block } = await queryBlock(api);
console.timeLog(
`UX->${eudChain}`,
'final block',
block.header.height,
block.header.time,
);
});
console.timeEnd(`UX->${eudChain}`);

const queryTxStatus = async () => {
const record = await smartWalletKit.readPublished(
Expand Down Expand Up @@ -397,6 +353,7 @@ const advanceAndSettleScenario = test.macro({
({ encumberedBalance }) =>
encumberedBalance && isEmpty(encumberedBalance),
'encumberedBalance returns to 0',
{ maxRetries: 24 },
),
);

Expand Down
Loading
Loading