Skip to content

Commit

Permalink
test of revising chain info (#9552)
Browse files Browse the repository at this point in the history
refs: #8896

## Description

Extracted from #9534 to focus that PR on multichain and lighten the review. Also it had merge conflicts with master that this resolves.

### Security Considerations
nothing new

### Scaling Considerations
no

### Documentation Considerations
none

### Testing Considerations
new coverage

### Upgrade Considerations
none
  • Loading branch information
mergify[bot] committed Jun 21, 2024
2 parents 788647d + 2f625b9 commit 01a1123
Show file tree
Hide file tree
Showing 10 changed files with 324 additions and 110 deletions.
45 changes: 39 additions & 6 deletions packages/boot/test/bootstrapTests/orchestration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ import { test as anyTest } from '@agoric/zoe/tools/prepare-test-env-ava.js';

import { Fail } from '@agoric/assert';
import { AmountMath } from '@agoric/ertp';
import { documentStorageSchema } from '@agoric/internal/src/storage-test-utils.js';
import type { CosmosValidatorAddress } from '@agoric/orchestration';
import type { start as startStakeIca } from '@agoric/orchestration/src/examples/stakeIca.contract.js';
import type { Instance } from '@agoric/zoe/src/zoeService/utils.js';
import { M, matches } from '@endo/patterns';
import type { TestFn } from 'ava';
import { documentStorageSchema } from '@agoric/internal/src/storage-test-utils.js';
import {
makeWalletFactoryContext,
type WalletFactoryTestContext,
Expand Down Expand Up @@ -56,13 +56,14 @@ test.serial('config', async t => {
'chainConnection',
'cosmoshub-4_juno-1',
);
t.like(connection, {
state: 3,
transferChannel: { portId: 'transfer', state: 3 },
});

t.like(
t.deepEqual(
readLatest(`published.agoricNames.chainConnection.cosmoshub-4_juno-1`),
{
state: 3,
transferChannel: { portId: 'transfer', state: 3 },
},
connection,
);

await documentStorageSchema(t, storage, {
Expand Down Expand Up @@ -230,3 +231,35 @@ test.serial('stakeAtom - smart wallet', async t => {
'delegate fails with invalid validator',
);
});

// XXX rely on .serial to be in sequence, and keep this one last
test.serial('revise chain info', async t => {
const {
buildProposal,
evalProposal,
runUtils: { EV },
} = t.context;

const agoricNames = await EV.vat('bootstrap').consumeItem('agoricNames');

await t.throwsAsync(EV(agoricNames).lookup('chain', 'hot'), {
message: '"nameKey" not found: "hot"',
});

// Revise chain info in agoricNames with the fixture in this script
await evalProposal(
buildProposal('@agoric/builders/scripts/testing/append-chain-info.js'),
);

const hotchain = await EV(agoricNames).lookup('chain', 'hot');
t.deepEqual(hotchain, { allegedName: 'Hot New Chain', chainId: 'hot-1' });

const connection = await EV(agoricNames).lookup(
'chainConnection',
'cosmoshub-4_hot-1',
);
t.like(connection, {
id: 'connection-99',
client_id: '07-tendermint-3',
});
});
4 changes: 2 additions & 2 deletions packages/builders/scripts/inter-protocol/add-STARS.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { defaultProposalBuilder as vaultProposalBuilder } from './add-collateral
import { defaultProposalBuilder as oraclesProposalBuilder } from './price-feed-core.js';

/** @type {import('@agoric/deploy-script-support/src/externalTypes.js').CoreEvalBuilder} */
export const starsVaultProposalBuilder = async powers => {
const starsVaultProposalBuilder = async powers => {
return vaultProposalBuilder(powers, {
interchainAssetOptions: {
// Values for the Stargaze token on Osmosis
Expand All @@ -18,7 +18,7 @@ export const starsVaultProposalBuilder = async powers => {
};

/** @type {import('@agoric/deploy-script-support/src/externalTypes.js').CoreEvalBuilder} */
export const starsOraclesProposalBuilder = async powers => {
const starsOraclesProposalBuilder = async powers => {
return oraclesProposalBuilder(powers, {
AGORIC_INSTANCE_NAME: `STARS-USD price feed`,
IN_BRAND_LOOKUP: ['agoricNames', 'oracleBrand', 'STARS'],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { makeHelpers } from '@agoric/deploy-script-support';
import { getManifestForGame1 } from '@agoric/smart-wallet/test/start-game1-proposal.js';

/** @type {import('@agoric/deploy-script-support/src/externalTypes.js').CoreEvalBuilder} */
export const game1ProposalBuilder = async ({ publishRef, install }) => {
const game1ProposalBuilder = async ({ publishRef, install }) => {
return harden({
sourceSpec: '@agoric/smart-wallet/test/start-game1-proposal.js',
getManifestCall: [
Expand Down
2 changes: 1 addition & 1 deletion packages/builders/scripts/testing/add-LEMONS.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { defaultProposalBuilder as vaultProposalBuilder } from '../inter-protoco
/** @file This is for use in tests in a3p-integration */

/** @type {import('@agoric/deploy-script-support/src/externalTypes.js').CoreEvalBuilder} */
export const starsVaultProposalBuilder = async powers => {
const starsVaultProposalBuilder = async powers => {
return vaultProposalBuilder(powers, {
interchainAssetOptions: {
denom: 'ibc/000C0FFEECAFE000',
Expand Down
2 changes: 1 addition & 1 deletion packages/builders/scripts/testing/add-OLIVES.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { defaultProposalBuilder as vaultProposalBuilder } from '../inter-protoco
/** @file This is for use in tests in a3p-integration */

/** @type {import('@agoric/deploy-script-support/src/externalTypes.js').CoreEvalBuilder} */
export const stars2VaultProposalBuilder = async powers => {
const stars2VaultProposalBuilder = async powers => {
return vaultProposalBuilder(powers, {
interchainAssetOptions: {
denom: 'ibc/111C0FFEECAFE111',
Expand Down
50 changes: 50 additions & 0 deletions packages/builders/scripts/testing/append-chain-info.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/// <reference types="ses" />
import { makeHelpers } from '@agoric/deploy-script-support';

/** @type {Record<string, import('@agoric/orchestration/src/chain-info.js').ChainInfo>} */
const chainInfo = {
hot: {
allegedName: 'Hot New Chain',
chainId: 'hot-1',
connections: {
'cosmoshub-4': {
id: 'connection-99',
client_id: '07-tendermint-3',
counterparty: {
client_id: '07-tendermint-2',
connection_id: 'connection-1',
prefix: {
key_prefix: '',
},
},
state: 3 /* IBCConnectionState.STATE_OPEN */,
transferChannel: {
portId: 'transfer',
channelId: 'channel-1',
counterPartyChannelId: 'channel-1',
counterPartyPortId: 'transfer',
ordering: 1 /* Order.ORDER_UNORDERED */,
state: 3 /* IBCConnectionState.STATE_OPEN */,
version: 'ics20-1',
},
},
},
},
};

/** @type {import('@agoric/deploy-script-support/src/externalTypes.js').CoreEvalBuilder} */
export const defaultProposalBuilder = async () =>
harden({
sourceSpec: '@agoric/orchestration/src/proposals/revise-chain-info.js',
getManifestCall: [
'getManifestForReviseChains',
{
chainInfo,
},
],
});

export default async (homeP, endowments) => {
const { writeCoreEval } = await makeHelpers(homeP, endowments);
await writeCoreEval('revise-chain-info', defaultProposalBuilder);
};
53 changes: 53 additions & 0 deletions packages/builders/scripts/testing/tweak-chain-info.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/// <reference types="ses" />
import { makeHelpers } from '@agoric/deploy-script-support';

/** @type {Record<string, import('@agoric/orchestration/src/chain-info.js').ChainInfo>} */
const chainInfo = {
agoric: {
chainId: 'agoric-4',
},
hot: {
allegedName: 'Hot New Chain',
chainId: 'hot-1',
connections: {
'cosmoshub-4': {
id: 'connection-99',
client_id: '07-tendermint-3',
counterparty: {
client_id: '07-tendermint-2',
connection_id: 'connection-1',
prefix: {
key_prefix: '',
},
},
state: 3 /* IBCConnectionState.STATE_OPEN */,
transferChannel: {
portId: 'transfer',
channelId: 'channel-1',
counterPartyChannelId: 'channel-1',
counterPartyPortId: 'transfer',
ordering: 1 /* Order.ORDER_UNORDERED */,
state: 3 /* IBCConnectionState.STATE_OPEN */,
version: 'ics20-1',
},
},
},
},
};

/** @type {import('@agoric/deploy-script-support/src/externalTypes.js').CoreEvalBuilder} */
export const defaultProposalBuilder = async () =>
harden({
sourceSpec: '@agoric/orchestration/src/proposals/revise-chain-info.js',
getManifestCall: [
'getManifestForReviseChains',
{
chainInfo,
},
],
});

export default async (homeP, endowments) => {
const { writeCoreEval } = await makeHelpers(homeP, endowments);
await writeCoreEval('revise-chain-info', defaultProposalBuilder);
};
101 changes: 2 additions & 99 deletions packages/orchestration/scripts/fetch-chain-info.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,9 @@
#!/usr/bin/env tsx
/** @file Fetch canonical chain info to generate the minimum needed for agoricNames */
import {
State as IBCChannelState,
Order,
} from '@agoric/cosmic-proto/ibc/core/channel/v1/channel.js';
import { State as IBCConnectionState } from '@agoric/cosmic-proto/ibc/core/connection/v1/connection.js';
import type { IBCChannelID, IBCConnectionID } from '@agoric/vats';
import { ChainRegistryClient } from '@chain-registry/client';
import type { IBCInfo } from '@chain-registry/types';
import assert from 'node:assert';
import fsp from 'node:fs/promises';
import prettier from 'prettier';
import type { CosmosChainInfo, IBCConnectionInfo } from '../src/cosmos-api.js';
import { convertChainInfo } from '../src/utils/registry.js';

// XXX script assumes it's run from the package path
// XXX .json would be more apt; UNTIL https://github.com/endojs/endo/issues/2110
Expand Down Expand Up @@ -43,96 +35,7 @@ const client = new ChainRegistryClient({
// chain info, assets and ibc data will be downloaded dynamically by invoking fetchUrls method
await client.fetchUrls();

const chainInfo = {} as Record<string, CosmosChainInfo>;

function toConnectionEntry(ibcInfo: IBCInfo, name: string) {
// IbcInfo encodes the undirected edge as a tuple of (chain_1, chain_2) in alphabetical order
const fromChain1 = ibcInfo.chain_1.chain_name === name;
const [from, to] = fromChain1
? [ibcInfo.chain_1, ibcInfo.chain_2]
: [ibcInfo.chain_2, ibcInfo.chain_1];
assert.equal(from.chain_name, name);
const transferChannels = ibcInfo.channels.filter(
c =>
c.chain_1.port_id === 'transfer' &&
// @ts-expect-error tags does not specify keys
c.tags?.preferred,
);
if (transferChannels.length === 0) {
console.warn(
'no transfer channel for [',
from.chain_name,
to.chain_name,
']',
'(skipping)',
);
return [];
}
if (transferChannels.length > 1) {
console.warn(
'multiple preferred transfer channels [',
from.chain_name,
to.chain_name,
']:',
transferChannels,
'(choosing first)',
);
}
const [channel] = transferChannels;
const [channelFrom, channelTo] = fromChain1
? [channel.chain_2, channel.chain_1]
: [channel.chain_1, channel.chain_2];
const record = {
id: from.connection_id as IBCConnectionID,
client_id: from.client_id,
counterparty: {
client_id: to.client_id,
connection_id: to.connection_id as IBCConnectionID,
prefix: {
key_prefix: 'FIXME',
},
},
state: IBCConnectionState.STATE_OPEN, // XXX presumably
transferChannel: {
channelId: channelFrom.channel_id as IBCChannelID,
portId: channelFrom.port_id,
counterPartyChannelId: channelTo.channel_id as IBCChannelID,
counterPartyPortId: channelTo.port_id,
// FIXME mapping, our guard expects a numerical enum
ordering: Order.ORDER_NONE_UNSPECIFIED,
state: IBCChannelState.STATE_OPEN, // XXX presumably
version: channel.version,
},
} as IBCConnectionInfo;
const destChainId = chainInfo[to.chain_name].chainId;
return [destChainId, record] as const;
}

for (const name of chainNames) {
console.log('processing info', name);

const chain = client.getChain(name);
chainInfo[name] = {
chainId: chain.chain_id,
stakingTokens: chain.staking?.staking_tokens,
// UNTIL https://github.com/Agoric/agoric-sdk/issues/9326
icqEnabled: name === 'osmosis',
};
}

// iterate this after chainInfo is filled out
for (const name of chainNames) {
console.log('processing connections', name);

const ibcData = client.getChainIbcData(name);
const connections = Object.fromEntries(
ibcData
.map(datum => toConnectionEntry(datum, name))
// sort alphabetically for consistency
.sort(([a], [b]) => a?.localeCompare(b)),
);
chainInfo[name] = { ...chainInfo[name], connections };
}
const chainInfo = await convertChainInfo(client);

const record = JSON.stringify(chainInfo, null, 2);
const src = `/** @file Generated by fetch-chain-info.ts */\nexport default /** @type {const} } */ (${record});`;
Expand Down
44 changes: 44 additions & 0 deletions packages/orchestration/src/proposals/revise-chain-info.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { makeTracer } from '@agoric/internal';
import { registerChain } from '../chain-info.js';

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

/** @import {CosmosChainInfo} from '../types.js'; */

/**
* This will add news values AND overwrite any existing values. Voters on a
* proposal of core-eval must be careful not to overwrite any values operating
* in production.
*
* @param {BootstrapPowers} powers
* @param {{ options: { chainInfo: Record<string, CosmosChainInfo> } }} opt
*/
export const reviseChainInfo = async (
{ consume: { agoricNamesAdmin } },
{ options: { chainInfo } },
) => {
trace('init-chainInfo');

assert(chainInfo, 'chainInfo is required');

trace(chainInfo);

// Now register the names
for await (const [name, info] of Object.entries(chainInfo)) {
await registerChain(agoricNamesAdmin, name, info, trace);
}
};
harden(reviseChainInfo);

export const getManifestForReviseChains = (_powers, { chainInfo }) => ({
manifest: {
[reviseChainInfo.name]: {
consume: {
agoricNamesAdmin: true,
},
},
},
options: {
chainInfo,
},
});
Loading

0 comments on commit 01a1123

Please sign in to comment.