github.com/cosmos/cosmos-sdk@v0.50.1/crypto/keys/multisig/amino.go (about) 1 package multisig 2 3 import ( 4 errorsmod "cosmossdk.io/errors" 5 6 types "github.com/cosmos/cosmos-sdk/codec/types" 7 cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" 8 sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" 9 ) 10 11 // tmMultisig implements a K of N threshold multisig. It is used for 12 // Amino JSON marshaling of LegacyAminoPubKey (see below for details). 13 // 14 // This struct is copy-pasted from: 15 // https://github.com/tendermint/tendermint/blob/v0.33.9/crypto/multisig/threshold_pubkey.go 16 // 17 // This struct was used in the SDK <=0.39. In 0.40 and the switch to protobuf, 18 // it has been converted to LegacyAminoPubKey. However, there's one difference: 19 // the threshold field was an `uint` before, and an `uint32` after. This caused 20 // amino marshaling to be breaking: amino marshals `uint32` as a JSON number, 21 // and `uint` as a JSON string. 22 // 23 // In this file, we're overriding LegacyAminoPubKey's default JSON Amino 24 // marshaling by using this struct. Please note that we are NOT overriding the 25 // Amino binary marshaling, as that _might_ introduce breaking changes in the 26 // keyring, where multisigs are amino-binary-encoded. 27 // 28 // ref: https://github.com/cosmos/cosmos-sdk/issues/8776 29 type tmMultisig struct { 30 K uint `json:"threshold"` 31 PubKeys []cryptotypes.PubKey `json:"pubkeys"` 32 } 33 34 // protoToTm converts a LegacyAminoPubKey into a tmMultisig. 35 func protoToTm(protoPk *LegacyAminoPubKey) (tmMultisig, error) { 36 var ok bool 37 pks := make([]cryptotypes.PubKey, len(protoPk.PubKeys)) 38 for i, pk := range protoPk.PubKeys { 39 pks[i], ok = pk.GetCachedValue().(cryptotypes.PubKey) 40 if !ok { 41 return tmMultisig{}, errorsmod.Wrapf(sdkerrors.ErrInvalidType, "expected %T, got %T", (cryptotypes.PubKey)(nil), pk.GetCachedValue()) 42 } 43 } 44 45 return tmMultisig{ 46 K: uint(protoPk.Threshold), 47 PubKeys: pks, 48 }, nil 49 } 50 51 // tmToProto converts a tmMultisig into a LegacyAminoPubKey. 52 func tmToProto(tmPk tmMultisig) (*LegacyAminoPubKey, error) { 53 var err error 54 pks := make([]*types.Any, len(tmPk.PubKeys)) 55 for i, pk := range tmPk.PubKeys { 56 pks[i], err = types.NewAnyWithValue(pk) 57 if err != nil { 58 return nil, err 59 } 60 } 61 62 return &LegacyAminoPubKey{ 63 Threshold: uint32(tmPk.K), 64 PubKeys: pks, 65 }, nil 66 } 67 68 // MarshalAminoJSON overrides amino JSON unmarshaling. 69 func (m LegacyAminoPubKey) MarshalAminoJSON() (tmMultisig, error) { //nolint:golint,revive // we need to override the default amino JSON marshaling 70 return protoToTm(&m) 71 } 72 73 // UnmarshalAminoJSON overrides amino JSON unmarshaling. 74 func (m *LegacyAminoPubKey) UnmarshalAminoJSON(tmPk tmMultisig) error { 75 protoPk, err := tmToProto(tmPk) 76 if err != nil { 77 return err 78 } 79 80 // Instead of just doing `*m = *protoPk`, we prefer to modify in-place the 81 // existing Anys inside `m` (instead of allocating new Anys), as so not to 82 // break the `.compat` fields in the existing Anys. 83 if m.PubKeys == nil { 84 m.PubKeys = make([]*types.Any, len(tmPk.PubKeys)) 85 } 86 for i := range m.PubKeys { 87 if m.PubKeys[i] == nil { 88 // create the compat jsonBz value 89 bz, err := AminoCdc.MarshalJSON(tmPk.PubKeys[i]) 90 if err != nil { 91 return err 92 } 93 94 m.PubKeys[i] = protoPk.PubKeys[i] 95 // UnmarshalJSON(): 96 // just sets the compat.jsonBz value. 97 // always succeeds: err == nil 98 if err := m.PubKeys[i].UnmarshalJSON(bz); err != nil { 99 return err 100 } 101 } else { 102 m.PubKeys[i].TypeUrl = protoPk.PubKeys[i].TypeUrl 103 m.PubKeys[i].Value = protoPk.PubKeys[i].Value 104 } 105 } 106 m.Threshold = protoPk.Threshold 107 108 return nil 109 }