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