Skip to content

Commit

Permalink
feat(orchestration): add support for queries (icq/v1)
Browse files Browse the repository at this point in the history
  • Loading branch information
0xpatrickdev committed May 3, 2024
1 parent ea8e6d3 commit 79b5d0f
Show file tree
Hide file tree
Showing 17 changed files with 758 additions and 118 deletions.
130 changes: 124 additions & 6 deletions packages/boot/test/bootstrapTests/test-vat-orchestration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,20 @@ import { test as anyTest } from '@agoric/zoe/tools/prepare-test-env-ava.js';
import type { ExecutionContext, TestFn } from 'ava';

import type { AnyJson } from '@agoric/cosmic-proto';
import { toRequestQueryJson } from '@agoric/cosmic-proto';
import {
QueryBalanceRequest,
QueryBalanceResponse,
} from '@agoric/cosmic-proto/cosmos/bank/v1beta1/query.js';
import { Any } from '@agoric/cosmic-proto/google/protobuf/any.js';
import {
MsgDelegate,
MsgDelegateResponse,
} from '@agoric/cosmic-proto/cosmos/staking/v1beta1/tx.js';
import { Any } from '@agoric/cosmic-proto/google/protobuf/any.js';
import type { OrchestrationService } from '@agoric/orchestration';
import type {
OrchestrationService,
ICQConnection,
} from '@agoric/orchestration';
import { decodeBase64 } from '@endo/base64';
import { M, matches } from '@endo/patterns';
import { makeWalletFactoryContext } from './walletFactory.ts';
Expand All @@ -19,9 +27,9 @@ 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).
* To update, pass the message into `makeTxPacket` or `makeQueryPacket` 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).
*/
Expand All @@ -32,6 +40,12 @@ const delegateMsgSuccess = Any.toJSON(
amount: { denom: 'uatom', amount: '10' },
}),
) as AnyJson;
const balanceQuery = toRequestQueryJson(
QueryBalanceRequest.toProtoMsg({
address: 'cosmos1test',
denom: 'uatom',
}),
);

test.before(async t => {
t.context = await makeTestContext(t);
Expand Down Expand Up @@ -133,7 +147,7 @@ test('ICA connection can send msg with proto3', async t => {
// @ts-expect-error intentional
await t.throwsAsync(EV(account).executeEncodedTx('malformed'), {
message:
'In "executeEncodedTx" method of (ChainAccount account): arg 0: string "malformed" - Must be a copyArray',
'In "executeEncodedTx" method of (ChainAccountKit account): arg 0: string "malformed" - Must be a copyArray',
});

const txSuccess = await EV(account).executeEncodedTx([delegateMsgSuccess]);
Expand Down Expand Up @@ -173,3 +187,107 @@ test('ICA connection can send msg with proto3', async t => {
message: 'ABCI code: 5: error handling packet: see events for details',
});
});

test('Query connection can be created', async t => {
const {
runUtils: { EV },
} = t.context;

type Powers = { orchestration: OrchestrationService };
const contract = async ({ orchestration }: Powers) => {
const connection =
await EV(orchestration).provideICQConnection('connection-0');
t.log('Query Connection', connection);
t.truthy(connection, 'provideICQConnection returns a connection');
t.truthy(
matches(connection, M.remotable('ICQConnection')),
'ICQConnection is a remotable',
);
};

// core eval context
{
const orchestration: OrchestrationService =
await EV.vat('bootstrap').consumeItem('orchestration');
await contract({ orchestration });
}
});

test('Query connection can send a query', async t => {
const {
runUtils: { EV },
} = t.context;

type Powers = { orchestration: OrchestrationService };
const contract = async ({ orchestration }: Powers) => {
const queryConnection: ICQConnection =
await EV(orchestration).provideICQConnection('connection-0');

const [result] = await EV(queryConnection).query([balanceQuery]);
t.is(result.code, 0);
t.is(typeof result.height, 'bigint');
t.deepEqual(QueryBalanceResponse.decode(decodeBase64(result.key)), {
balance: {
amount: '0',
denom: 'uatom',
},
});

const results = await EV(queryConnection).query([
balanceQuery,
balanceQuery,
]);
t.is(results.length, 2);
for (const { key } of results) {
t.deepEqual(QueryBalanceResponse.decode(decodeBase64(key)), {
balance: {
amount: '0',
denom: 'uatom',
},
});
}

await t.throwsAsync(
EV(queryConnection).query([
{ ...balanceQuery, path: '/cosmos.bank.v1beta1.QueryBalanceRequest' },
]),
{
message: 'ABCI code: 4: error handling packet: see events for details',
},
'Use gRPC method to query, not protobuf typeUrl',
);
};

// core eval context
{
const orchestration: OrchestrationService =
await EV.vat('bootstrap').consumeItem('orchestration');
await contract({ orchestration });
}
});

test('provideICQConnection is idempotent', async t => {
const {
runUtils: { EV },
} = t.context;
const orchestration: OrchestrationService =
await EV.vat('bootstrap').consumeItem('orchestration');

const queryConn0 =
await EV(orchestration).provideICQConnection('connection-0');
const queryConn1 =
await EV(orchestration).provideICQConnection('connection-1');
const queryConn02 =
await EV(orchestration).provideICQConnection('connection-0');

const [addr0, addr1, addr02] = await Promise.all([
EV(queryConn0).getRemoteAddress(),
EV(queryConn1).getRemoteAddress(),
EV(queryConn02).getRemoteAddress(),
]);
t.is(addr0, addr02);
t.not(addr0, addr1);

const [result] = await EV(queryConn02).query([balanceQuery]);
t.is(result.code, 0, 'ICQConnectionKit from MapStore state can send queries');
});
51 changes: 41 additions & 10 deletions packages/boot/tools/ibc/mocks.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,22 @@ const responses = {
// {"result":"+/cosmos.staking.v1beta1.MsgDelegateResponse"}
delegate:
'eyJyZXN1bHQiOiJFaTBLS3k5amIzTnRiM011YzNSaGEybHVaeTUyTVdKbGRHRXhMazF6WjBSbGJHVm5ZWFJsVW1WemNHOXVjMlU9In0=',
// XXX what does code 5 mean? are there other codes?
// '{"result":{"data":{"balance":{"amount":"0","denom":"uatom"}}}}'
queryBalance:
'eyJyZXN1bHQiOiJleUprWVhSaElqb2lRMmMwZVVSQmIwdERaMVl4V1ZoU2RtSlNTVUpOUVQwOUluMD0ifQ==',
// {"result":{"data":[{"balance":{"amount":"0","denom":"uatom"}},{"balance":{"amount":"0","denom":"uatom"}}]}}
queryBalanceMulti:
'eyJyZXN1bHQiOiJleUprWVhSaElqb2lRMmMwZVVSQmIwdERaMVl4V1ZoU2RtSlNTVUpOUVc5UFRXZDNTME5uYjBaa1YwWXdZakl3VTBGVVFUMGlmUT09In0=',
// '{"result":{"data":{"balance":{"amount":"0","denom":"some-invalid-denom"}}}}' (does not result in an error)
// eyJkYXRhIjoiQ2hzeUdRb1hDaEp6YjIxbExXbHVkbUZzYVdRdFpHVnViMjBTQVRBPSJ9
queryBalanceUnknownDenom:
'eyJyZXN1bHQiOiJleUprWVhSaElqb2lRMmh6ZVVkUmIxaERhRXA2WWpJeGJFeFhiSFZrYlVaellWZFJkRnBIVm5WaU1qQlRRVlJCUFNKOSJ9',
// {"error":"ABCI code: 4: error handling packet: see events for details"}
error4:
'eyJlcnJvciI6IkFCQ0kgY29kZTogNDogZXJyb3IgaGFuZGxpbmcgcGFja2V0OiBzZWUgZXZlbnRzIGZvciBkZXRhaWxzIn0=',
// XXX what does code 5 mean? are there other codes? I have observed 1, 4, 5, 7
// {"error":"ABCI code: 5: error handling packet: see events for details"}
error:
error5:
'eyJlcnJvciI6IkFCQ0kgY29kZTogNTogZXJyb3IgaGFuZGxpbmcgcGFja2V0OiBzZWUgZXZlbnRzIGZvciBkZXRhaWxzIn0=',
};

Expand All @@ -18,13 +31,33 @@ export const protoMsgMocks = {
msg: 'eyJ0eXBlIjoxLCJkYXRhIjoiQ2xVS0l5OWpiM050YjNNdWMzUmhhMmx1Wnk1Mk1XSmxkR0V4TGsxelowUmxiR1ZuWVhSbEVpNEtDMk52YzIxdmN6RjBaWE4wRWhKamIzTnRiM04yWVd4dmNHVnlNWFJsYzNRYUN3b0ZkV0YwYjIwU0FqRXciLCJtZW1vIjoiIn0=',
ack: responses.delegate,
},
// QueryBalanceRequest (/cosmos.bank.v1beta1.Query/Balance) of uatom for cosmos1test
queryBalance: {
msg: 'eyJkYXRhIjoiQ2pvS0ZBb0xZMjl6Ylc5ek1YUmxjM1FTQlhWaGRHOXRFaUl2WTI5emJXOXpMbUpoYm1zdWRqRmlaWFJoTVM1UmRXVnllUzlDWVd4aGJtTmwiLCJtZW1vIjoiIn0=',
ack: responses.queryBalance,
},
// QueryBalanceRequest of uatom for cosmos1test, repeated twice
queryBalanceMulti: {
msg: 'eyJkYXRhIjoiQ2pvS0ZBb0xZMjl6Ylc5ek1YUmxjM1FTQlhWaGRHOXRFaUl2WTI5emJXOXpMbUpoYm1zdWRqRmlaWFJoTVM1UmRXVnllUzlDWVd4aGJtTmxDam9LRkFvTFkyOXpiVzl6TVhSbGMzUVNCWFZoZEc5dEVpSXZZMjl6Ylc5ekxtSmhibXN1ZGpGaVpYUmhNUzVSZFdWeWVTOUNZV3hoYm1ObCIsIm1lbW8iOiIifQ==',
ack: responses.queryBalanceMulti,
},
// QueryBalanceRequest of 'some-invalid-denom' for cosmos1test
queryBalanceUnknownDenom: {
msg: 'eyJkYXRhIjoiQ2tjS0lRb0xZMjl6Ylc5ek1YUmxjM1FTRW5OdmJXVXRhVzUyWVd4cFpDMWtaVzV2YlJJaUwyTnZjMjF2Y3k1aVlXNXJMbll4WW1WMFlURXVVWFZsY25rdlFtRnNZVzVqWlE9PSIsIm1lbW8iOiIifQ==',
ack: responses.queryBalanceUnknownDenom,
},
// Query for /cosmos.bank.v1beta1.QueryBalanceRequest
queryUnknownPath: {
msg: 'eyJkYXRhIjoiQ2tBS0ZBb0xZMjl6Ylc5ek1YUmxjM1FTQlhWaGRHOXRFaWd2WTI5emJXOXpMbUpoYm1zdWRqRmlaWFJoTVM1UmRXVnllVUpoYkdGdVkyVlNaWEYxWlhOMCIsIm1lbW8iOiIifQ==',
ack: responses.error4,
},
// 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,
ack: responses.error5,
},
};

Expand Down Expand Up @@ -60,15 +93,13 @@ export const icaMocks = {
* @returns {IBCEvent<'channelOpenAck'>}
*/
channelOpenAck: obj => {
// Fake a channel IDs from port suffixes. _Ports have no relation to channels._
// Fake a channel IDs from port suffixes. _Ports have no relation to channels, and hosts
// and controllers will likely have different channel IDs for the same channel._
const mocklID = Number(obj.packet.source_port.split('-').at(-1));
/** @type {IBCChannelID} */
const mockLocalChannelID = `channel-${Number(
obj?.packet?.source_port?.split('-')?.at(-1),
)}`;
const mockLocalChannelID = `channel-${mocklID}`;
/** @type {IBCChannelID} */
const mockRemoteChannelID = `channel-${Number(
obj?.packet?.destination_port?.split('-')?.at(-1),
)}`;
const mockRemoteChannelID = `channel-${mocklID}`;

return {
type: 'IBC_EVENT',
Expand Down
37 changes: 27 additions & 10 deletions packages/boot/tools/supports.ts
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,9 @@ export const makeSwingsetTestKit = async (

const makeAckEvent = (obj: IBCMethod<'sendPacket'>, ack: string) => {
ibcSequenceNonce += 1;
return icaMocks.ackPacket(obj, ibcSequenceNonce, ack);
const msg = icaMocks.ackPacket(obj, ibcSequenceNonce, ack);
inbound(BridgeId.DIBC, msg);
return msg.packet;
};
/**
* Mock the bridge outbound handler. The real one is implemented in Golang so
Expand Down Expand Up @@ -376,22 +378,37 @@ export const makeSwingsetTestKit = async (
case 'sendPacket':
switch (obj.packet.data) {
case protoMsgMocks.delegate.msg: {
const msg = makeAckEvent(obj, protoMsgMocks.delegate.ack);
inbound(BridgeId.DIBC, msg);
return msg.packet;
return makeAckEvent(obj, protoMsgMocks.delegate.ack);
}
case protoMsgMocks.delegateWithOpts.msg: {
const msg = makeAckEvent(
return makeAckEvent(
obj,
protoMsgMocks.delegateWithOpts.ack,
);
inbound(BridgeId.DIBC, msg);
return msg.packet;
}
case protoMsgMocks.queryBalance.msg: {
return makeAckEvent(obj, protoMsgMocks.queryBalance.ack);
}
case protoMsgMocks.queryUnknownPath.msg: {
return makeAckEvent(
obj,
protoMsgMocks.queryUnknownPath.ack,
);
}
case protoMsgMocks.queryBalanceMulti.msg: {
return makeAckEvent(
obj,
protoMsgMocks.queryBalanceMulti.ack,
);
}
case protoMsgMocks.queryBalanceUnknownDenom.msg: {
return makeAckEvent(
obj,
protoMsgMocks.queryBalanceUnknownDenom.ack,
);
}
default: {
const msg = makeAckEvent(obj, protoMsgMocks.error.ack);
inbound(BridgeId.DIBC, msg);
return msg.packet;
return makeAckEvent(obj, protoMsgMocks.error.ack);
}
}
default:
Expand Down
36 changes: 30 additions & 6 deletions packages/cosmic-proto/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,17 +32,21 @@
"types": "./dist/codegen/cosmos/*.d.ts",
"default": "./dist/codegen/cosmos/*.js"
},
"./cosmos/tx/v1beta1/tx.js": {
"types": "./dist/codegen/cosmos/tx/v1beta1/tx.d.ts",
"default": "./dist/codegen/cosmos/tx/v1beta1/tx.js"
"./cosmos/bank/v1beta1/query.js": {
"types": "./dist/codegen/cosmos/bank/v1beta1/query.d.ts",
"default": "./dist/codegen/cosmos/bank/v1beta1/query.js"
},
"./cosmos/distribution/v1beta1/tx.js": {
"types": "./dist/codegen/cosmos/distribution/v1beta1/tx.d.ts",
"default": "./dist/codegen/cosmos/distribution/v1beta1/tx.js"
},
"./cosmos/staking/v1beta1/tx.js": {
"types": "./dist/codegen/cosmos/staking/v1beta1/tx.d.ts",
"default": "./dist/codegen/cosmos/staking/v1beta1/tx.js"
},
"./cosmos/distribution/v1beta1/tx.js": {
"types": "./dist/codegen/cosmos/distribution/v1beta1/tx.d.ts",
"default": "./dist/codegen/cosmos/distribution/v1beta1/tx.js"
"./cosmos/tx/v1beta1/tx.js": {
"types": "./dist/codegen/cosmos/tx/v1beta1/tx.d.ts",
"default": "./dist/codegen/cosmos/tx/v1beta1/tx.js"
},
"./google/*.js": {
"types": "./dist/codegen/google/*.d.ts",
Expand All @@ -56,6 +60,18 @@
"types": "./dist/codegen/ibc/*.d.ts",
"default": "./dist/codegen/ibc/*.js"
},
"./ibc/applications/interchain_accounts/v1/packet.js": {
"types": "./dist/codegen/ibc/applications/interchain_accounts/v1/packet.d.ts",
"default": "./dist/codegen/ibc/applications/interchain_accounts/v1/packet.js"
},
"./icq/*.js": {
"types": "./dist/codegen/icq/*.d.ts",
"default": "./dist/codegen/icq/v1/*.js"
},
"./icq/v1/packet.js": {
"types": "./dist/codegen/icq/v1/packet.d.ts",
"default": "./dist/codegen/icq/v1/packet.js"
},
"./swingset/msgs.js": {
"types": "./dist/codegen/agoric/swingset/msgs.d.ts",
"default": "./dist/codegen/agoric/swingset/msgs.js"
Expand All @@ -68,6 +84,14 @@
"types": "./dist/codegen/agoric/swingset/swingset.d.ts",
"default": "./dist/codegen/agoric/swingset/swingset.js"
},
"./tendermint/*.js": {
"types": "./dist/codegen/tendermint/*.d.ts",
"default": "./dist/codegen/tendermint/*.js"
},
"./tendermint/abci/types.js": {
"types": "./dist/codegen/tendermint/abci/types.d.ts",
"default": "./dist/codegen/tendermint/abci/types.js"
},
"./vstorage/query.js": {
"types": "./dist/codegen/agoric/vstorage/query.d.ts",
"default": "./dist/codegen/agoric/vstorage/query.js"
Expand Down
3 changes: 1 addition & 2 deletions packages/orchestration/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
// eslint-disable-next-line import/export
export * from './src/types.js';
export * from './src/utils/address.js';
export * from './src/utils/packet.js';
export * from './src/service.js';
export * from './src/typeGuards.js';
Loading

0 comments on commit 79b5d0f

Please sign in to comment.