Skip to content

Commit

Permalink
Merge pull request #9195 from Agoric/8881-ica-send-message
Browse files Browse the repository at this point in the history
feat(orchestration): send message
  • Loading branch information
mergify[bot] authored Apr 9, 2024
2 parents c74c628 + 54d830f commit e468caa
Show file tree
Hide file tree
Showing 22 changed files with 980 additions and 130 deletions.
1 change: 1 addition & 0 deletions packages/boot/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
"@agoric/governance": "^0.10.3",
"@agoric/store": "^0.9.2",
"@agoric/swingset-liveslots": "^0.10.2",
"@endo/base64": "^1.0.4",
"@endo/patterns": "^1.3.1",
"ava": "^5.3.0",
"c8": "^9.1.0",
Expand Down
72 changes: 70 additions & 2 deletions packages/boot/test/bootstrapTests/test-orchestration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { test as anyTest } from '@agoric/zoe/tools/prepare-test-env-ava.js';
import type { TestFn } from 'ava';

import { Fail } from '@agoric/assert';
import { AmountMath } from '@agoric/ertp';
import type { start as stakeBldStart } from '@agoric/orchestration/src/contracts/stakeBld.contract.js';
import type { Instance } from '@agoric/zoe/src/zoeService/utils.js';
import { M, matches } from '@endo/patterns';
Expand Down Expand Up @@ -60,7 +61,6 @@ test.serial('stakeBld', async t => {

const current = await wd.getCurrentWalletRecord();
const latest = await wd.getLatestUpdateRecord();
console.log({ current, latest });
t.like(current, {
offerToPublicSubscriberPaths: [
// TODO publish something useful
Expand Down Expand Up @@ -88,7 +88,7 @@ test.serial('stakeBld', async t => {
});
});

test.serial('stakeAtom', async t => {
test.serial('stakeAtom - repl-style', async t => {
const {
buildProposal,
evalProposal,
Expand Down Expand Up @@ -120,4 +120,72 @@ test.serial('stakeAtom', async t => {
matches(account, M.remotable('ChainAccount')),
'account is a remotable',
);

const atomBrand = await EV(agoricNames).lookup('brand', 'ATOM');
const atomAmount = AmountMath.make(atomBrand, 10n);

const res = await EV(account).delegate('cosmosvaloper1test', atomAmount);
t.is(res, 'Success', 'delegate returns Success');
});

test.serial('stakeAtom - smart wallet', async t => {
const { agoricNamesRemotes } = t.context;

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

await wd.executeOffer({
id: 'request-account',
invitationSpec: {
source: 'agoricContract',
instancePath: ['stakeAtom'],
callPipe: [['makeCreateAccountInvitation']],
},
proposal: {},
});
t.like(wd.getCurrentWalletRecord(), {
offerToPublicSubscriberPaths: [
['request-account', { account: 'published.stakeAtom' }],
],
});
t.like(wd.getLatestUpdateRecord(), {
status: { id: 'request-account', numWantsSatisfied: 1 },
});

const { ATOM } = agoricNamesRemotes.brand;
ATOM || Fail`ATOM missing from agoricNames`;

await t.notThrowsAsync(
wd.executeOffer({
id: 'request-delegate-success',
invitationSpec: {
source: 'continuing',
previousOffer: 'request-account',
invitationMakerName: 'Delegate',
invitationArgs: ['cosmosvaloper1test', { brand: ATOM, value: 10n }],
},
proposal: {},
}),
);
t.like(wd.getLatestUpdateRecord(), {
status: { id: 'request-delegate-success', numWantsSatisfied: 1 },
});

await t.throwsAsync(
wd.executeOffer({
id: 'request-delegate-fail',
invitationSpec: {
source: 'continuing',
previousOffer: 'request-account',
invitationMakerName: 'Delegate',
invitationArgs: ['cosmosvaloper1fail', { brand: ATOM, value: 10n }],
},
proposal: {},
}),
{
message: 'ABCI code: 5: error handling packet: see events for details',
},
'delegate fails with invalid validator',
);
});
83 changes: 82 additions & 1 deletion packages/boot/test/bootstrapTests/test-vat-orchestration.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
import { test as anyTest } from '@agoric/zoe/tools/prepare-test-env-ava.js';
import type { ExecutionContext, TestFn } from 'ava';
import {
MsgDelegate,
MsgDelegateResponse,
} from '@agoric/cosmic-proto/cosmos/staking/v1beta1/tx.js';
import { decodeBase64 } from '@endo/base64';
import { M, matches } from '@endo/patterns';
import { txToBase64 } from '@agoric/orchestration';
import { makeWalletFactoryContext } from './walletFactory.ts';

const makeTestContext = async (t: ExecutionContext) =>
Expand All @@ -9,6 +15,21 @@ const makeTestContext = async (t: ExecutionContext) =>
type DefaultTestContext = Awaited<ReturnType<typeof makeTestContext>>;
const test: TestFn<DefaultTestContext> = anyTest;

/**
* To update, pass the message into `makeTxPacket` from `@agoric/orchestration`,
* and paste the resulting `data` key into `protoMsgMocks` in
* [mocks.js](../../tools/ibc/mocks.js).
* If adding a new msg, reference the mock in the `sendPacket` switch statement
* in [supports.ts](../../tools/supports.ts).
*/
const delegateMsgSuccess = txToBase64(
MsgDelegate.toProtoMsg({
delegatorAddress: 'cosmos1test',
validatorAddress: 'cosmosvaloper1test',
amount: { denom: 'uatom', amount: '10' },
}),
);

test.before(async t => {
t.context = await makeTestContext(t);

Expand Down Expand Up @@ -62,7 +83,7 @@ test('createAccount returns an ICA connection', async t => {
);
t.regex(remoteAddress, /icahost/);
t.regex(localAddress, /icacontroller/);
t.regex(accountAddress, /osmo1/);
t.regex(accountAddress, /cosmos1/);
t.truthy(matches(port, M.remotable('Port')));
t.log('ICA Account Addresses', {
remoteAddress,
Expand All @@ -86,4 +107,64 @@ test('ICA connection can be closed', async t => {

const res = await EV(account).close();
t.is(res, 'Connection closed');

await t.throwsAsync(EV(account).executeEncodedTx([delegateMsgSuccess]), {
message: 'Connection closed',
});
});

test('ICA connection can send msg with proto3', async t => {
const {
runUtils: { EV },
} = t.context;

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

/** @type {ChainAccount} */
const account = await EV(orchestration).createAccount(
'connection-0',
'connection-0',
);
t.truthy(account, 'createAccount returns an account');

await t.throwsAsync(EV(account).executeEncodedTx('malformed'), {
message:
'In "executeEncodedTx" method of (ChainAccount account): arg 0: string "malformed" - Must be a copyArray',
});

const txSuccess = await EV(account).executeEncodedTx([delegateMsgSuccess]);
t.is(
txSuccess,
'Ei0KKy9jb3Ntb3Muc3Rha2luZy52MWJldGExLk1zZ0RlbGVnYXRlUmVzcG9uc2U=', // cosmos.staking.v1beta1.MsgDelegateResponse
'delegateMsgSuccess',
);
t.deepEqual(
MsgDelegateResponse.decode(decodeBase64(txSuccess)),
{},
'success tx',
);

const txWithOptions = await EV(account).executeEncodedTx(
[delegateMsgSuccess],
{
memo: 'TESTING',
timeoutHeight: 1_000_000_000n,
},
);
t.deepEqual(
MsgDelegateResponse.decode(decodeBase64(txWithOptions)),
{},
'txWithOptions',
);

const delegateMsgFailure = txToBase64(
MsgDelegate.toProtoMsg({
delegatorAddress: 'cosmos1fail',
validatorAddress: 'cosmosvaloper1fail',
amount: { denom: 'uatom', amount: '10' },
}),
);
await t.throwsAsync(EV(account).executeEncodedTx([delegateMsgFailure]), {
message: 'ABCI code: 5: error handling packet: see events for details',
});
});
31 changes: 31 additions & 0 deletions packages/boot/test/tools/ibc/test-mocks.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// @ts-check

import { test } from '@agoric/zoe/tools/prepare-test-env-ava.js';
import { addParamsIfJsonVersion } from '../../../tools/ibc/mocks.js';

test('addParamsToVersion', t => {
const params = { address: 'cosmos1234' };
const scenarios = [
{
version:
'{"version":"ics27-1","controllerConnectionId":"connection-0","hostConnectionId":"connection-1","address":"","encoding":"proto3","txType":"sdk_multi_msg"}',
expected:
'{"version":"ics27-1","controllerConnectionId":"connection-0","hostConnectionId":"connection-1","address":"cosmos1234","encoding":"proto3","txType":"sdk_multi_msg"}',
message: 'ICA: add mock negotiated address to version string',
},
{
version: 'ics20-1',
expected: 'ics20-1',
message: 'preserves existing transfer version',
},
{
version: 'icq-1',
expected: 'icq-1',
message: 'preserves existing query version',
},
];

for (const { version, expected, message } of scenarios) {
t.is(addParamsIfJsonVersion(version, params), expected, message);
}
});
112 changes: 103 additions & 9 deletions packages/boot/tools/ibc/mocks.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,51 @@
// @ts-check

/** @import { IBCChannelID, IBCMethod, IBCEvent } from '@agoric/vats'; */

const responses = {
// {"result":"+/cosmos.staking.v1beta1.MsgDelegateResponse"}
delegate:
'eyJyZXN1bHQiOiJFaTBLS3k5amIzTnRiM011YzNSaGEybHVaeTUyTVdKbGRHRXhMazF6WjBSbGJHVm5ZWFJsVW1WemNHOXVjMlU9In0=',
// XXX what does code 5 mean? are there other codes?
// {"error":"ABCI code: 5: error handling packet: see events for details"}
error:
'eyJlcnJvciI6IkFCQ0kgY29kZTogNTogZXJyb3IgaGFuZGxpbmcgcGFja2V0OiBzZWUgZXZlbnRzIGZvciBkZXRhaWxzIn0=',
};

export const protoMsgMocks = {
// MsgDelegate 10uatom from cosmos1test to cosmosvaloper1test
delegate: {
msg: 'eyJ0eXBlIjoxLCJkYXRhIjoiQ2xVS0l5OWpiM050YjNNdWMzUmhhMmx1Wnk1Mk1XSmxkR0V4TGsxelowUmxiR1ZuWVhSbEVpNEtDMk52YzIxdmN6RjBaWE4wRWhKamIzTnRiM04yWVd4dmNHVnlNWFJsYzNRYUN3b0ZkV0YwYjIwU0FqRXciLCJtZW1vIjoiIn0=',
ack: responses.delegate,
},
// MsgDelegate 10uatom from cosmos1test to cosmosvaloper1test with memo: 'TESTING' and timeoutHeight: 1_000_000_000n
delegateWithOpts: {
msg: 'eyJ0eXBlIjoxLCJkYXRhIjoiQ2xVS0l5OWpiM050YjNNdWMzUmhhMmx1Wnk1Mk1XSmxkR0V4TGsxelowUmxiR1ZuWVhSbEVpNEtDMk52YzIxdmN6RjBaWE4wRWhKamIzTnRiM04yWVd4dmNHVnlNWFJsYzNRYUN3b0ZkV0YwYjIwU0FqRXdFZ2RVUlZOVVNVNUhHSUNVNjl3RCIsIm1lbW8iOiIifQ==',
ack: responses.delegate,
},
error: {
ack: responses.error,
},
};

/**
* Adds parameters to IBC version string if it's JSON
* @param {string} version version or JSON version string
* @param {{ address: string; }} params
* @returns {string}
*/
export const addParamsIfJsonVersion = (version, params) => {
try {
const parsed = JSON.parse(version);
return JSON.stringify({
...parsed,
...params,
});
} catch {
return version;
}
};

/**
* mock bridgeInbound events for ICA (ICS-27) flow
* see [./ics27-1.md](./ics27-1.md) for more details
Expand All @@ -6,23 +54,69 @@
* and end e2e testing with sim chains (v16: IBC fromBridge logs)
*/
export const icaMocks = {
startChannelOpenInit: {
// ICA Channel Creation
channelOpenAck: obj => ({
/**
* ICA Channel Creation
* @param {IBCMethod<'initOpenExecuted'>} obj
* @returns {IBCEvent<'channelOpenAck'>}
*/
channelOpenAck: obj => {
// Fake a channel IDs from port suffixes. _Ports have no relation to channels._
/** @type {IBCChannelID} */
const mockLocalChannelID = `channel-${Number(
obj?.packet?.source_port?.split('-')?.at(-1),
)}`;
/** @type {IBCChannelID} */
const mockRemoteChannelID = `channel-${Number(
obj?.packet?.destination_port?.split('-')?.at(-1),
)}`;

return {
type: 'IBC_EVENT',
blockHeight: 99,
blockTime: 1711571357,
event: 'channelOpenAck',
portID: obj.packet.source_port,
channelID: 'channel-0',
channelID: mockLocalChannelID,
counterparty: {
port_id: obj.packet.destination_port,
channel_id: 'channel-1',
channel_id: mockRemoteChannelID,
},
counterpartyVersion:
'{"version":"ics27-1","controllerConnectionId":"connection-0","hostConnectionId":"connection-0","address":"osmo1234","encoding":"proto3","txType":"sdk_multi_msg"}',
counterpartyVersion: addParamsIfJsonVersion(obj.version, {
// TODO, parameterize
address: 'cosmos1test',
}),
connectionHops: obj.hops,
}),
// XXX channelOpenAckFailure
order: obj.order,
version: obj.version,
};
},
// TODO channelOpenAckFailure

/**
* ICA Send Packet (Transaction) - MsgDelegate success
* @param {IBCMethod<'sendPacket'>} obj
* @param {number} sequence transaction sequence number
* @param {string} acknowledgement acknowledgement response as base64 encoded bytes
* @returns {IBCEvent<'acknowledgementPacket'>}
*/
ackPacket: (obj, sequence = 1, acknowledgement) => {
return {
acknowledgement,
blockHeight: 289,
blockTime: 1712180320,
event: 'acknowledgementPacket',
packet: {
data: obj.packet.data,
destination_channel: obj.packet.destination_channel,
destination_port: obj.packet.destination_port,
sequence,
source_channel: obj.packet.source_channel,
source_port: obj.packet.source_port,
timeout_height: 0,
timeout_timestamp: 1712183910866313000,
},
relayer: 'agoric1gtkg0g6x8lqc734ht3qe2sdkrfugpdp2h7fuu0',
type: 'IBC_EVENT',
};
},
};
Loading

0 comments on commit e468caa

Please sign in to comment.