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  }