Skip to content

Commit

Permalink
test(orchestration): ibc mocking utils (#9628)
Browse files Browse the repository at this point in the history
closes: #9572 

## Description

Provides testing utilities for simulating IBC bridge events and packets in `@agoric/orchestration` .
  • Loading branch information
mergify[bot] authored Jul 2, 2024
2 parents 8edf902 + 0bd7161 commit 5d7774e
Show file tree
Hide file tree
Showing 18 changed files with 826 additions and 245 deletions.
2 changes: 1 addition & 1 deletion packages/boot/tools/ibc/mocks.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ export const addParamsIfJsonVersion = (version, params) => {
export const icaMocks = {
/**
* ICA Channel Creation
* @param {IBCMethod<'initOpenExecuted'>} obj
* @param {IBCMethod<'startChannelOpenInit'>} obj
* @returns {IBCEvent<'channelOpenAck'>}
*/
channelOpenAck: obj => {
Expand Down
2 changes: 1 addition & 1 deletion packages/cosmic-proto/src/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ export const typeUrlToGrpcPath = (typeUrl: Any['typeUrl']) => {
type RequestQueryOpts = Partial<Omit<RequestQuery, 'path' | 'data'>>;

export const toRequestQueryJson = (
msg: QueryBalanceRequestProtoMsg,
msg: Any | QueryBalanceRequestProtoMsg,
opts: RequestQueryOpts = {},
) =>
RequestQuery.toJSON(
Expand Down
2 changes: 1 addition & 1 deletion packages/orchestration/src/examples/stakeIca.contract.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { makeDurableZone } from '@agoric/zone/durable.js';
import { M } from '@endo/patterns';
import { prepareCosmosOrchestrationAccount } from '../exos/cosmos-orchestration-account.js';

const trace = makeTracer('StakeAtom');
const trace = makeTracer('StakeIca');
/**
* @import {Baggage} from '@agoric/vat-data';
* @import {IBCConnectionID} from '@agoric/vats';
Expand Down
16 changes: 5 additions & 11 deletions packages/orchestration/src/utils/packet.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import { Fail } from '@endo/errors';
import { TxBody } from '@agoric/cosmic-proto/cosmos/tx/v1beta1/tx.js';
import { Any } from '@agoric/cosmic-proto/google/protobuf/any.js';
import { RequestQuery } from '@agoric/cosmic-proto/tendermint/abci/types.js';
import {
RequestQuery,
ResponseQuery,
} from '@agoric/cosmic-proto/tendermint/abci/types.js';
import { atob, decodeBase64, encodeBase64 } from '@endo/base64';
import {
CosmosQuery,
Expand All @@ -11,7 +14,6 @@ import { Type as PacketType } from '@agoric/cosmic-proto/ibc/applications/interc

/**
* @import {AnyJson, JsonSafe} from '@agoric/cosmic-proto';
* @import {ResponseQuery} from '@agoric/cosmic-proto/tendermint/abci/types.js';
* @import {InterchainAccountPacketData} from '@agoric/cosmic-proto/ibc/applications/interchain_accounts/v1/packet.js';
* @import {InterchainQueryPacketData} from '@agoric/cosmic-proto/icq/v1/packet.js';
*/
Expand Down Expand Up @@ -105,14 +107,6 @@ export function parseQueryPacket(response) {
const result = parseTxPacket(response);
const { data } = JSON.parse(atob(result));
const { responses = [] } = CosmosResponse.decode(decodeBase64(data));
return harden(
responses.map(resp => ({
...resp,
height: String(resp.index),
index: String(resp.index),
key: encodeBase64(resp.key),
value: encodeBase64(resp.value),
})),
);
return harden(responses.map(ResponseQuery.toJSON));
}
harden(parseQueryPacket);
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Snapshot report for `test/examples/stake-ica.contract.test.ts`

The actual snapshot is saved in `stake-ica.contract.test.ts.snap`.

Generated by [AVA](https://avajs.dev).

## makeAccount, getAddress, getBalances, getBalance

> accounts in vstorage
Object @Map Iterator {
[
'mockChainStorageRoot.stakeAtom.accounts.cosmos1test',
'{"body":"#\\"\\"","slots":[]}',
],
[
'mockChainStorageRoot.stakeAtom.accounts.cosmos1test1',
'{"body":"#\\"\\"","slots":[]}',
],
[
'mockChainStorageRoot.stakeOsmo.accounts.osmo1test2',
'{"body":"#\\"\\"","slots":[]}',
],
}
Binary file not shown.
107 changes: 0 additions & 107 deletions packages/orchestration/test/examples/stake-atom.contract.test.ts

This file was deleted.

166 changes: 166 additions & 0 deletions packages/orchestration/test/examples/stake-ica.contract.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
import { test } from '@agoric/zoe/tools/prepare-test-env-ava.js';

import { setUpZoeForTest } from '@agoric/zoe/tools/setup-zoe.js';
import { heapVowE as E } from '@agoric/vow/vat.js';
import path from 'path';
import { makeNotifierFromSubscriber } from '@agoric/notifier';
import type { Installation } from '@agoric/zoe/src/zoeService/utils.js';
import {
QueryBalanceRequest,
QueryBalanceResponse,
} from '@agoric/cosmic-proto/cosmos/bank/v1beta1/query.js';
import { commonSetup } from '../supports.js';
import { type StakeIcaTerms } from '../../src/examples/stakeIca.contract.js';
import fetchedChainInfo from '../../src/fetched-chain-info.js';
import {
buildQueryPacketString,
buildQueryResponseString,
} from '../../tools/ibc-mocks.js';
import type { CosmosChainInfo } from '../../src/cosmos-api.js';

const dirname = path.dirname(new URL(import.meta.url).pathname);

const contractFile = `${dirname}/../../src/examples/stakeIca.contract.js`;
type StartFn =
typeof import('@agoric/orchestration/src/examples/stakeIca.contract.js').start;

const getChainTerms = (
chainName: keyof typeof fetchedChainInfo,
chainInfo: Record<string, CosmosChainInfo> = fetchedChainInfo,
): StakeIcaTerms => {
const { chainId, stakingTokens, icqEnabled } = chainInfo[chainName];
const agoricConns = chainInfo.agoric.connections!;
if (!stakingTokens?.[0].denom) {
throw Error(`Bond denom not found for chainName: ${chainName}`);
}
return {
chainId,
hostConnectionId: agoricConns[chainId].counterparty.connection_id,
controllerConnectionId: agoricConns[chainId].id,
bondDenom: stakingTokens[0].denom,
icqEnabled: !!icqEnabled,
};
};

const startContract = async ({
orchestration,
timer,
marshaller,
storage,
issuerKeywordRecord = undefined,
terms = getChainTerms('cosmoshub'),
storagePath = 'stakeAtom',
}) => {
const { zoe, bundleAndInstall } = await setUpZoeForTest();
const installation: Installation<StartFn> =
await bundleAndInstall(contractFile);

const { publicFacet } = await E(zoe).startInstance(
installation,
issuerKeywordRecord,
terms,
{
marshaller,
orchestration,
storageNode: storage.rootNode.makeChildNode(storagePath),
timer,
},
);
return { publicFacet, zoe };
};

test('makeAccount, getAddress, getBalances, getBalance', async t => {
const { bootstrap } = await commonSetup(t);
{
// stakeAtom
const { publicFacet } = await startContract(bootstrap);

t.log('make an ICA account');
const account = await E(publicFacet).makeAccount();
t.truthy(account, 'account is returned');
const chainAddress = await E(account).getAddress();
t.regex(chainAddress.address, /cosmos1/);
t.like(chainAddress, { chainId: 'cosmoshub-4', addressEncoding: 'bech32' });

await t.throwsAsync(E(account).getBalances(), {
message: 'not yet implemented',
});

await t.throwsAsync(E(account).getBalance('uatom'), {
message: 'Queries not available for chain "cosmoshub-4"',
});

const accountP = E(publicFacet).makeAccount();
const { address: address2 } = await E(accountP).getAddress();
t.regex(address2, /cosmos1/);
t.not(chainAddress.address, address2, 'account addresses are unique');
}
{
// stakeOsmo
const { ibcBridge } = bootstrap;
await E(ibcBridge).setAddressPrefix('osmo');
const { publicFacet } = await startContract({
...bootstrap,
terms: getChainTerms('osmosis'),
storagePath: 'stakeOsmo',
});

const account = await E(publicFacet).makeAccount();
t.truthy(account, 'account is returned');
const chainAddress = await E(account).getAddress();
t.regex(chainAddress.address, /osmo1/);
t.like(chainAddress, { chainId: 'osmosis-1' });

const buildMocks = () => {
const balanceReq = buildQueryPacketString([
QueryBalanceRequest.toProtoMsg({
address: chainAddress.address,
denom: 'uosmo',
}),
]);
const balanceResp = buildQueryResponseString(QueryBalanceResponse, {
balance: { amount: '0', denom: 'uosmo' },
});
return { [balanceReq]: balanceResp };
};
await E(ibcBridge).setMockAck(buildMocks());

const balance = await E(account).getBalance('uosmo');
t.deepEqual(balance, { denom: 'uosmo', value: 0n });
}

t.snapshot(bootstrap.storage.data.entries(), 'accounts in vstorage');
});

test('makeAccountInvitationMaker', async t => {
const { bootstrap } = await commonSetup(t);
const { publicFacet, zoe } = await startContract(bootstrap);
const inv = await E(publicFacet).makeAccountInvitationMaker();
t.log('make an offer for ICA account');

const seat = await E(zoe).offer(inv);
const offerResult = await E(seat).getOfferResult();

t.like(offerResult, {
publicSubscribers: {
account: {
description: 'Staking Account holder status',
},
},
});

const accountNotifier = makeNotifierFromSubscriber(
offerResult.publicSubscribers.account.subscriber,
);
const storageUpdate = await E(accountNotifier).getUpdateSince();
t.deepEqual(storageUpdate, {
updateCount: 1n,
value: '',
});

const vstorageEntry = bootstrap.storage.data.get(
'mockChainStorageRoot.stakeAtom.accounts.cosmos1test',
);
t.truthy(vstorageEntry, 'vstorage account entry created');
t.is(bootstrap.marshaller.fromCapData(JSON.parse(vstorageEntry!)), '');
});
Loading

0 comments on commit 5d7774e

Please sign in to comment.