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 }