diff --git a/types/msg.go b/types/msg.go index ddbce9d14..7864ba61d 100644 --- a/types/msg.go +++ b/types/msg.go @@ -103,10 +103,49 @@ type CosmosMsg struct { Gov *GovMsg `json:"gov,omitempty"` IBC *IBCMsg `json:"ibc,omitempty"` Staking *StakingMsg `json:"staking,omitempty"` - Stargate *StargateMsg `json:"stargate,omitempty"` + Any *AnyMsg `json:"any,omitempty"` Wasm *WasmMsg `json:"wasm,omitempty"` } +func (m *CosmosMsg) UnmarshalJSON(data []byte) error { + // We need a custom unmarshaler to parse both the "stargate" and "any" variants + type InternalCosmosMsg struct { + Bank *BankMsg `json:"bank,omitempty"` + Custom json.RawMessage `json:"custom,omitempty"` + Distribution *DistributionMsg `json:"distribution,omitempty"` + Gov *GovMsg `json:"gov,omitempty"` + IBC *IBCMsg `json:"ibc,omitempty"` + Staking *StakingMsg `json:"staking,omitempty"` + Any *AnyMsg `json:"any,omitempty"` + Wasm *WasmMsg `json:"wasm,omitempty"` + Stargate *AnyMsg `json:"stargate,omitempty"` + } + var tmp InternalCosmosMsg + err := json.Unmarshal(data, &tmp) + if err != nil { + return err + } + + if tmp.Any != nil && tmp.Stargate != nil { + return fmt.Errorf("invalid CosmosMsg: both 'any' and 'stargate' fields are set") + } else if tmp.Any == nil && tmp.Stargate != nil { + // Use "Any" for both variants + tmp.Any = tmp.Stargate + } + + *m = CosmosMsg{ + Bank: tmp.Bank, + Custom: tmp.Custom, + Distribution: tmp.Distribution, + Gov: tmp.Gov, + IBC: tmp.IBC, + Staking: tmp.Staking, + Any: tmp.Any, + Wasm: tmp.Wasm, + } + return nil +} + type BankMsg struct { Send *SendMsg `json:"send,omitempty"` Burn *BurnMsg `json:"burn,omitempty"` @@ -272,9 +311,9 @@ type FundCommunityPoolMsg struct { Amount Coins `json:"amount"` } -// StargateMsg is encoded the same way as a protobof [Any](https://github.com/protocolbuffers/protobuf/blob/master/src/google/protobuf/any.proto). +// AnyMsg is encoded the same way as a protobof [Any](https://github.com/protocolbuffers/protobuf/blob/master/src/google/protobuf/any.proto). // This is the same structure as messages in `TxBody` from [ADR-020](https://github.com/cosmos/cosmos-sdk/blob/master/docs/architecture/adr-020-protobuf-transaction-encoding.md) -type StargateMsg struct { +type AnyMsg struct { TypeURL string `json:"type_url"` Value []byte `json:"value"` } diff --git a/types/msg_test.go b/types/msg_test.go index ffd6baa41..8aec492ea 100644 --- a/types/msg_test.go +++ b/types/msg_test.go @@ -1,6 +1,7 @@ package types import ( + "encoding/base64" "encoding/json" "testing" @@ -76,6 +77,44 @@ func TestWasmMsgInstantiate2Serialization(t *testing.T) { require.Equal(t, []byte{0x52, 0x43, 0x95, 0x6b, 0x38, 0x62, 0xc2, 0x8a}, msg.Instantiate2.Salt) } +func TestAnyMsgSerialization(t *testing.T) { + expectedData, err := base64.StdEncoding.DecodeString("5yu/rQ+HrMcxH1zdga7P5hpGMLE=") + require.NoError(t, err) + + // test backwards compatibility with old stargate variant + document1 := []byte(`{"stargate":{"type_url":"/cosmos.foo.v1beta.MsgBar","value":"5yu/rQ+HrMcxH1zdga7P5hpGMLE="}}`) + var res CosmosMsg + err = json.Unmarshal(document1, &res) + require.NoError(t, err) + require.Equal(t, CosmosMsg{ + Any: &AnyMsg{ + TypeURL: "/cosmos.foo.v1beta.MsgBar", + Value: expectedData, + }, + }, res) + + // test new any variant + document2 := []byte(`{"any":{"type_url":"/cosmos.foo.v1beta.MsgBar","value":"5yu/rQ+HrMcxH1zdga7P5hpGMLE="}}`) + var res2 CosmosMsg + err = json.Unmarshal(document2, &res2) + require.NoError(t, err) + require.Equal(t, res, res2) + + // serializing should use the new any variant + serialized, err := json.Marshal(res) + require.NoError(t, err) + require.Equal(t, document2, serialized) + + // test providing both variants is rejected + document3 := []byte(`{ + "stargate":{"type_url":"/cosmos.foo.v1beta.MsgBar","value":"5yu/rQ+HrMcxH1zdga7P5hpGMLE="}, + "any":{"type_url":"/cosmos.foo.v1beta.MsgBar","value":"5yu/rQ+HrMcxH1zdga7P5hpGMLE="} + }`) + var res3 CosmosMsg + err = json.Unmarshal(document3, &res3) + require.Error(t, err) +} + func TestGovMsgVoteSerialization(t *testing.T) { document := []byte(`{"vote":{"proposal_id":4,"vote":"no_with_veto"}}`)