Merge pull request #1224 from cosmos/add-MsgVoteWeighted

Add support for MsgVoteWeighted
This commit is contained in:
Simon Warta 2022-08-02 15:15:47 +02:00 committed by GitHub
commit e025af9f05
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 395 additions and 23 deletions

View File

@ -216,11 +216,11 @@ jobs:
background: true
- run:
name: Start simapp
command: ./scripts/simapp42/start.sh
command: ./scripts/simapp44/start.sh
background: true
- run:
name: Start slow simapp
command: ./scripts/simapp42/slow_start.sh
command: ./scripts/simapp44/slow_start.sh
background: true
- run:
name: Start Tendermint blockchains
@ -278,8 +278,8 @@ jobs:
- run:
environment:
HTTPSERVER_ENABLED: 1
SIMAPP42_ENABLED: 1
SLOW_SIMAPP42_ENABLED: 1
SIMAPP44_ENABLED: 1
SLOW_SIMAPP44_ENABLED: 1
TENDERMINT_ENABLED: 1
SOCKETSERVER_ENABLED: 1
SKIP_BUILD: 1
@ -297,8 +297,8 @@ jobs:
working_directory: packages/cli
environment:
HTTPSERVER_ENABLED: 1
SIMAPP42_ENABLED: 1
SLOW_SIMAPP42_ENABLED: 1
SIMAPP44_ENABLED: 1
SLOW_SIMAPP44_ENABLED: 1
TENDERMINT_ENABLED: 1
SOCKETSERVER_ENABLED: 1
SKIP_BUILD: 1
@ -310,8 +310,8 @@ jobs:
./scripts/httpserver/stop.sh
./scripts/socketserver/stop.sh
./scripts/tendermint/all_stop.sh
./scripts/simapp42/stop.sh
./scripts/simapp42/slow_stop.sh
./scripts/simapp44/stop.sh
./scripts/simapp44/slow_stop.sh
./scripts/wasmd/stop.sh
test-chrome:
machine:
@ -339,11 +339,11 @@ jobs:
background: true
- run:
name: Start simapp
command: ./scripts/simapp42/start.sh
command: ./scripts/simapp44/start.sh
background: true
- run:
name: Start slow simapp
command: ./scripts/simapp42/slow_start.sh
command: ./scripts/simapp44/slow_start.sh
background: true
- run:
name: Start Tendermint blockchains
@ -396,8 +396,8 @@ jobs:
- run:
environment:
HTTPSERVER_ENABLED: 1
SIMAPP42_ENABLED: 1
SLOW_SIMAPP42_ENABLED: 1
SIMAPP44_ENABLED: 1
SLOW_SIMAPP44_ENABLED: 1
TENDERMINT_ENABLED: 1
SOCKETSERVER_ENABLED: 1
SKIP_BUILD: 1
@ -409,8 +409,8 @@ jobs:
./scripts/httpserver/stop.sh
./scripts/socketserver/stop.sh
./scripts/tendermint/all_stop.sh
./scripts/simapp42/stop.sh
./scripts/simapp42/slow_stop.sh
./scripts/simapp44/stop.sh
./scripts/simapp44/slow_stop.sh
./scripts/wasmd/stop.sh
coverage:
machine:
@ -437,11 +437,11 @@ jobs:
background: true
- run:
name: Start simapp
command: ./scripts/simapp42/start.sh
command: ./scripts/simapp44/start.sh
background: true
- run:
name: Start slow simapp
command: ./scripts/simapp42/slow_start.sh
command: ./scripts/simapp44/slow_start.sh
background: true
- run:
name: Start Tendermint blockchains
@ -494,8 +494,8 @@ jobs:
- run:
environment:
HTTPSERVER_ENABLED: 1
SIMAPP42_ENABLED: 1
SLOW_SIMAPP42_ENABLED: 1
SIMAPP44_ENABLED: 1
SLOW_SIMAPP44_ENABLED: 1
TENDERMINT_ENABLED: 1
SOCKETSERVER_ENABLED: 1
SKIP_BUILD: 1
@ -510,8 +510,8 @@ jobs:
./scripts/httpserver/stop.sh
./scripts/socketserver/stop.sh
./scripts/tendermint/all_stop.sh
./scripts/simapp42/stop.sh
./scripts/simapp42/slow_stop.sh
./scripts/simapp44/stop.sh
./scripts/simapp44/slow_stop.sh
./scripts/wasmd/stop.sh
docs-build:
docker:

View File

@ -19,10 +19,14 @@ and this project adheres to
- @cosmjs/cosmwasm-stargate: Add `SigningCosmWasmClient.executeMultiple`
([#1072]).
- @cosmjs/math: Add `{Uint32,Int53,Uint53,Uint64}.toBigInt` converter methods.
- @cosmjs/stargate: Add missing exports `AminoMsgTransfer`/`isAminoMsgTransfer`.
- @cosmjs/stargate: Add support for `MsgVoteWeighted` (register by default and
create Amino JSON converters) ([#1224]).
[#1072]: https://github.com/cosmos/cosmjs/issues/1072
[#1154]: https://github.com/cosmos/cosmjs/issues/1154
[#1176]: https://github.com/cosmos/cosmjs/pull/1176
[#1224]: https://github.com/cosmos/cosmjs/pull/1224
### Fixed

View File

@ -14,10 +14,12 @@ export {
AminoMsgSetWithdrawAddress,
AminoMsgSubmitEvidence,
AminoMsgSubmitProposal,
AminoMsgTransfer,
AminoMsgUndelegate,
AminoMsgUnjail,
AminoMsgVerifyInvariant,
AminoMsgVote,
AminoMsgVoteWeighted,
AminoMsgWithdrawDelegatorReward,
AminoMsgWithdrawValidatorCommission,
AuthExtension,
@ -38,10 +40,12 @@ export {
isAminoMsgSetWithdrawAddress,
isAminoMsgSubmitEvidence,
isAminoMsgSubmitProposal,
isAminoMsgTransfer,
isAminoMsgUndelegate,
isAminoMsgUnjail,
isAminoMsgVerifyInvariant,
isAminoMsgVote,
isAminoMsgVoteWeighted,
isAminoMsgWithdrawDelegatorReward,
isAminoMsgWithdrawValidatorCommission,
isMsgDelegateEncodeObject,
@ -51,6 +55,7 @@ export {
isMsgTransferEncodeObject,
isMsgUndelegateEncodeObject,
isMsgVoteEncodeObject,
isMsgVoteWeightedEncodeObject,
isMsgWithdrawDelegatorRewardEncodeObject,
MintExtension,
MintParams,
@ -61,6 +66,7 @@ export {
MsgTransferEncodeObject,
MsgUndelegateEncodeObject,
MsgVoteEncodeObject,
MsgVoteWeightedEncodeObject,
MsgWithdrawDelegatorRewardEncodeObject,
setupAuthExtension,
setupBankExtension,

View File

@ -1,6 +1,6 @@
/* eslint-disable @typescript-eslint/naming-convention */
import { TextProposal, VoteOption } from "cosmjs-types/cosmos/gov/v1beta1/gov";
import { MsgDeposit, MsgSubmitProposal, MsgVote } from "cosmjs-types/cosmos/gov/v1beta1/tx";
import { MsgDeposit, MsgSubmitProposal, MsgVote, MsgVoteWeighted } from "cosmjs-types/cosmos/gov/v1beta1/tx";
import Long from "long";
import { AminoTypes } from "../../aminotypes";
@ -8,6 +8,7 @@ import {
AminoMsgDeposit,
AminoMsgSubmitProposal,
AminoMsgVote,
AminoMsgVoteWeighted,
createGovAminoConverters,
} from "./aminomessages";
@ -90,6 +91,34 @@ describe("AminoTypes", () => {
};
expect(aminoMsg).toEqual(expected);
});
it("works for MsgVoteWeighted", () => {
const msg: MsgVoteWeighted = {
proposalId: Long.fromNumber(5),
voter: "cosmos1xy4yqngt0nlkdcenxymg8tenrghmek4nmqm28k",
options: [
{ option: VoteOption.VOTE_OPTION_NO_WITH_VETO, weight: "700000000000000000" /* 0.7 */ },
{ option: VoteOption.VOTE_OPTION_NO, weight: "300000000000000000" /* 0.3 */ },
],
};
const aminoTypes = new AminoTypes(createGovAminoConverters());
const aminoMsg = aminoTypes.toAmino({
typeUrl: "/cosmos.gov.v1beta1.MsgVoteWeighted",
value: msg,
});
const expected: AminoMsgVoteWeighted = {
type: "cosmos-sdk/MsgVoteWeighted",
value: {
proposal_id: "5",
voter: "cosmos1xy4yqngt0nlkdcenxymg8tenrghmek4nmqm28k",
options: [
{ option: VoteOption.VOTE_OPTION_NO_WITH_VETO, weight: "0.700000000000000000" },
{ option: VoteOption.VOTE_OPTION_NO, weight: "0.300000000000000000" },
],
},
};
expect(aminoMsg).toEqual(expected);
});
});
describe("fromAmino", () => {
@ -167,5 +196,32 @@ describe("AminoTypes", () => {
value: expectedValue,
});
});
it("works for MsgVoteWeighted", () => {
const aminoMsg: AminoMsgVoteWeighted = {
type: "cosmos-sdk/MsgVoteWeighted",
value: {
proposal_id: "5",
voter: "cosmos1xy4yqngt0nlkdcenxymg8tenrghmek4nmqm28k",
options: [
{ option: 4, weight: "0.750000000000000000" },
{ option: 3, weight: "0.250000000000000000" },
],
},
};
const msg = new AminoTypes(createGovAminoConverters()).fromAmino(aminoMsg);
const expectedValue: MsgVoteWeighted = {
proposalId: Long.fromNumber(5),
voter: "cosmos1xy4yqngt0nlkdcenxymg8tenrghmek4nmqm28k",
options: [
{ option: VoteOption.VOTE_OPTION_NO_WITH_VETO, weight: "750000000000000000" },
{ option: VoteOption.VOTE_OPTION_NO, weight: "250000000000000000" },
],
};
expect(msg).toEqual({
typeUrl: "/cosmos.gov.v1beta1.MsgVoteWeighted",
value: expectedValue,
});
});
});
});

View File

@ -1,12 +1,14 @@
/* eslint-disable @typescript-eslint/naming-convention */
import { AminoMsg, Coin } from "@cosmjs/amino";
import { Decimal } from "@cosmjs/math";
import { assert, assertDefinedAndNotNull, isNonNullObject } from "@cosmjs/utils";
import { TextProposal, voteOptionFromJSON } from "cosmjs-types/cosmos/gov/v1beta1/gov";
import { MsgDeposit, MsgSubmitProposal, MsgVote } from "cosmjs-types/cosmos/gov/v1beta1/tx";
import { MsgDeposit, MsgSubmitProposal, MsgVote, MsgVoteWeighted } from "cosmjs-types/cosmos/gov/v1beta1/tx";
import { Any } from "cosmjs-types/google/protobuf/any";
import Long from "long";
import { AminoConverters } from "../../aminotypes";
import { decodeCosmosSdkDecFromProto } from "../../queryclient";
/** Supports submitting arbitrary proposal content. */
export interface AminoMsgSubmitProposal extends AminoMsg {
@ -59,6 +61,32 @@ export function isAminoMsgVote(msg: AminoMsg): msg is AminoMsgVote {
return msg.type === "cosmos-sdk/MsgVote";
}
/**
* @see https://github.com/cosmos/cosmos-sdk/blob/v0.44.5/x/gov/types/tx.pb.go#L196-L203
* @see https://github.com/cosmos/cosmos-sdk/blob/v0.44.5/x/gov/types/gov.pb.go#L124-L130
*/
export interface AminoMsgVoteWeighted extends AminoMsg {
readonly type: "cosmos-sdk/MsgVoteWeighted";
readonly value: {
readonly proposal_id: string;
/** Bech32 account address */
readonly voter: string;
readonly options: Array<{
/**
* VoteOption as integer from 0 to 4 🤷
*
* @see https://github.com/cosmos/cosmos-sdk/blob/v0.44.5/x/gov/types/gov.pb.go#L35-L49
*/
readonly option: number;
readonly weight: string;
}>;
};
}
export function isAminoMsgVoteWeighted(msg: AminoMsg): msg is AminoMsgVoteWeighted {
return (msg as AminoMsgVoteWeighted).type === "cosmos-sdk/MsgVoteWeighted";
}
/** Submits a deposit to an existing proposal */
export interface AminoMsgDeposit extends AminoMsg {
readonly type: "cosmos-sdk/MsgDeposit";
@ -110,6 +138,31 @@ export function createGovAminoConverters(): AminoConverters {
};
},
},
"/cosmos.gov.v1beta1.MsgVoteWeighted": {
aminoType: "cosmos-sdk/MsgVoteWeighted",
toAmino: ({ options, proposalId, voter }: MsgVoteWeighted): AminoMsgVoteWeighted["value"] => {
return {
options: options.map((o) => ({
option: o.option,
// Weight is between 0 and 1, so we always have 20 characters when printing all trailing
// zeros (e.g. "0.700000000000000000" or "1.000000000000000000")
weight: decodeCosmosSdkDecFromProto(o.weight).toString().padEnd(20, "0"),
})),
proposal_id: proposalId.toString(),
voter: voter,
};
},
fromAmino: ({ options, proposal_id, voter }: AminoMsgVoteWeighted["value"]): MsgVoteWeighted => {
return {
proposalId: Long.fromString(proposal_id),
voter: voter,
options: options.map((o) => ({
option: voteOptionFromJSON(o.option),
weight: Decimal.fromUserInput(o.weight, 18).atomics,
})),
};
},
},
"/cosmos.gov.v1beta1.MsgSubmitProposal": {
aminoType: "cosmos-sdk/MsgSubmitProposal",
toAmino: ({

View File

@ -0,0 +1,239 @@
import { coin, coins, makeCosmoshubPath, Secp256k1HdWallet } from "@cosmjs/amino";
import { DirectSecp256k1HdWallet } from "@cosmjs/proto-signing";
import { assert, sleep } from "@cosmjs/utils";
import { TextProposal, VoteOption } from "cosmjs-types/cosmos/gov/v1beta1/gov";
import { Any } from "cosmjs-types/google/protobuf/any";
import { longify } from "../../queryclient";
import { SigningStargateClient } from "../../signingstargateclient";
import { assertIsDeliverTxSuccess } from "../../stargateclient";
import {
defaultSigningClientOptions,
faucet,
nonNegativeIntegerMatcher,
pendingWithoutSimapp,
pendingWithoutSimapp44,
simapp,
simappEnabled,
validator,
} from "../../testutils.spec";
import { MsgDelegateEncodeObject, MsgSubmitProposalEncodeObject, MsgVoteEncodeObject } from "../";
import { MsgVoteWeightedEncodeObject } from "./messages";
describe("gov messages", () => {
const defaultFee = {
amount: coins(25000, "ucosm"),
gas: "1500000", // 1.5 million
};
const textProposal = TextProposal.fromPartial({
title: "Test Proposal",
description: "This proposal proposes to test whether this proposal passes",
});
const initialDeposit = coins(12300000, "ustake");
const delegationVoter1 = coin(424242, "ustake");
const delegationVoter2 = coin(777, "ustake");
const voter1Address = faucet.address1;
const voter2Address = faucet.address2;
// Use address 1 and 2 instead of 0 to avoid conflicts with other delegation tests
// This must match `voterAddress` above.
const voterPaths = [makeCosmoshubPath(1), makeCosmoshubPath(2)];
let voterWallet: DirectSecp256k1HdWallet | undefined;
let voterWalletAmino: Secp256k1HdWallet | undefined;
let proposalId: string | undefined;
beforeAll(async () => {
if (simappEnabled()) {
voterWallet = await DirectSecp256k1HdWallet.fromMnemonic(faucet.mnemonic, { hdPaths: voterPaths });
voterWalletAmino = await Secp256k1HdWallet.fromMnemonic(faucet.mnemonic, { hdPaths: voterPaths });
const client = await SigningStargateClient.connectWithSigner(
simapp.tendermintUrl,
voterWallet,
defaultSigningClientOptions,
);
const proposalMsg: MsgSubmitProposalEncodeObject = {
typeUrl: "/cosmos.gov.v1beta1.MsgSubmitProposal",
value: {
content: Any.fromPartial({
typeUrl: "/cosmos.gov.v1beta1.TextProposal",
value: Uint8Array.from(TextProposal.encode(textProposal).finish()),
}),
proposer: voter1Address,
initialDeposit: initialDeposit,
},
};
const proposalResult = await client.signAndBroadcast(
voter1Address,
[proposalMsg],
defaultFee,
"Test proposal for simd",
);
assertIsDeliverTxSuccess(proposalResult);
const logs = JSON.parse(proposalResult.rawLog || "");
proposalId = logs[0].events
.find(({ type }: any) => type === "submit_proposal")
.attributes.find(({ key }: any) => key === "proposal_id").value;
assert(proposalId, "Proposal ID not found in events");
assert(proposalId.match(nonNegativeIntegerMatcher));
// Voter 1
{
// My vote only counts when I delegate
if (!(await client.getDelegation(voter1Address, validator.validatorAddress))) {
const msgDelegate: MsgDelegateEncodeObject = {
typeUrl: "/cosmos.staking.v1beta1.MsgDelegate",
value: {
delegatorAddress: voter1Address,
validatorAddress: validator.validatorAddress,
amount: delegationVoter1,
},
};
const result = await client.signAndBroadcast(voter1Address, [msgDelegate], defaultFee);
assertIsDeliverTxSuccess(result);
}
}
// Voter 2
{
// My vote only counts when I delegate
if (!(await client.getDelegation(voter2Address, validator.validatorAddress))) {
const msgDelegate: MsgDelegateEncodeObject = {
typeUrl: "/cosmos.staking.v1beta1.MsgDelegate",
value: {
delegatorAddress: voter2Address,
validatorAddress: validator.validatorAddress,
amount: delegationVoter2,
},
};
const result = await client.signAndBroadcast(voter2Address, [msgDelegate], defaultFee);
assertIsDeliverTxSuccess(result);
}
const voteMsg: MsgVoteEncodeObject = {
typeUrl: "/cosmos.gov.v1beta1.MsgVote",
value: {
proposalId: longify(proposalId),
voter: voter2Address,
option: VoteOption.VOTE_OPTION_NO_WITH_VETO,
},
};
const voteResult = await client.signAndBroadcast(voter2Address, [voteMsg], defaultFee);
assertIsDeliverTxSuccess(voteResult);
}
await sleep(75); // wait until transactions are indexed
client.disconnect();
}
});
describe("MsgVote", () => {
it("works", async () => {
pendingWithoutSimapp();
assert(voterWallet);
assert(proposalId, "Missing proposal ID");
const client = await SigningStargateClient.connectWithSigner(simapp.tendermintUrl, voterWallet);
const voteMsg: MsgVoteEncodeObject = {
typeUrl: "/cosmos.gov.v1beta1.MsgVote",
value: {
proposalId: longify(proposalId),
voter: voter1Address,
option: VoteOption.VOTE_OPTION_YES,
},
};
const voteResult = await client.signAndBroadcast(voter1Address, [voteMsg], defaultFee);
assertIsDeliverTxSuccess(voteResult);
client.disconnect();
});
it("works with Amino JSON sign mode", async () => {
pendingWithoutSimapp();
assert(voterWalletAmino);
assert(proposalId, "Missing proposal ID");
const client = await SigningStargateClient.connectWithSigner(simapp.tendermintUrl, voterWalletAmino);
const voteMsg: MsgVoteEncodeObject = {
typeUrl: "/cosmos.gov.v1beta1.MsgVote",
value: {
proposalId: longify(proposalId),
voter: voter1Address,
option: VoteOption.VOTE_OPTION_YES,
},
};
const voteResult = await client.signAndBroadcast(voter1Address, [voteMsg], defaultFee);
assertIsDeliverTxSuccess(voteResult);
client.disconnect();
});
});
describe("MsgVoteWeighted", () => {
it("works", async () => {
pendingWithoutSimapp44(); // MsgVoteWeighted does not yet exist in Cosmos SDK 0.42
assert(voterWallet);
assert(proposalId, "Missing proposal ID");
const client = await SigningStargateClient.connectWithSigner(simapp.tendermintUrl, voterWallet);
const voteMsg: MsgVoteWeightedEncodeObject = {
typeUrl: "/cosmos.gov.v1beta1.MsgVoteWeighted",
value: {
proposalId: longify(proposalId),
voter: voter1Address,
options: [
{
option: VoteOption.VOTE_OPTION_YES,
weight: "700000000000000000", // 0.7
},
{
option: VoteOption.VOTE_OPTION_NO,
weight: "200000000000000000", // 0.2
},
{
option: VoteOption.VOTE_OPTION_ABSTAIN,
weight: "100000000000000000", // 0.1
},
],
},
};
const voteResult = await client.signAndBroadcast(voter1Address, [voteMsg], defaultFee);
assertIsDeliverTxSuccess(voteResult);
client.disconnect();
});
it("works with Amino JSON sign mode", async () => {
pendingWithoutSimapp44(); // MsgVoteWeighted does not yet exist in Cosmos SDK 0.42
assert(voterWalletAmino);
assert(proposalId, "Missing proposal ID");
const client = await SigningStargateClient.connectWithSigner(simapp.tendermintUrl, voterWalletAmino);
const voteMsg: MsgVoteWeightedEncodeObject = {
typeUrl: "/cosmos.gov.v1beta1.MsgVoteWeighted",
value: {
proposalId: longify(proposalId),
voter: voter1Address,
options: [
{
option: VoteOption.VOTE_OPTION_YES,
weight: "700000000000000000", // 0.7
},
{
option: VoteOption.VOTE_OPTION_NO,
weight: "200000000000000000", // 0.2
},
{
option: VoteOption.VOTE_OPTION_ABSTAIN,
weight: "100000000000000000", // 0.1
},
],
},
};
const voteResult = await client.signAndBroadcast(voter1Address, [voteMsg], defaultFee);
assertIsDeliverTxSuccess(voteResult);
client.disconnect();
});
});
});

View File

@ -1,10 +1,11 @@
import { EncodeObject, GeneratedType } from "@cosmjs/proto-signing";
import { MsgDeposit, MsgSubmitProposal, MsgVote } from "cosmjs-types/cosmos/gov/v1beta1/tx";
import { MsgDeposit, MsgSubmitProposal, MsgVote, MsgVoteWeighted } from "cosmjs-types/cosmos/gov/v1beta1/tx";
export const govTypes: ReadonlyArray<[string, GeneratedType]> = [
["/cosmos.gov.v1beta1.MsgDeposit", MsgDeposit],
["/cosmos.gov.v1beta1.MsgSubmitProposal", MsgSubmitProposal],
["/cosmos.gov.v1beta1.MsgVote", MsgVote],
["/cosmos.gov.v1beta1.MsgVoteWeighted", MsgVoteWeighted],
];
export interface MsgDepositEncodeObject extends EncodeObject {
@ -35,3 +36,12 @@ export interface MsgVoteEncodeObject extends EncodeObject {
export function isMsgVoteEncodeObject(object: EncodeObject): object is MsgVoteEncodeObject {
return (object as MsgVoteEncodeObject).typeUrl === "/cosmos.gov.v1beta1.MsgVote";
}
export interface MsgVoteWeightedEncodeObject extends EncodeObject {
readonly typeUrl: "/cosmos.gov.v1beta1.MsgVoteWeighted";
readonly value: Partial<MsgVoteWeighted>;
}
export function isMsgVoteWeightedEncodeObject(object: EncodeObject): object is MsgVoteWeightedEncodeObject {
return (object as MsgVoteWeightedEncodeObject).typeUrl === "/cosmos.gov.v1beta1.MsgVoteWeighted";
}

View File

@ -43,19 +43,23 @@ export {
AminoMsgDeposit,
AminoMsgSubmitProposal,
AminoMsgVote,
AminoMsgVoteWeighted,
createGovAminoConverters,
isAminoMsgDeposit,
isAminoMsgSubmitProposal,
isAminoMsgVote,
isAminoMsgVoteWeighted,
} from "./gov/aminomessages";
export {
govTypes,
isMsgDepositEncodeObject,
isMsgSubmitProposalEncodeObject,
isMsgVoteEncodeObject,
isMsgVoteWeightedEncodeObject,
MsgDepositEncodeObject,
MsgSubmitProposalEncodeObject,
MsgVoteEncodeObject,
MsgVoteWeightedEncodeObject,
} from "./gov/messages";
export { GovExtension, GovParamsType, GovProposalId, setupGovExtension } from "./gov/queries";
export { AminoMsgTransfer, createIbcAminoConverters, isAminoMsgTransfer } from "./ibc/aminomessages";