From a39a818b5c77eb6ac23106875276ae85705c4d9b Mon Sep 17 00:00:00 2001 From: willclarktech Date: Wed, 14 Apr 2021 16:19:45 +0200 Subject: [PATCH] cosmwasm-stargate: Add tests for sign method --- .../src/signingcosmwasmclient.spec.ts | 300 +++++++++++++++++- 1 file changed, 283 insertions(+), 17 deletions(-) diff --git a/packages/cosmwasm-stargate/src/signingcosmwasmclient.spec.ts b/packages/cosmwasm-stargate/src/signingcosmwasmclient.spec.ts index ea76b5da..de0ad3dc 100644 --- a/packages/cosmwasm-stargate/src/signingcosmwasmclient.spec.ts +++ b/packages/cosmwasm-stargate/src/signingcosmwasmclient.spec.ts @@ -15,7 +15,7 @@ import { import { DeepPartial, MsgSend } from "@cosmjs/stargate/build/codec/cosmos/bank/v1beta1/tx"; import { Coin } from "@cosmjs/stargate/build/codec/cosmos/base/v1beta1/coin"; import { MsgDelegate } from "@cosmjs/stargate/build/codec/cosmos/staking/v1beta1/tx"; -import { Tx } from "@cosmjs/stargate/build/codec/cosmos/tx/v1beta1/tx"; +import { AuthInfo, Tx, TxBody, TxRaw } from "@cosmjs/stargate/build/codec/cosmos/tx/v1beta1/tx"; import { assert, sleep } from "@cosmjs/utils"; import Long from "long"; import pako from "pako"; @@ -547,11 +547,9 @@ describe("SigningCosmWasmClient", () => { describe("direct mode", () => { it("works", async () => { pendingWithoutWasmd(); - const wallet = await DirectSecp256k1HdWallet.fromMnemonic(alice.mnemonic, { prefix: wasmd.prefix }); + const options = { prefix: wasmd.prefix }; + const wallet = await DirectSecp256k1HdWallet.fromMnemonic(alice.mnemonic, options); const msgDelegateTypeUrl = "/cosmos.staking.v1beta1.MsgDelegate"; - const registry = new Registry(); - registry.register(msgDelegateTypeUrl, MsgDelegate); - const options = { prefix: wasmd.prefix, registry: registry }; const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet, options); const msg = MsgDelegate.fromPartial({ @@ -574,13 +572,9 @@ describe("SigningCosmWasmClient", () => { it("works with a modifying signer", async () => { pendingWithoutWasmd(); - const wallet = await ModifyingDirectSecp256k1HdWallet.fromMnemonic(alice.mnemonic, { - prefix: wasmd.prefix, - }); + const options = { prefix: wasmd.prefix }; + const wallet = await ModifyingDirectSecp256k1HdWallet.fromMnemonic(alice.mnemonic, options); const msgDelegateTypeUrl = "/cosmos.staking.v1beta1.MsgDelegate"; - const registry = new Registry(); - registry.register(msgDelegateTypeUrl, MsgDelegate); - const options = { prefix: wasmd.prefix, registry: registry }; const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet, options); const msg = MsgDelegate.fromPartial({ @@ -615,8 +609,8 @@ describe("SigningCosmWasmClient", () => { describe("legacy Amino mode", () => { it("works with bank MsgSend", async () => { pendingWithoutWasmd(); - const wallet = await Secp256k1HdWallet.fromMnemonic(alice.mnemonic, { prefix: wasmd.prefix }); const options = { prefix: wasmd.prefix }; + const wallet = await Secp256k1HdWallet.fromMnemonic(alice.mnemonic, options); const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet, options); const msgSend: MsgSend = { @@ -639,8 +633,8 @@ describe("SigningCosmWasmClient", () => { it("works with staking MsgDelegate", async () => { pendingWithoutWasmd(); - const wallet = await Secp256k1HdWallet.fromMnemonic(alice.mnemonic, { prefix: wasmd.prefix }); const options = { prefix: wasmd.prefix }; + const wallet = await Secp256k1HdWallet.fromMnemonic(alice.mnemonic, options); const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet, options); const msgDelegate: MsgDelegate = { @@ -663,8 +657,8 @@ describe("SigningCosmWasmClient", () => { it("works with wasm MsgStoreCode", async () => { pendingWithoutWasmd(); - const wallet = await Secp256k1HdWallet.fromMnemonic(alice.mnemonic, { prefix: wasmd.prefix }); const options = { prefix: wasmd.prefix }; + const wallet = await Secp256k1HdWallet.fromMnemonic(alice.mnemonic, options); const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet, options); const { data, builder, source } = getHackatom(); @@ -808,10 +802,8 @@ describe("SigningCosmWasmClient", () => { it("works with a modifying signer", async () => { pendingWithoutWasmd(); - const wallet = await ModifyingSecp256k1HdWallet.fromMnemonic(alice.mnemonic, { - prefix: wasmd.prefix, - }); const options = { prefix: wasmd.prefix }; + const wallet = await ModifyingSecp256k1HdWallet.fromMnemonic(alice.mnemonic, options); const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet, options); const msg = { @@ -843,4 +835,278 @@ describe("SigningCosmWasmClient", () => { }); }); }); + + describe("sign", () => { + describe("direct mode", () => { + it("works", async () => { + pendingWithoutWasmd(); + const options = { prefix: wasmd.prefix }; + const wallet = await DirectSecp256k1HdWallet.fromMnemonic(alice.mnemonic, options); + const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet, options); + + const msg = MsgDelegate.fromPartial({ + delegatorAddress: alice.address0, + validatorAddress: validator.validatorAddress, + amount: coin(1234, "ustake"), + }); + const msgAny = { + typeUrl: "/cosmos.staking.v1beta1.MsgDelegate", + value: msg, + }; + const fee = { + amount: coins(2000, "ucosm"), + gas: "180000", // 180k + }; + const memo = "Use your power wisely"; + const signed = await client.sign(alice.address0, [msgAny], fee, memo); + + // ensure signature is valid + const result = await client.broadcastTx(Uint8Array.from(TxRaw.encode(signed).finish())); + assertIsBroadcastTxSuccess(result); + }); + + it("works with a modifying signer", async () => { + pendingWithoutWasmd(); + const options = { prefix: wasmd.prefix }; + const wallet = await ModifyingDirectSecp256k1HdWallet.fromMnemonic(alice.mnemonic, options); + const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet, options); + + const msg = MsgDelegate.fromPartial({ + delegatorAddress: alice.address0, + validatorAddress: validator.validatorAddress, + amount: coin(1234, "ustake"), + }); + const msgAny = { + typeUrl: "/cosmos.staking.v1beta1.MsgDelegate", + value: msg, + }; + const fee = { + amount: coins(2000, "ucosm"), + gas: "180000", // 180k + }; + const memo = "Use your power wisely"; + const signed = await client.sign(alice.address0, [msgAny], fee, memo); + + const body = TxBody.decode(signed.bodyBytes); + const authInfo = AuthInfo.decode(signed.authInfoBytes); + // From ModifyingDirectSecp256k1HdWallet + expect(body.memo).toEqual("This was modified"); + expect({ ...authInfo.fee!.amount[0] }).toEqual(coin(3000, "ucosm")); + expect(authInfo.fee!.gasLimit.toNumber()).toEqual(333333); + + // ensure signature is valid + const result = await client.broadcastTx(Uint8Array.from(TxRaw.encode(signed).finish())); + assertIsBroadcastTxSuccess(result); + }); + }); + + describe("legacy Amino mode", () => { + it("works with bank MsgSend", async () => { + pendingWithoutWasmd(); + const options = { prefix: wasmd.prefix }; + const wallet = await Secp256k1HdWallet.fromMnemonic(alice.mnemonic, options); + const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet, options); + + const msgSend: MsgSend = { + fromAddress: alice.address0, + toAddress: makeRandomAddress(), + amount: coins(1234, "ucosm"), + }; + const msgAny = { + typeUrl: "/cosmos.bank.v1beta1.MsgSend", + value: msgSend, + }; + const fee = { + amount: coins(2000, "ucosm"), + gas: "200000", + }; + const memo = "Use your tokens wisely"; + const signed = await client.sign(alice.address0, [msgAny], fee, memo); + + // ensure signature is valid + const result = await client.broadcastTx(Uint8Array.from(TxRaw.encode(signed).finish())); + assertIsBroadcastTxSuccess(result); + }); + + it("works with staking MsgDelegate", async () => { + pendingWithoutWasmd(); + const options = { prefix: wasmd.prefix }; + const wallet = await Secp256k1HdWallet.fromMnemonic(alice.mnemonic, options); + const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet, options); + + const msgDelegate: MsgDelegate = { + delegatorAddress: alice.address0, + validatorAddress: validator.validatorAddress, + amount: coin(1234, "ustake"), + }; + const msgAny = { + typeUrl: "/cosmos.staking.v1beta1.MsgDelegate", + value: msgDelegate, + }; + const fee = { + amount: coins(2000, "ustake"), + gas: "200000", + }; + const memo = "Use your tokens wisely"; + const signed = await client.sign(alice.address0, [msgAny], fee, memo); + + // ensure signature is valid + const result = await client.broadcastTx(Uint8Array.from(TxRaw.encode(signed).finish())); + assertIsBroadcastTxSuccess(result); + }); + + it("works with a custom registry and custom message", async () => { + pendingWithoutWasmd(); + const wallet = await Secp256k1HdWallet.fromMnemonic(alice.mnemonic, { prefix: wasmd.prefix }); + + const customRegistry = new Registry(); + const msgDelegateTypeUrl = "/cosmos.staking.v1beta1.MsgDelegate"; + interface CustomMsgDelegate { + customDelegatorAddress?: string; + customValidatorAddress?: string; + customAmount?: Coin; + } + const baseCustomMsgDelegate: CustomMsgDelegate = { + customDelegatorAddress: "", + customValidatorAddress: "", + }; + const CustomMsgDelegate = { + // Adapted from autogenerated MsgDelegate implementation + encode( + message: CustomMsgDelegate, + writer: protobuf.Writer = protobuf.Writer.create(), + ): protobuf.Writer { + writer.uint32(10).string(message.customDelegatorAddress ?? ""); + writer.uint32(18).string(message.customValidatorAddress ?? ""); + if (message.customAmount !== undefined && message.customAmount !== undefined) { + Coin.encode(message.customAmount, writer.uint32(26).fork()).ldelim(); + } + return writer; + }, + + decode(): CustomMsgDelegate { + throw new Error("decode method should not be required"); + }, + + fromJSON(): CustomMsgDelegate { + throw new Error("fromJSON method should not be required"); + }, + + fromPartial(object: DeepPartial): CustomMsgDelegate { + const message = { ...baseCustomMsgDelegate } as CustomMsgDelegate; + if (object.customDelegatorAddress !== undefined && object.customDelegatorAddress !== null) { + message.customDelegatorAddress = object.customDelegatorAddress; + } else { + message.customDelegatorAddress = ""; + } + if (object.customValidatorAddress !== undefined && object.customValidatorAddress !== null) { + message.customValidatorAddress = object.customValidatorAddress; + } else { + message.customValidatorAddress = ""; + } + if (object.customAmount !== undefined && object.customAmount !== null) { + message.customAmount = Coin.fromPartial(object.customAmount); + } else { + message.customAmount = undefined; + } + return message; + }, + + toJSON(): unknown { + throw new Error("toJSON method should not be required"); + }, + }; + customRegistry.register(msgDelegateTypeUrl, CustomMsgDelegate); + const customAminoTypes = new AminoTypes({ + additions: { + "/cosmos.staking.v1beta1.MsgDelegate": { + aminoType: "cosmos-sdk/MsgDelegate", + toAmino: ({ + customDelegatorAddress, + customValidatorAddress, + customAmount, + }: CustomMsgDelegate): AminoMsgDelegate["value"] => { + assert(customDelegatorAddress, "missing customDelegatorAddress"); + assert(customValidatorAddress, "missing validatorAddress"); + assert(customAmount, "missing amount"); + return { + delegator_address: customDelegatorAddress, + validator_address: customValidatorAddress, + amount: { + amount: customAmount.amount, + denom: customAmount.denom, + }, + }; + }, + fromAmino: ({ + delegator_address, + validator_address, + amount, + }: AminoMsgDelegate["value"]): CustomMsgDelegate => ({ + customDelegatorAddress: delegator_address, + customValidatorAddress: validator_address, + customAmount: Coin.fromPartial(amount), + }), + }, + }, + }); + const options = { registry: customRegistry, aminoTypes: customAminoTypes }; + const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet, options); + + const msg: CustomMsgDelegate = { + customDelegatorAddress: alice.address0, + customValidatorAddress: validator.validatorAddress, + customAmount: coin(1234, "ustake"), + }; + const msgAny = { + typeUrl: "/cosmos.staking.v1beta1.MsgDelegate", + value: msg, + }; + const fee = { + amount: coins(2000, "ucosm"), + gas: "200000", + }; + const memo = "Use your power wisely"; + const signed = await client.sign(alice.address0, [msgAny], fee, memo); + + // ensure signature is valid + const result = await client.broadcastTx(Uint8Array.from(TxRaw.encode(signed).finish())); + assertIsBroadcastTxSuccess(result); + }); + + it("works with a modifying signer", async () => { + pendingWithoutWasmd(); + const options = { prefix: wasmd.prefix }; + const wallet = await ModifyingSecp256k1HdWallet.fromMnemonic(alice.mnemonic, options); + const client = await SigningCosmWasmClient.connectWithSigner(wasmd.endpoint, wallet, options); + + const msg: MsgDelegate = { + delegatorAddress: alice.address0, + validatorAddress: validator.validatorAddress, + amount: coin(1234, "ustake"), + }; + const msgAny = { + typeUrl: "/cosmos.staking.v1beta1.MsgDelegate", + value: msg, + }; + const fee = { + amount: coins(2000, "ucosm"), + gas: "200000", + }; + const memo = "Use your power wisely"; + const signed = await client.sign(alice.address0, [msgAny], fee, memo); + + const body = TxBody.decode(signed.bodyBytes); + const authInfo = AuthInfo.decode(signed.authInfoBytes); + // From ModifyingSecp256k1HdWallet + expect(body.memo).toEqual("This was modified"); + expect({ ...authInfo.fee!.amount[0] }).toEqual(coin(3000, "ucosm")); + expect(authInfo.fee!.gasLimit.toNumber()).toEqual(333333); + + // ensure signature is valid + const result = await client.broadcastTx(Uint8Array.from(TxRaw.encode(signed).finish())); + assertIsBroadcastTxSuccess(result); + }); + }); + }); });