github.com/gnolang/gno@v0.0.0-20240520182011-228e9d0192ce/tm2/pkg/std/tx.go (about)

     1  package std
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/gnolang/gno/tm2/pkg/amino"
     7  	"github.com/gnolang/gno/tm2/pkg/crypto"
     8  	"github.com/gnolang/gno/tm2/pkg/crypto/multisig"
     9  )
    10  
    11  var maxGasWanted = int64((1 << 60) - 1) // something smaller than math.MaxInt64
    12  
    13  // Tx is a standard way to wrap a Msg with Fee and Signatures.
    14  // NOTE: the first signature is the fee payer (Signatures must not be nil).
    15  type Tx struct {
    16  	Msgs       []Msg       `json:"msg" yaml:"msg"`
    17  	Fee        Fee         `json:"fee" yaml:"fee"`
    18  	Signatures []Signature `json:"signatures" yaml:"signatures"`
    19  	Memo       string      `json:"memo" yaml:"memo"`
    20  }
    21  
    22  func NewTx(msgs []Msg, fee Fee, sigs []Signature, memo string) Tx {
    23  	return Tx{
    24  		Msgs:       msgs,
    25  		Fee:        fee,
    26  		Signatures: sigs,
    27  		Memo:       memo,
    28  	}
    29  }
    30  
    31  // GetMsgs returns the all the transaction's messages.
    32  func (tx Tx) GetMsgs() []Msg { return tx.Msgs }
    33  
    34  // ValidateBasic does a simple and lightweight validation check that doesn't
    35  // require access to any other information.
    36  func (tx Tx) ValidateBasic() error {
    37  	stdSigs := tx.GetSignatures()
    38  
    39  	if tx.Fee.GasWanted > maxGasWanted {
    40  		return ErrGasOverflow(fmt.Sprintf("invalid gas supplied; %d > %d", tx.Fee.GasWanted, maxGasWanted))
    41  	}
    42  	if !tx.Fee.GasFee.IsValid() {
    43  		return ErrInsufficientFee(fmt.Sprintf("invalid fee %s amount provided", tx.Fee.GasFee))
    44  	}
    45  	if len(stdSigs) == 0 {
    46  		return ErrNoSignatures("no signers")
    47  	}
    48  	if len(stdSigs) != len(tx.GetSigners()) {
    49  		return ErrUnauthorized("wrong number of signers")
    50  	}
    51  
    52  	return nil
    53  }
    54  
    55  // CountSubKeys counts the total number of keys for a multi-sig public key.
    56  func CountSubKeys(pub crypto.PubKey) int {
    57  	v, ok := pub.(multisig.PubKeyMultisigThreshold)
    58  	if !ok {
    59  		return 1
    60  	}
    61  
    62  	numKeys := 0
    63  	for _, subkey := range v.PubKeys {
    64  		numKeys += CountSubKeys(subkey)
    65  	}
    66  
    67  	return numKeys
    68  }
    69  
    70  // GetSigners returns the addresses that must sign the transaction.
    71  // Addresses are returned in a deterministic order.
    72  // They are accumulated from the GetSigners method for each Msg
    73  // in the order they appear in tx.GetMsgs().
    74  // Duplicate addresses will be omitted.
    75  func (tx Tx) GetSigners() []crypto.Address {
    76  	seen := map[string]bool{}
    77  	var signers []crypto.Address
    78  	for _, msg := range tx.GetMsgs() {
    79  		for _, addr := range msg.GetSigners() {
    80  			if !seen[addr.String()] {
    81  				signers = append(signers, addr)
    82  				seen[addr.String()] = true
    83  			}
    84  		}
    85  	}
    86  	return signers
    87  }
    88  
    89  // GetMemo returns the memo
    90  func (tx Tx) GetMemo() string { return tx.Memo }
    91  
    92  // GetSignatures returns the signature of signers who signed the Msg.
    93  // GetSignatures returns the signature of signers who signed the Msg.
    94  // CONTRACT: Length returned is same as length of
    95  // pubkeys returned from MsgKeySigners, and the order
    96  // matches.
    97  // CONTRACT: If the signature is missing (ie the Msg is
    98  // invalid), then the corresponding signature is
    99  // .Empty().
   100  func (tx Tx) GetSignatures() []Signature { return tx.Signatures }
   101  
   102  func (tx Tx) GetSignBytes(chainID string, accountNumber uint64, sequence uint64) ([]byte, error) {
   103  	return GetSignaturePayload(SignDoc{
   104  		ChainID:       chainID,
   105  		AccountNumber: accountNumber,
   106  		Sequence:      sequence,
   107  		Fee:           tx.Fee,
   108  		Msgs:          tx.Msgs,
   109  		Memo:          tx.Memo,
   110  	})
   111  }
   112  
   113  // __________________________________________________________
   114  
   115  // Fee includes the amount of coins paid in fees and the maximum
   116  // gas to be used by the transaction. The ratio yields an effective "gasprice",
   117  // which must be above some miminum to be accepted into the mempool.
   118  type Fee struct {
   119  	GasWanted int64 `json:"gas_wanted" yaml:"gas_wanted"`
   120  	GasFee    Coin  `json:"gas_fee" yaml:"gas_fee"`
   121  }
   122  
   123  // NewFee returns a new instance of Fee
   124  func NewFee(gasWanted int64, gasFee Coin) Fee {
   125  	return Fee{
   126  		GasWanted: gasWanted,
   127  		GasFee:    gasFee,
   128  	}
   129  }
   130  
   131  // Bytes for signing later
   132  func (fee Fee) Bytes() []byte {
   133  	bz, err := amino.MarshalJSON(fee) // TODO
   134  	if err != nil {
   135  		panic(err)
   136  	}
   137  	return bz
   138  }