github.com/gnolang/gno@v0.0.0-20240520182011-228e9d0192ce/tm2/pkg/sdk/auth/ante.go (about)

     1  package auth
     2  
     3  import (
     4  	"encoding/hex"
     5  	"fmt"
     6  	"math/big"
     7  
     8  	"github.com/gnolang/gno/tm2/pkg/amino"
     9  	"github.com/gnolang/gno/tm2/pkg/crypto"
    10  	"github.com/gnolang/gno/tm2/pkg/crypto/ed25519"
    11  	"github.com/gnolang/gno/tm2/pkg/crypto/multisig"
    12  	"github.com/gnolang/gno/tm2/pkg/crypto/secp256k1"
    13  	"github.com/gnolang/gno/tm2/pkg/sdk"
    14  	"github.com/gnolang/gno/tm2/pkg/std"
    15  	"github.com/gnolang/gno/tm2/pkg/store"
    16  )
    17  
    18  var (
    19  	// simulation signature values used to estimate gas consumption
    20  	simSecp256k1Pubkey secp256k1.PubKeySecp256k1
    21  	simSecp256k1Sig    [64]byte
    22  )
    23  
    24  func init() {
    25  	// This decodes a valid hex string into a sepc256k1Pubkey for use in transaction simulation
    26  	bz, _ := hex.DecodeString("035AD6810A47F073553FF30D2FCC7E0D3B1C0B74B61A1AAA2582344037151E143A")
    27  	copy(simSecp256k1Pubkey[:], bz)
    28  }
    29  
    30  // SignatureVerificationGasConsumer is the type of function that is used to both consume gas when verifying signatures
    31  // and also to accept or reject different types of PubKey's. This is where apps can define their own PubKey
    32  type SignatureVerificationGasConsumer = func(meter store.GasMeter, sig []byte, pubkey crypto.PubKey, params Params) sdk.Result
    33  
    34  type AnteOptions struct {
    35  	// If verifyGenesisSignatures is false, does not check signatures when Height==0.
    36  	// This is useful for development, and maybe production chains.
    37  	// Always check your settings and inspect genesis transactions.
    38  	VerifyGenesisSignatures bool
    39  }
    40  
    41  // NewAnteHandler returns an AnteHandler that checks and increments sequence
    42  // numbers, checks signatures & account numbers, and deducts fees from the first
    43  // signer.
    44  func NewAnteHandler(ak AccountKeeper, bank BankKeeperI, sigGasConsumer SignatureVerificationGasConsumer, opts AnteOptions) sdk.AnteHandler {
    45  	return func(
    46  		ctx sdk.Context, tx std.Tx, simulate bool,
    47  	) (newCtx sdk.Context, res sdk.Result, abort bool) {
    48  		// Ensure that the gas wanted is not greater than the max allowed.
    49  		consParams := ctx.ConsensusParams()
    50  		if consParams.Block.MaxGas == -1 {
    51  			// no gas bounds (not recommended)
    52  		} else if consParams.Block.MaxGas < tx.Fee.GasWanted {
    53  			// tx gas-wanted too large.
    54  			res = abciResult(std.ErrInvalidGasWanted(
    55  				fmt.Sprintf(
    56  					"invalid gas-wanted; got: %d block-max-gas: %d",
    57  					tx.Fee.GasWanted, consParams.Block.MaxGas,
    58  				),
    59  			))
    60  			return ctx, res, true
    61  		}
    62  
    63  		// Ensure that the provided fees meet a minimum threshold for the validator,
    64  		// if this is a CheckTx. This is only for local mempool purposes, and thus
    65  		// is only run upon checktx.
    66  		if ctx.IsCheckTx() && !simulate {
    67  			res := EnsureSufficientMempoolFees(ctx, tx.Fee)
    68  			if !res.IsOK() {
    69  				return ctx, res, true
    70  			}
    71  		}
    72  
    73  		newCtx = SetGasMeter(simulate, ctx, tx.Fee.GasWanted)
    74  
    75  		// AnteHandlers must have their own defer/recover in order for the BaseApp
    76  		// to know how much gas was used! This is because the GasMeter is created in
    77  		// the AnteHandler, but if it panics the context won't be set properly in
    78  		// runTx's recover call.
    79  		defer func() {
    80  			if r := recover(); r != nil {
    81  				switch ex := r.(type) {
    82  				case store.OutOfGasException:
    83  					log := fmt.Sprintf(
    84  						"out of gas in location: %v; gasWanted: %d, gasUsed: %d",
    85  						ex.Descriptor, tx.Fee.GasWanted, newCtx.GasMeter().GasConsumed(),
    86  					)
    87  					res = abciResult(std.ErrOutOfGas(log))
    88  
    89  					res.GasWanted = tx.Fee.GasWanted
    90  					res.GasUsed = newCtx.GasMeter().GasConsumed()
    91  					abort = true
    92  				default:
    93  					panic(r)
    94  				}
    95  			}
    96  		}()
    97  
    98  		// Get params from context.
    99  		params := ctx.Value(AuthParamsContextKey{}).(Params)
   100  		if res := ValidateSigCount(tx, params); !res.IsOK() {
   101  			return newCtx, res, true
   102  		}
   103  
   104  		if err := tx.ValidateBasic(); err != nil {
   105  			return newCtx, abciResult(err), true
   106  		}
   107  
   108  		newCtx.GasMeter().ConsumeGas(params.TxSizeCostPerByte*store.Gas(len(newCtx.TxBytes())), "txSize")
   109  
   110  		if res := ValidateMemo(tx, params); !res.IsOK() {
   111  			return newCtx, res, true
   112  		}
   113  
   114  		// stdSigs contains the sequence number, account number, and signatures.
   115  		// When simulating, this would just be a 0-length slice.
   116  		signerAddrs := tx.GetSigners()
   117  		signerAccs := make([]std.Account, len(signerAddrs))
   118  		isGenesis := ctx.BlockHeight() == 0
   119  
   120  		// fetch first signer, who's going to pay the fees
   121  		signerAccs[0], res = GetSignerAcc(newCtx, ak, signerAddrs[0])
   122  		if !res.IsOK() {
   123  			return newCtx, res, true
   124  		}
   125  
   126  		// deduct the fees
   127  		if !tx.Fee.GasFee.IsZero() {
   128  			res = DeductFees(bank, newCtx, signerAccs[0], std.Coins{tx.Fee.GasFee})
   129  			if !res.IsOK() {
   130  				return newCtx, res, true
   131  			}
   132  
   133  			// reload the account as fees have been deducted
   134  			signerAccs[0] = ak.GetAccount(newCtx, signerAccs[0].GetAddress())
   135  		}
   136  
   137  		// stdSigs contains the sequence number, account number, and signatures.
   138  		// When simulating, this would just be a 0-length slice.
   139  		stdSigs := tx.GetSignatures()
   140  
   141  		for i := 0; i < len(stdSigs); i++ {
   142  			// skip the fee payer, account is cached and fees were deducted already
   143  			if i != 0 {
   144  				signerAccs[i], res = GetSignerAcc(newCtx, ak, signerAddrs[i])
   145  				if !res.IsOK() {
   146  					return newCtx, res, true
   147  				}
   148  			}
   149  
   150  			// check signature, return account with incremented nonce
   151  			sacc := signerAccs[i]
   152  			if isGenesis && !opts.VerifyGenesisSignatures {
   153  				// No signatures are needed for genesis.
   154  			} else {
   155  				// Check signature
   156  				signBytes, err := GetSignBytes(newCtx.ChainID(), tx, sacc, isGenesis)
   157  				if err != nil {
   158  					return newCtx, res, true
   159  				}
   160  
   161  				signerAccs[i], res = processSig(newCtx, sacc, stdSigs[i], signBytes, simulate, params, sigGasConsumer)
   162  				if !res.IsOK() {
   163  					return newCtx, res, true
   164  				}
   165  			}
   166  			ak.SetAccount(newCtx, signerAccs[i])
   167  		}
   168  
   169  		// TODO: tx tags (?)
   170  		return newCtx, sdk.Result{GasWanted: tx.Fee.GasWanted}, false // continue...
   171  	}
   172  }
   173  
   174  // GetSignerAcc returns an account for a given address that is expected to sign
   175  // a transaction.
   176  func GetSignerAcc(ctx sdk.Context, ak AccountKeeper, addr crypto.Address) (std.Account, sdk.Result) {
   177  	if acc := ak.GetAccount(ctx, addr); acc != nil {
   178  		return acc, sdk.Result{}
   179  	}
   180  	return nil, abciResult(std.ErrUnknownAddress(fmt.Sprintf("account %s does not exist", addr)))
   181  }
   182  
   183  // ValidateSigCount validates that the transaction has a valid cumulative total
   184  // amount of signatures.
   185  func ValidateSigCount(tx std.Tx, params Params) sdk.Result {
   186  	stdSigs := tx.GetSignatures()
   187  
   188  	sigCount := 0
   189  	for i := 0; i < len(stdSigs); i++ {
   190  		sigCount += std.CountSubKeys(stdSigs[i].PubKey)
   191  		if int64(sigCount) > params.TxSigLimit {
   192  			return abciResult(std.ErrTooManySignatures(
   193  				fmt.Sprintf("signatures: %d, limit: %d", sigCount, params.TxSigLimit),
   194  			))
   195  		}
   196  	}
   197  
   198  	return sdk.Result{}
   199  }
   200  
   201  // ValidateMemo validates the memo size.
   202  func ValidateMemo(tx std.Tx, params Params) sdk.Result {
   203  	memoLength := len(tx.GetMemo())
   204  	if int64(memoLength) > params.MaxMemoBytes {
   205  		return abciResult(std.ErrMemoTooLarge(
   206  			fmt.Sprintf(
   207  				"maximum number of bytes is %d but received %d bytes",
   208  				params.MaxMemoBytes, memoLength,
   209  			),
   210  		))
   211  	}
   212  
   213  	return sdk.Result{}
   214  }
   215  
   216  // verify the signature and increment the sequence. If the account doesn't
   217  // have a pubkey, set it.
   218  func processSig(
   219  	ctx sdk.Context, acc std.Account, sig std.Signature, signBytes []byte, simulate bool, params Params,
   220  	sigGasConsumer SignatureVerificationGasConsumer,
   221  ) (updatedAcc std.Account, res sdk.Result) {
   222  	pubKey, res := ProcessPubKey(acc, sig, simulate)
   223  	if !res.IsOK() {
   224  		return nil, res
   225  	}
   226  
   227  	err := acc.SetPubKey(pubKey)
   228  	if err != nil {
   229  		return nil, abciResult(std.ErrInternal("setting PubKey on signer's account"))
   230  	}
   231  
   232  	if simulate {
   233  		// Simulated txs should not contain a signature and are not required to
   234  		// contain a pubkey, so we must account for tx size of including a
   235  		// std.Signature (Amino encoding) and simulate gas consumption
   236  		// (assuming a SECP256k1 simulation key).
   237  		consumeSimSigGas(ctx.GasMeter(), pubKey, sig, params)
   238  	}
   239  
   240  	if res := sigGasConsumer(ctx.GasMeter(), sig.Signature, pubKey, params); !res.IsOK() {
   241  		return nil, res
   242  	}
   243  
   244  	if !simulate && !pubKey.VerifyBytes(signBytes, sig.Signature) {
   245  		return nil, abciResult(std.ErrUnauthorized("signature verification failed; verify correct account, sequence, and chain-id"))
   246  	}
   247  
   248  	if err := acc.SetSequence(acc.GetSequence() + 1); err != nil {
   249  		panic(err)
   250  	}
   251  
   252  	return acc, res
   253  }
   254  
   255  func consumeSimSigGas(gasmeter store.GasMeter, pubkey crypto.PubKey, sig std.Signature, params Params) {
   256  	simSig := std.Signature{PubKey: pubkey}
   257  	if len(sig.Signature) == 0 {
   258  		simSig.Signature = simSecp256k1Sig[:]
   259  	}
   260  
   261  	sigBz := amino.MustMarshalSized(simSig)
   262  	cost := store.Gas(len(sigBz) + 6)
   263  
   264  	// If the pubkey is a multi-signature pubkey, then we estimate for the maximum
   265  	// number of signers.
   266  	if _, ok := pubkey.(multisig.PubKeyMultisigThreshold); ok {
   267  		cost *= params.TxSigLimit
   268  	}
   269  
   270  	gasmeter.ConsumeGas(params.TxSizeCostPerByte*cost, "txSize")
   271  }
   272  
   273  // ProcessPubKey verifies that the given account address matches that of the
   274  // std.Signature. In addition, it will set the public key of the account if it
   275  // has not been set.
   276  func ProcessPubKey(acc std.Account, sig std.Signature, simulate bool) (crypto.PubKey, sdk.Result) {
   277  	// If pubkey is not known for account, set it from the std.Signature.
   278  	pubKey := acc.GetPubKey()
   279  	if simulate {
   280  		// In simulate mode the transaction comes with no signatures, thus if the
   281  		// account's pubkey is nil, both signature verification and gasKVStore.Set()
   282  		// shall consume the largest amount, i.e. it takes more gas to verify
   283  		// secp256k1 keys than ed25519 ones.
   284  		if pubKey == nil {
   285  			return simSecp256k1Pubkey, sdk.Result{}
   286  		}
   287  
   288  		return pubKey, sdk.Result{}
   289  	}
   290  
   291  	if pubKey == nil {
   292  		pubKey = sig.PubKey
   293  		if pubKey == nil {
   294  			return nil, abciResult(std.ErrInvalidPubKey("PubKey not found"))
   295  		}
   296  
   297  		if pubKey.Address() != acc.GetAddress() {
   298  			return nil, abciResult(std.ErrInvalidPubKey(
   299  				fmt.Sprintf("PubKey does not match Signer address %s", acc.GetAddress())))
   300  		}
   301  	}
   302  
   303  	return pubKey, sdk.Result{}
   304  }
   305  
   306  // DefaultSigVerificationGasConsumer is the default implementation of
   307  // SignatureVerificationGasConsumer. It consumes gas for signature verification
   308  // based upon the public key type. The cost is fetched from the given params
   309  // and is matched by the concrete type.
   310  func DefaultSigVerificationGasConsumer(
   311  	meter store.GasMeter, sig []byte, pubkey crypto.PubKey, params Params,
   312  ) sdk.Result {
   313  	switch pubkey := pubkey.(type) {
   314  	case ed25519.PubKeyEd25519:
   315  		meter.ConsumeGas(params.SigVerifyCostED25519, "ante verify: ed25519")
   316  		return abciResult(std.ErrInvalidPubKey("ED25519 public keys are unsupported"))
   317  
   318  	case secp256k1.PubKeySecp256k1:
   319  		meter.ConsumeGas(params.SigVerifyCostSecp256k1, "ante verify: secp256k1")
   320  		return sdk.Result{}
   321  
   322  	case multisig.PubKeyMultisigThreshold:
   323  		var multisignature multisig.Multisignature
   324  		amino.MustUnmarshal(sig, &multisignature)
   325  
   326  		consumeMultisignatureVerificationGas(meter, multisignature, pubkey, params)
   327  		return sdk.Result{}
   328  
   329  	default:
   330  		return abciResult(std.ErrInvalidPubKey(fmt.Sprintf("unrecognized public key type: %T", pubkey)))
   331  	}
   332  }
   333  
   334  func consumeMultisignatureVerificationGas(meter store.GasMeter,
   335  	sig multisig.Multisignature, pubkey multisig.PubKeyMultisigThreshold,
   336  	params Params,
   337  ) {
   338  	size := sig.BitArray.Size()
   339  	sigIndex := 0
   340  	for i := 0; i < size; i++ {
   341  		if sig.BitArray.GetIndex(i) {
   342  			DefaultSigVerificationGasConsumer(meter, sig.Sigs[sigIndex], pubkey.PubKeys[i], params)
   343  			sigIndex++
   344  		}
   345  	}
   346  }
   347  
   348  // DeductFees deducts fees from the given account.
   349  //
   350  // NOTE: We could use the CoinKeeper (in addition to the AccountKeeper, because
   351  // the CoinKeeper doesn't give us accounts), but it seems easier to do this.
   352  func DeductFees(bank BankKeeperI, ctx sdk.Context, acc std.Account, fees std.Coins) sdk.Result {
   353  	coins := acc.GetCoins()
   354  
   355  	if !fees.IsValid() {
   356  		return abciResult(std.ErrInsufficientFee(fmt.Sprintf("invalid fee amount: %s", fees)))
   357  	}
   358  
   359  	// verify the account has enough funds to pay for fees
   360  	diff := coins.SubUnsafe(fees)
   361  	if !diff.IsValid() {
   362  		return abciResult(std.ErrInsufficientFunds(
   363  			fmt.Sprintf("insufficient funds to pay for fees; %s < %s", coins, fees),
   364  		))
   365  	}
   366  
   367  	err := bank.SendCoins(ctx, acc.GetAddress(), FeeCollectorAddress(), fees)
   368  	if err != nil {
   369  		return abciResult(err)
   370  	}
   371  
   372  	return sdk.Result{}
   373  }
   374  
   375  // EnsureSufficientMempoolFees verifies that the given transaction has supplied
   376  // enough fees to cover a proposer's minimum fees. A result object is returned
   377  // indicating success or failure.
   378  //
   379  // Contract: This should only be called during CheckTx as it cannot be part of
   380  // consensus.
   381  func EnsureSufficientMempoolFees(ctx sdk.Context, fee std.Fee) sdk.Result {
   382  	minGasPrices := ctx.MinGasPrices()
   383  	if len(minGasPrices) == 0 {
   384  		// no minimum gas price (not recommended)
   385  		// TODO: allow for selective filtering of 0 fee txs.
   386  		return sdk.Result{}
   387  	} else {
   388  		fgw := big.NewInt(fee.GasWanted)
   389  		fga := big.NewInt(fee.GasFee.Amount)
   390  		fgd := fee.GasFee.Denom
   391  
   392  		for _, gp := range minGasPrices {
   393  			gpg := big.NewInt(gp.Gas)
   394  			gpa := big.NewInt(gp.Price.Amount)
   395  			gpd := gp.Price.Denom
   396  
   397  			if fgd == gpd {
   398  				prod1 := big.NewInt(0).Mul(fga, gpg) // fee amount * price gas
   399  				prod2 := big.NewInt(0).Mul(fgw, gpa) // fee gas * price amount
   400  				// This is equivalent to checking
   401  				// That the Fee / GasWanted ratio is greater than or equal to the minimum GasPrice per gas.
   402  				// This approach helps us avoid dealing with configurations where the value of
   403  				// the minimum gas price is set to 0.00001ugnot/gas.
   404  				if prod1.Cmp(prod2) >= 0 {
   405  					return sdk.Result{}
   406  				} else {
   407  					return abciResult(std.ErrInsufficientFee(
   408  						fmt.Sprintf(
   409  							"insufficient fees; got: %q required: %q", fee.GasFee, gp,
   410  						),
   411  					))
   412  				}
   413  			}
   414  		}
   415  	}
   416  
   417  	return abciResult(std.ErrInsufficientFee(
   418  		fmt.Sprintf(
   419  			"insufficient fees; got: %q required (one of): %q", fee.GasFee, minGasPrices,
   420  		),
   421  	))
   422  }
   423  
   424  // SetGasMeter returns a new context with a gas meter set from a given context.
   425  func SetGasMeter(simulate bool, ctx sdk.Context, gasLimit int64) sdk.Context {
   426  	// In various cases such as simulation and during the genesis block, we do not
   427  	// meter any gas utilization.
   428  	if simulate || ctx.BlockHeight() == 0 {
   429  		return ctx.WithGasMeter(store.NewInfiniteGasMeter())
   430  	}
   431  
   432  	return ctx.WithGasMeter(store.NewGasMeter(gasLimit))
   433  }
   434  
   435  // GetSignBytes returns a slice of bytes to sign over for a given transaction
   436  // and an account.
   437  func GetSignBytes(chainID string, tx std.Tx, acc std.Account, genesis bool) ([]byte, error) {
   438  	var accNum uint64
   439  	if !genesis {
   440  		accNum = acc.GetAccountNumber()
   441  	}
   442  
   443  	return std.GetSignaturePayload(
   444  		std.SignDoc{
   445  			ChainID:       chainID,
   446  			AccountNumber: accNum,
   447  			Sequence:      acc.GetSequence(),
   448  			Fee:           tx.Fee,
   449  			Msgs:          tx.Msgs,
   450  			Memo:          tx.Memo,
   451  		},
   452  	)
   453  }
   454  
   455  func abciResult(err error) sdk.Result {
   456  	return sdk.ABCIResultFromError(err)
   457  }