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  }