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

Problem: Support IBC MsgSubmitMisbehaviour #288

Merged
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
2 changes: 2 additions & 0 deletions lib/src/core/cro.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import { communityPoolSpendProposalV2 } from '../transaction/msg/v2/gov/proposal
import { msgSubmitProposalV2 } from '../transaction/msg/v2/gov/v2.MsgSubmitProposal';
import { msgUpdateClientIBC } from '../transaction/msg/ibc/core/MsgUpdateClient';
import { msgUpgradeClientIBC } from '../transaction/msg/ibc/core/MsgUpgradeClient';
import { msgSubmitMisbehaviourIBC } from '../transaction/msg/ibc/core/MsgSubmitMisbehaviour';

export const CroSDK = function (configs: InitConfigurations) {
ow(configs, 'configs', owCroSDKInitParams);
Expand Down Expand Up @@ -87,6 +88,7 @@ export const CroSDK = function (configs: InitConfigurations) {
MsgCreateClient: msgCreateClientIBC(configs),
MsgUpdateClient: msgUpdateClientIBC(configs),
MsgUpgradeClient: msgUpgradeClientIBC(configs),
MsgSubmitMisbehaviour: msgSubmitMisbehaviourIBC(configs),
},
v2: {
bank: {
Expand Down
1 change: 1 addition & 0 deletions lib/src/cosmos/v1beta1/types/typeurls.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export const typeUrlMappings: {
'/ibc.core.client.v1.MsgCreateClient': ibc.core.client.v1.MsgCreateClient,
'/ibc.core.client.v1.MsgUpdateClient': ibc.core.client.v1.MsgUpdateClient,
'/ibc.core.client.v1.MsgUpgradeClient': ibc.core.client.v1.MsgUpgradeClient,
'/ibc.core.client.v1.MsgSubmitMisbehaviour': ibc.core.client.v1.MsgSubmitMisbehaviour,
};

export interface GeneratedType {
Expand Down
3 changes: 3 additions & 0 deletions lib/src/transaction/common/constants/typeurl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export const COSMOS_MSG_TYPEURL = {
MsgCreateClient: '/ibc.core.client.v1.MsgCreateClient',
MsgUpdateClient: '/ibc.core.client.v1.MsgUpdateClient',
MsgUpgradeClient: '/ibc.core.client.v1.MsgUpgradeClient',
MsgSubmitMisbehaviour: '/ibc.core.client.v1.MsgSubmitMisbehaviour',
},
};

Expand Down Expand Up @@ -99,6 +100,8 @@ export const typeUrlToMsgClassMapping = (cro: any, typeUrl: string) => {
return cro.ibc.MsgUpdateClient;
case COSMOS_MSG_TYPEURL.ibc.MsgUpgradeClient:
return cro.ibc.MsgUpgradeClient;
case COSMOS_MSG_TYPEURL.ibc.MsgSubmitMisbehaviour:
return cro.ibc.MsgSubmitMisbehaviour;

// nft
case COSMOS_MSG_TYPEURL.nft.MsgIssueDenom:
Expand Down
180 changes: 180 additions & 0 deletions lib/src/transaction/msg/ibc/core/MsgSubmitMisbehaviour.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
import 'mocha';
import { expect } from 'chai';
import Big from 'big.js';

import { fuzzyDescribe } from '../../../../test/mocha-fuzzy/suite';
import { Msg } from '../../../../cosmos/v1beta1/types/msg';
import { Secp256k1KeyPair } from '../../../../keypair/secp256k1';
import { Bytes } from '../../../../utils/bytes/bytes';
import { CroSDK, CroNetwork } from '../../../../core/cro';
import { COSMOS_MSG_TYPEURL } from '../../../common/constants/typeurl';

const cro = CroSDK({
network: {
defaultNodeUrl: '',
chainId: 'testnet-croeseid-1',
addressPrefix: 'tcro',
validatorAddressPrefix: 'tcrocncl',
validatorPubKeyPrefix: 'tcrocnclconspub',
coin: {
baseDenom: 'basetcro',
croDenom: 'tcro',
},
bip44Path: {
coinType: 1,
account: 0,
},
rpcUrl: '',
},
});

describe('Testing MsgSubmitMisbehaviour', function () {
fuzzyDescribe('should throw Error when options is invalid', function (fuzzy) {
const anyValidOptions = {
signer: 'tcro15sfupd26sp6qf37ll5q6xuf330k7df9tnvrqht',
};

const testRunner = fuzzy(fuzzy.ObjArg(anyValidOptions));

testRunner(function (options) {
if (options.valid) {
return;
}
expect(() => new cro.ibc.MsgSubmitMisbehaviour(options.value)).to.throw(
'Expected `options` to be of type `object`',
);
});
});

it('Test MsgSubmitMisbehaviour conversion', function () {
const MsgSubmitMisbehaviour = new cro.ibc.MsgSubmitMisbehaviour({
signer: 'tcro15sfupd26sp6qf37ll5q6xuf330k7df9tnvrqht',
clientId: 'clientId',
});

const rawMsg: Msg = {
typeUrl: COSMOS_MSG_TYPEURL.ibc.MsgSubmitMisbehaviour,
value: {
signer: 'tcro15sfupd26sp6qf37ll5q6xuf330k7df9tnvrqht',
clientId: 'clientId',
misbehaviour: undefined,
},
};

expect(MsgSubmitMisbehaviour.toRawMsg()).to.deep.eq(rawMsg);
});

it('Test appendTxBody MsgSubmitMisbehaviour Tx signing', function () {
const anyKeyPair = Secp256k1KeyPair.fromPrivKey(
Bytes.fromHexString('66633d18513bec30dd11a209f1ceb1787aa9e2069d5d47e590174dc9665102b3'),
);

const MsgSubmitMisbehaviour = new cro.ibc.MsgSubmitMisbehaviour({
signer: 'tcro15sfupd26sp6qf37ll5q6xuf330k7df9tnvrqht',
misbehaviour: undefined,
clientId: 'clientId',
});

const anySigner = {
publicKey: anyKeyPair.getPubKey(),
accountNumber: new Big(0),
accountSequence: new Big(2),
};

const rawTx = new cro.RawTransaction();

const signableTx = rawTx.appendMessage(MsgSubmitMisbehaviour).addSigner(anySigner).toSignable();

const signedTx = signableTx.setSignature(0, anyKeyPair.sign(signableTx.toSignDocumentHash(0))).toSigned();

const signedTxHex = signedTx.encode().toHexString();
expect(signedTxHex).to.be.eql(
'0a660a640a292f6962632e636f72652e636c69656e742e76312e4d73675375626d69744d69736265686176696f757212370a08636c69656e7449641a2b7463726f313573667570643236737036716633376c6c3571367875663333306b37646639746e767271687412580a500a460a1f2f636f736d6f732e63727970746f2e736563703235366b312e5075624b657912230a2103fd0d560b6c4aa1ca16721d039a192867c3457e19dad553edb98e7ba88b159c2712040a0208011802120410c09a0c1a40b38eea165bd21573402b2cdf8093e28f6241fc27bcaa68fffbcba93f5e2a8dd01edb3d5c242499083d39bd9c11124d20a11a331eec638498b9abd5974c49563d',
);
});

it('Should validate MsgSubmitMisbehaviour provided addresses with network config', function () {
const params1 = {
signer: 'cosmos1vw4ucaeagtduv5ep4sa95e3aqzqpsk5meda08c',
misbehaviour: undefined,
clientId: 'clientId',
};

expect(() => new cro.ibc.MsgSubmitMisbehaviour(params1)).to.throw(
'Provided `signer` does not match network selected',
);
});

it('Should throw on getting toRawAminoMsg()', function () {
const MsgSubmitMisbehaviour = new cro.ibc.MsgSubmitMisbehaviour({
signer: 'tcro15sfupd26sp6qf37ll5q6xuf330k7df9tnvrqht',
clientId: 'clientId',
});

expect(() => MsgSubmitMisbehaviour.toRawAminoMsg()).to.throw(
'IBC Module not supported under amino encoding scheme',
);
});

describe('fromCosmosJSON', function () {
it('should throw Error if the JSON is not a IBC MsgSubmitMisbehaviour', function () {
const json =
'{ "@type": "/cosmos.bank.v1beta1.MsgCreateValidator", "amount": [{ "denom": "basetcro", "amount": "3478499933290496" }], "from_address": "tcro1x07kkkepfj2hl8etlcuqhej7jj6myqrp48y4hg", "to_address": "tcro184lta2lsyu47vwyp2e8zmtca3k5yq85p6c4vp3" }';
expect(() => cro.ibc.MsgSubmitMisbehaviour.fromCosmosMsgJSON(json, CroNetwork.Testnet)).to.throw(
'Expected /ibc.core.client.v1.MsgSubmitMisbehaviour but got /cosmos.bank.v1beta1.MsgCreateValidator',
);
});
it('should throw on invalid `signer`', function () {
const json = `
{
"@type": "/ibc.core.client.v1.MsgSubmitMisbehaviour",
"signer": "cosmos1u8prj0rj3ur7kr23dhjgyteuq55ntahfuzlf6g",
"client_id": "07-tendermint-33"
}
`;

expect(() => cro.ibc.MsgSubmitMisbehaviour.fromCosmosMsgJSON(json, CroNetwork.Testnet)).to.throw(
'Provided `signer` does not match network selected',
);
});
it('should throw on invalid `clientId`', function () {
const json = `
{
"@type": "/ibc.core.client.v1.MsgSubmitMisbehaviour",
"signer": "cosmos1u8prj0rj3ur7kr23dhjgyteuq55ntahfuzlf6g"
}
`;

expect(() => cro.ibc.MsgSubmitMisbehaviour.fromCosmosMsgJSON(json, CroNetwork.Testnet)).to.throw(
'Expected property `clientId` to be of type `string` but received type `undefined` in object `options`',
);
});
it('should throw on non-empty field `misbehaviour`', function () {
const json = `
{
"@type": "/ibc.core.client.v1.MsgSubmitMisbehaviour",
"signer": "tcro1agr5hwr6gxljf4kpg6fm7l7ehjxtyazg86nef8",
"client_id": "07-tendermint-33",
"misbehaviour": {"type": "07-tendermint-33"}
}
`;

expect(() => cro.ibc.MsgSubmitMisbehaviour.fromCosmosMsgJSON(json, CroNetwork.Testnet)).to.throw(
'IBC MsgSubmitMisbehaviour does not support `misbehaviour` decoding.',
);
});
it('should return the IBC MsgSubmitMisbehaviour corresponding to the JSON', function () {
const json = `{
"@type": "/ibc.core.client.v1.MsgSubmitMisbehaviour",
"signer": "tcro1agr5hwr6gxljf4kpg6fm7l7ehjxtyazg86nef8",
"client_id": "07-tendermint-33"
}
`;

const MsgSubmitMisbehaviour = cro.ibc.MsgSubmitMisbehaviour.fromCosmosMsgJSON(json, CroNetwork.Testnet);
expect(MsgSubmitMisbehaviour.signer).to.eql('tcro1agr5hwr6gxljf4kpg6fm7l7ehjxtyazg86nef8');
expect(MsgSubmitMisbehaviour.clientId).to.eql('07-tendermint-33');
expect(MsgSubmitMisbehaviour.misbehaviour).to.be.null;
});
});
});
110 changes: 110 additions & 0 deletions lib/src/transaction/msg/ibc/core/MsgSubmitMisbehaviour.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
/* eslint-disable camelcase */
import ow from 'ow';
import { google } from '../../../../cosmos/v1beta1/codec/generated/codecimpl';
import { InitConfigurations } from '../../../../core/cro';
import { CosmosMsg } from '../../cosmosMsg';
import { Msg } from '../../../../cosmos/v1beta1/types/msg';
import { COSMOS_MSG_TYPEURL } from '../../../common/constants/typeurl';
import { validateAddress, AddressType } from '../../../../utils/address';
import { owMsgSubmitMisbehaviourOptions } from '../../ow.types';
import * as legacyAmino from '../../../../cosmos/amino';
import { Network } from '../../../../network/network';

export const msgSubmitMisbehaviourIBC = function (config: InitConfigurations) {
return class MsgSubmitMisbehaviour implements CosmosMsg {
/** MsgSubmitMisbehaviour clientId. */
public clientId: string;

/** MsgSubmitMisbehaviour misbehaviour. */
public misbehaviour?: google.protobuf.IAny | null;

/** MsgSubmitMisbehaviour signer. */
public signer: string;

/**
* Constructor to create a new IBC.MsgSubmitMisbehaviour
* @param {MsgSubmitMisbehaviourOptions} options
* @returns {MsgSubmitMisbehaviour}
* @throws {Error} when options is invalid
*/
constructor(options: MsgSubmitMisbehaviourOptions) {
ow(options, 'options', owMsgSubmitMisbehaviourOptions);
this.clientId = options.clientId;
this.misbehaviour = options.misbehaviour;
this.signer = options.signer;
this.validateAddresses();
}

/**
* Returns the raw Msg representation of Ibc.MsgSubmitMisbehaviour
* @returns {Msg}
*/
toRawMsg(): Msg {
return {
typeUrl: COSMOS_MSG_TYPEURL.ibc.MsgSubmitMisbehaviour,
value: {
clientId: this.clientId,
misbehaviour: this.misbehaviour,
signer: this.signer,
},
};
}

// eslint-disable-next-line class-methods-use-this
toRawAminoMsg(): legacyAmino.Msg {
throw new Error('IBC Module not supported under amino encoding scheme');
}

/**
* Returns an instance of IBC.MsgSubmitMisbehaviour
* @param {string} msgJsonStr
* @param {Network} network
* @returns {MsgSubmitMisbehaviour}
*/
public static fromCosmosMsgJSON(msgJsonStr: string, _network: Network): MsgSubmitMisbehaviour {
const parsedMsg = JSON.parse(msgJsonStr) as MsgSubmitMisbehaviourJsonRaw;
if (parsedMsg['@type'] !== COSMOS_MSG_TYPEURL.ibc.MsgSubmitMisbehaviour) {
throw new Error(
`Expected ${COSMOS_MSG_TYPEURL.ibc.MsgSubmitMisbehaviour} but got ${parsedMsg['@type']}`,
);
}

// TODO: The `misbehaviour` value needs to be handled, currently keeping it as `null`
if (typeof parsedMsg.misbehaviour === 'object' && Object.keys(parsedMsg.misbehaviour).length > 0) {
throw new Error('IBC MsgSubmitMisbehaviour does not support `misbehaviour` decoding.');
}

return new MsgSubmitMisbehaviour({
clientId: parsedMsg.client_id,
misbehaviour: null,
signer: parsedMsg.signer,
});
}

validateAddresses() {
// TODO: Can `signer` be from non-CRO network
if (
!validateAddress({
address: this.signer,
network: config.network,
type: AddressType.USER,
})
) {
throw new TypeError('Provided `signer` does not match network selected');
}
}
};
};

export type MsgSubmitMisbehaviourOptions = {
clientId: string;
misbehaviour?: google.protobuf.IAny | null;
signer: string;
};

export interface MsgSubmitMisbehaviourJsonRaw {
'@type': string;
client_id: string;
misbehaviour?: any;
signer: string;
}
5 changes: 5 additions & 0 deletions lib/src/transaction/msg/ow.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -253,3 +253,8 @@ export const owMsgUpgradeClientOptions = owStrictObject().exactShape({
proofUpgradeConsensusState: ow.uint8Array,
signer: ow.string,
});
export const owMsgSubmitMisbehaviourOptions = owStrictObject().exactShape({
clientId: ow.string,
misbehaviour: ow.optional.any(owGoogleProtoAnyOptional(), ow.optional.null),
signer: ow.string,
});