github.com/cosmos/cosmos-sdk@v0.50.10/client/tx/aux_builder.go (about)

     1  package tx
     2  
     3  import (
     4  	"context"
     5  
     6  	"github.com/cosmos/gogoproto/proto"
     7  	"google.golang.org/protobuf/types/known/anypb"
     8  
     9  	txv1beta1 "cosmossdk.io/api/cosmos/tx/v1beta1"
    10  	txsigning "cosmossdk.io/x/tx/signing"
    11  	"cosmossdk.io/x/tx/signing/aminojson"
    12  
    13  	codectypes "github.com/cosmos/cosmos-sdk/codec/types"
    14  	cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
    15  	sdk "github.com/cosmos/cosmos-sdk/types"
    16  	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
    17  	"github.com/cosmos/cosmos-sdk/types/tx"
    18  	"github.com/cosmos/cosmos-sdk/types/tx/signing"
    19  )
    20  
    21  // AuxTxBuilder is a client-side builder for creating an AuxSignerData.
    22  type AuxTxBuilder struct {
    23  	// msgs is used to store the sdk.Msgs that are added to the
    24  	// TxBuilder. It's also added inside body.Messages, because:
    25  	// - b.msgs is used for constructing the AMINO sign bz,
    26  	// - b.body is used for constructing the DIRECT_AUX sign bz.
    27  	msgs          []sdk.Msg
    28  	body          *txv1beta1.TxBody
    29  	auxSignerData *tx.AuxSignerData
    30  }
    31  
    32  // NewAuxTxBuilder creates a new client-side builder for constructing an
    33  // AuxSignerData.
    34  func NewAuxTxBuilder() AuxTxBuilder {
    35  	return AuxTxBuilder{}
    36  }
    37  
    38  // SetAddress sets the aux signer's bech32 address.
    39  func (b *AuxTxBuilder) SetAddress(addr string) {
    40  	b.checkEmptyFields()
    41  
    42  	b.auxSignerData.Address = addr
    43  }
    44  
    45  // SetMemo sets a memo in the tx.
    46  func (b *AuxTxBuilder) SetMemo(memo string) {
    47  	b.checkEmptyFields()
    48  
    49  	b.body.Memo = memo
    50  	b.auxSignerData.SignDoc.BodyBytes = nil
    51  }
    52  
    53  // SetTimeoutHeight sets a timeout height in the tx.
    54  func (b *AuxTxBuilder) SetTimeoutHeight(height uint64) {
    55  	b.checkEmptyFields()
    56  
    57  	b.body.TimeoutHeight = height
    58  	b.auxSignerData.SignDoc.BodyBytes = nil
    59  }
    60  
    61  // SetMsgs sets an array of Msgs in the tx.
    62  func (b *AuxTxBuilder) SetMsgs(msgs ...sdk.Msg) error {
    63  	anys := make([]*anypb.Any, len(msgs))
    64  	for i, msg := range msgs {
    65  		legacyAny, err := codectypes.NewAnyWithValue(msg)
    66  		if err != nil {
    67  			return err
    68  		}
    69  		anys[i] = &anypb.Any{
    70  			TypeUrl: legacyAny.TypeUrl,
    71  			Value:   legacyAny.Value,
    72  		}
    73  	}
    74  
    75  	b.checkEmptyFields()
    76  
    77  	b.msgs = msgs
    78  	b.body.Messages = anys
    79  	b.auxSignerData.SignDoc.BodyBytes = nil
    80  
    81  	return nil
    82  }
    83  
    84  // SetAccountNumber sets the aux signer's account number in the AuxSignerData.
    85  func (b *AuxTxBuilder) SetAccountNumber(accNum uint64) {
    86  	b.checkEmptyFields()
    87  
    88  	b.auxSignerData.SignDoc.AccountNumber = accNum
    89  }
    90  
    91  // SetChainID sets the chain id in the AuxSignerData.
    92  func (b *AuxTxBuilder) SetChainID(chainID string) {
    93  	b.checkEmptyFields()
    94  
    95  	b.auxSignerData.SignDoc.ChainId = chainID
    96  }
    97  
    98  // SetSequence sets the aux signer's sequence in the AuxSignerData.
    99  func (b *AuxTxBuilder) SetSequence(accSeq uint64) {
   100  	b.checkEmptyFields()
   101  
   102  	b.auxSignerData.SignDoc.Sequence = accSeq
   103  }
   104  
   105  // SetPubKey sets the aux signer's pubkey in the AuxSignerData.
   106  func (b *AuxTxBuilder) SetPubKey(pk cryptotypes.PubKey) error {
   107  	anyPk, err := codectypes.NewAnyWithValue(pk)
   108  	if err != nil {
   109  		return err
   110  	}
   111  
   112  	b.checkEmptyFields()
   113  	b.auxSignerData.SignDoc.PublicKey = anyPk
   114  
   115  	return nil
   116  }
   117  
   118  // SetSignMode sets the aux signer's sign mode. Allowed sign modes are
   119  // DIRECT_AUX and LEGACY_AMINO_JSON.
   120  func (b *AuxTxBuilder) SetSignMode(mode signing.SignMode) error {
   121  	switch mode {
   122  	case signing.SignMode_SIGN_MODE_DIRECT_AUX, signing.SignMode_SIGN_MODE_LEGACY_AMINO_JSON:
   123  	default:
   124  		return sdkerrors.ErrInvalidRequest.Wrapf("AuxTxBuilder can only sign with %s or %s",
   125  			signing.SignMode_SIGN_MODE_DIRECT_AUX, signing.SignMode_SIGN_MODE_LEGACY_AMINO_JSON)
   126  	}
   127  
   128  	b.auxSignerData.Mode = mode
   129  	return nil
   130  }
   131  
   132  // SetSignature sets the aux signer's signature in the AuxSignerData.
   133  func (b *AuxTxBuilder) SetSignature(sig []byte) {
   134  	b.checkEmptyFields()
   135  
   136  	b.auxSignerData.Sig = sig
   137  }
   138  
   139  // SetExtensionOptions sets the aux signer's extension options.
   140  func (b *AuxTxBuilder) SetExtensionOptions(extOpts ...*codectypes.Any) {
   141  	b.checkEmptyFields()
   142  
   143  	anyExtOpts := make([]*anypb.Any, len(extOpts))
   144  	for i, extOpt := range extOpts {
   145  		anyExtOpts[i] = &anypb.Any{
   146  			TypeUrl: extOpt.TypeUrl,
   147  			Value:   extOpt.Value,
   148  		}
   149  	}
   150  	b.body.ExtensionOptions = anyExtOpts
   151  	b.auxSignerData.SignDoc.BodyBytes = nil
   152  }
   153  
   154  // SetSignature sets the aux signer's signature.
   155  func (b *AuxTxBuilder) SetNonCriticalExtensionOptions(extOpts ...*codectypes.Any) {
   156  	b.checkEmptyFields()
   157  
   158  	anyNonCritExtOpts := make([]*anypb.Any, len(extOpts))
   159  	for i, extOpt := range extOpts {
   160  		anyNonCritExtOpts[i] = &anypb.Any{
   161  			TypeUrl: extOpt.TypeUrl,
   162  			Value:   extOpt.Value,
   163  		}
   164  	}
   165  	b.body.NonCriticalExtensionOptions = anyNonCritExtOpts
   166  	b.auxSignerData.SignDoc.BodyBytes = nil
   167  }
   168  
   169  // GetSignBytes returns the builder's sign bytes.
   170  func (b *AuxTxBuilder) GetSignBytes() ([]byte, error) {
   171  	auxTx := b.auxSignerData
   172  	if auxTx == nil {
   173  		return nil, sdkerrors.ErrLogic.Wrap("aux tx is nil, call setters on AuxTxBuilder first")
   174  	}
   175  
   176  	body := b.body
   177  	if body == nil {
   178  		return nil, sdkerrors.ErrLogic.Wrap("tx body is nil, call setters on AuxTxBuilder first")
   179  	}
   180  
   181  	sd := auxTx.SignDoc
   182  	if sd == nil {
   183  		return nil, sdkerrors.ErrLogic.Wrap("sign doc is nil, call setters on AuxTxBuilder first")
   184  	}
   185  
   186  	bodyBz, err := proto.Marshal(body)
   187  	if err != nil {
   188  		return nil, err
   189  	}
   190  
   191  	sd.BodyBytes = bodyBz
   192  	if err = sd.ValidateBasic(); err != nil {
   193  		return nil, err
   194  	}
   195  
   196  	var signBz []byte
   197  	switch b.auxSignerData.Mode {
   198  	case signing.SignMode_SIGN_MODE_DIRECT_AUX:
   199  		{
   200  			signBz, err = proto.Marshal(b.auxSignerData.SignDoc)
   201  			if err != nil {
   202  				return nil, err
   203  			}
   204  		}
   205  	case signing.SignMode_SIGN_MODE_LEGACY_AMINO_JSON:
   206  		{
   207  			handler := aminojson.NewSignModeHandler(aminojson.SignModeHandlerOptions{
   208  				FileResolver: proto.HybridResolver,
   209  			})
   210  
   211  			auxBody := &txv1beta1.TxBody{
   212  				Messages:      body.Messages,
   213  				Memo:          body.Memo,
   214  				TimeoutHeight: body.TimeoutHeight,
   215  				// AuxTxBuilder has no concern with extension options, so we set them to nil.
   216  				// This preserves pre-PR#16025 behavior where extension options were ignored, this code path:
   217  				// https://github.com/cosmos/cosmos-sdk/blob/ac3c209326a26b46f65a6cc6f5b5ebf6beb79b38/client/tx/aux_builder.go#L193
   218  				// https://github.com/cosmos/cosmos-sdk/blob/ac3c209326a26b46f65a6cc6f5b5ebf6beb79b38/x/auth/migrations/legacytx/stdsign.go#L49
   219  				ExtensionOptions:            nil,
   220  				NonCriticalExtensionOptions: nil,
   221  			}
   222  
   223  			signBz, err = handler.GetSignBytes(
   224  				context.Background(),
   225  				txsigning.SignerData{
   226  					Address:       b.auxSignerData.Address,
   227  					ChainID:       b.auxSignerData.SignDoc.ChainId,
   228  					AccountNumber: b.auxSignerData.SignDoc.AccountNumber,
   229  					Sequence:      b.auxSignerData.SignDoc.Sequence,
   230  					PubKey:        nil,
   231  				},
   232  				txsigning.TxData{
   233  					Body: auxBody,
   234  					AuthInfo: &txv1beta1.AuthInfo{
   235  						SignerInfos: nil,
   236  						// Aux signer never signs over fee.
   237  						// For LEGACY_AMINO_JSON, we use the convention to sign
   238  						// over empty fees.
   239  						// ref: https://github.com/cosmos/cosmos-sdk/pull/10348
   240  						Fee: &txv1beta1.Fee{},
   241  					},
   242  				},
   243  			)
   244  			return signBz, err
   245  		}
   246  	default:
   247  		return nil, sdkerrors.ErrInvalidRequest.Wrapf("got unknown sign mode %s", b.auxSignerData.Mode)
   248  	}
   249  
   250  	return signBz, nil
   251  }
   252  
   253  // GetAuxSignerData returns the builder's AuxSignerData.
   254  func (b *AuxTxBuilder) GetAuxSignerData() (tx.AuxSignerData, error) {
   255  	if err := b.auxSignerData.ValidateBasic(); err != nil {
   256  		return tx.AuxSignerData{}, err
   257  	}
   258  
   259  	return *b.auxSignerData, nil
   260  }
   261  
   262  func (b *AuxTxBuilder) checkEmptyFields() {
   263  	if b.body == nil {
   264  		b.body = &txv1beta1.TxBody{}
   265  	}
   266  
   267  	if b.auxSignerData == nil {
   268  		b.auxSignerData = &tx.AuxSignerData{}
   269  		if b.auxSignerData.SignDoc == nil {
   270  			b.auxSignerData.SignDoc = &tx.SignDocDirectAux{}
   271  		}
   272  	}
   273  }