github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/app/ante/account.go (about)

     1  package ante
     2  
     3  import (
     4  	"bytes"
     5  	"math/big"
     6  	"strconv"
     7  	"strings"
     8  
     9  	"github.com/ethereum/go-ethereum/common"
    10  	ethcore "github.com/ethereum/go-ethereum/core"
    11  	ethtypes "github.com/ethereum/go-ethereum/core/types"
    12  	"github.com/fibonacci-chain/fbc/libs/cosmos-sdk/baseapp"
    13  	sdk "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types"
    14  	sdkerrors "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types/errors"
    15  	"github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types/innertx"
    16  	"github.com/fibonacci-chain/fbc/libs/cosmos-sdk/x/auth"
    17  	"github.com/fibonacci-chain/fbc/libs/cosmos-sdk/x/auth/exported"
    18  	"github.com/fibonacci-chain/fbc/libs/cosmos-sdk/x/auth/types"
    19  	evmtypes "github.com/fibonacci-chain/fbc/x/evm/types"
    20  )
    21  
    22  type accountKeeperInterface interface {
    23  	SetAccount(ctx sdk.Context, acc exported.Account)
    24  }
    25  
    26  type AccountAnteDecorator struct {
    27  	ak        auth.AccountKeeper
    28  	sk        types.SupplyKeeper
    29  	evmKeeper EVMKeeper
    30  }
    31  
    32  // NewAccountVerificationDecorator creates a new AccountVerificationDecorator
    33  func NewAccountAnteDecorator(ak auth.AccountKeeper, ek EVMKeeper, sk types.SupplyKeeper) AccountAnteDecorator {
    34  	return AccountAnteDecorator{
    35  		ak:        ak,
    36  		sk:        sk,
    37  		evmKeeper: ek,
    38  	}
    39  }
    40  
    41  func accountVerification(ctx *sdk.Context, acc exported.Account, tx *evmtypes.MsgEthereumTx) error {
    42  	if ctx.BlockHeight() == 0 && acc.GetAccountNumber() != 0 {
    43  		return sdkerrors.Wrapf(
    44  			sdkerrors.ErrInvalidSequence,
    45  			"invalid account number for height zero (got %d)", acc.GetAccountNumber(),
    46  		)
    47  	}
    48  
    49  	const evmDenom = sdk.DefaultBondDenom
    50  
    51  	feeInts := feeIntsPool.Get().(*[2]big.Int)
    52  	defer feeIntsPool.Put(feeInts)
    53  
    54  	// validate sender has enough funds to pay for gas cost
    55  	balance := acc.GetCoins().AmountOf(evmDenom)
    56  	if balance.Int.Cmp(tx.CalcCostTo(&feeInts[0])) < 0 {
    57  		return sdkerrors.Wrapf(
    58  			sdkerrors.ErrInsufficientFunds,
    59  			"sender balance < tx gas cost (%s%s < %s%s)", balance.String(), evmDenom, sdk.NewDecFromBigIntWithPrec(tx.Cost(), sdk.Precision).String(), evmDenom,
    60  		)
    61  	}
    62  	return nil
    63  }
    64  
    65  func nonceVerificationInCheckTx(ctx sdk.Context, seq uint64, msgEthTx *evmtypes.MsgEthereumTx, isReCheckTx bool) error {
    66  	if isReCheckTx {
    67  		// recheckTx mode
    68  		// sequence must strictly increasing
    69  		if msgEthTx.Data.AccountNonce != seq {
    70  			return sdkerrors.Wrapf(
    71  				sdkerrors.ErrInvalidSequence,
    72  				"invalid nonce; got %d, expected %d", msgEthTx.Data.AccountNonce, seq,
    73  			)
    74  		}
    75  	} else {
    76  		if baseapp.IsMempoolEnablePendingPool() {
    77  			if msgEthTx.Data.AccountNonce < seq {
    78  				return sdkerrors.Wrapf(
    79  					sdkerrors.ErrInvalidSequence,
    80  					"invalid nonce; got %d, expected %d", msgEthTx.Data.AccountNonce, seq,
    81  				)
    82  			}
    83  		} else {
    84  			// checkTx mode
    85  			checkTxModeNonce := seq
    86  			if !baseapp.IsMempoolEnableRecheck() {
    87  				// if is enable recheck, the sequence of checkState will increase after commit(), so we do not need
    88  				// to add pending txs len in the mempool.
    89  				// but, if disable recheck, we will not increase sequence of checkState (even in force recheck case, we
    90  				// will also reset checkState), so we will need to add pending txs len to get the right nonce
    91  				gPool := baseapp.GetGlobalMempool()
    92  				if gPool != nil {
    93  					addr := msgEthTx.GetSender(ctx)
    94  					if pendingNonce, ok := gPool.GetPendingNonce(addr); ok {
    95  						checkTxModeNonce = pendingNonce + 1
    96  					}
    97  				}
    98  			}
    99  
   100  			if baseapp.IsMempoolEnableSort() {
   101  				if msgEthTx.Data.AccountNonce < seq || msgEthTx.Data.AccountNonce > checkTxModeNonce {
   102  					accNonceStr := strconv.FormatUint(msgEthTx.Data.AccountNonce, 10)
   103  					seqStr := strconv.FormatUint(seq, 10)
   104  					checkTxModeNonceStr := strconv.FormatUint(checkTxModeNonce, 10)
   105  
   106  					errStr := strings.Join([]string{
   107  						"invalid nonce; got ", accNonceStr,
   108  						", expected in the range of [", seqStr, ", ", checkTxModeNonceStr, "]"},
   109  						"")
   110  
   111  					return sdkerrors.WrapNoStack(sdkerrors.ErrInvalidSequence, errStr)
   112  				}
   113  			} else {
   114  				if msgEthTx.Data.AccountNonce != checkTxModeNonce {
   115  					accNonceStr := strconv.FormatUint(msgEthTx.Data.AccountNonce, 10)
   116  					checkTxModeNonceStr := strconv.FormatUint(checkTxModeNonce, 10)
   117  
   118  					errStr := strings.Join([]string{
   119  						"invalid nonce; got ", accNonceStr, ", expected ", checkTxModeNonceStr},
   120  						"")
   121  
   122  					return sdkerrors.WrapNoStack(sdkerrors.ErrInvalidSequence, errStr)
   123  				}
   124  			}
   125  		}
   126  	}
   127  	return nil
   128  }
   129  
   130  func nonceVerification(ctx sdk.Context, acc exported.Account, msgEthTx *evmtypes.MsgEthereumTx) (sdk.Context, error) {
   131  	seq := acc.GetSequence()
   132  	// if multiple transactions are submitted in succession with increasing nonces,
   133  	// all will be rejected except the first, since the first needs to be included in a block
   134  	// before the sequence increments
   135  	if ctx.IsCheckTx() {
   136  		ctx.SetAccountNonce(seq)
   137  		// will be checkTx and RecheckTx mode
   138  		err := nonceVerificationInCheckTx(ctx, seq, msgEthTx, ctx.IsReCheckTx())
   139  		if err != nil {
   140  			return ctx, err
   141  		}
   142  	} else {
   143  		// only deliverTx mode
   144  		if msgEthTx.Data.AccountNonce != seq {
   145  			return ctx, sdkerrors.Wrapf(
   146  				sdkerrors.ErrInvalidSequence,
   147  				"invalid nonce; got %d, expected %d", msgEthTx.Data.AccountNonce, seq,
   148  			)
   149  		}
   150  	}
   151  	return ctx, nil
   152  }
   153  
   154  func ethGasConsume(ik innertx.InnerTxKeeper, ak accountKeeperInterface, sk types.SupplyKeeper, ctx *sdk.Context, acc exported.Account, accGetGas sdk.Gas, msgEthTx *evmtypes.MsgEthereumTx, simulate bool) error {
   155  	gasLimit := msgEthTx.GetGas()
   156  	gas, err := ethcore.IntrinsicGas(msgEthTx.Data.Payload, []ethtypes.AccessTuple{}, msgEthTx.To() == nil, true, false)
   157  	if err != nil {
   158  		return sdkerrors.Wrap(err, "failed to compute intrinsic gas cost")
   159  	}
   160  
   161  	// intrinsic gas verification during CheckTx
   162  	if ctx.IsCheckTx() && gasLimit < gas {
   163  		return sdkerrors.Wrapf(sdkerrors.ErrOutOfGas, "intrinsic gas too low: %d < %d", gasLimit, gas)
   164  	}
   165  
   166  	// Charge sender for gas up to limit
   167  	if gasLimit != 0 {
   168  		feeInts := feeIntsPool.Get().(*[2]big.Int)
   169  		defer feeIntsPool.Put(feeInts)
   170  		// Cost calculates the fees paid to validators based on gas limit and price
   171  		cost := (&feeInts[0]).SetUint64(gasLimit)
   172  		cost = cost.Mul(msgEthTx.Data.Price, cost)
   173  
   174  		const evmDenom = sdk.DefaultBondDenom
   175  
   176  		feeAmt := sdk.NewDecCoinsFromDec(evmDenom, sdk.NewDecWithBigIntAndPrec(cost, sdk.Precision))
   177  
   178  		ctx.UpdateFromAccountCache(acc, accGetGas)
   179  
   180  		err = deductFees(ik, ak, sk, *ctx, acc, feeAmt)
   181  		if err != nil {
   182  			return err
   183  		}
   184  	}
   185  
   186  	// Set gas meter after ante handler to ignore gaskv costs
   187  	auth.SetGasMeter(simulate, ctx, gasLimit)
   188  	return nil
   189  }
   190  
   191  func deductFees(ik innertx.InnerTxKeeper, ak accountKeeperInterface, sk types.SupplyKeeper, ctx sdk.Context, acc exported.Account, fees sdk.Coins) error {
   192  	blockTime := ctx.BlockTime()
   193  	coins := acc.GetCoins()
   194  
   195  	if !fees.IsValid() {
   196  		return sdkerrors.Wrapf(sdkerrors.ErrInsufficientFee, "invalid fee amount: %s", fees)
   197  	}
   198  
   199  	// verify the account has enough funds to pay for fees
   200  	balance, hasNeg := coins.SafeSub(fees)
   201  	if hasNeg {
   202  		return sdkerrors.Wrapf(sdkerrors.ErrInsufficientFunds,
   203  			"insufficient funds to pay for fees; %s < %s", coins, fees)
   204  	}
   205  
   206  	// Validate the account has enough "spendable" coins as this will cover cases
   207  	// such as vesting accounts.
   208  	spendableCoins := acc.SpendableCoins(blockTime)
   209  	if _, hasNeg := spendableCoins.SafeSub(fees); hasNeg {
   210  		return sdkerrors.Wrapf(sdkerrors.ErrInsufficientFunds,
   211  			"insufficient funds to pay for fees; %s < %s", spendableCoins, fees)
   212  	}
   213  
   214  	// set coins and record innertx
   215  	err := acc.SetCoins(balance)
   216  	if !ctx.IsCheckTx() {
   217  		toAcc := sk.GetModuleAddress(types.FeeCollectorName)
   218  		ik.UpdateInnerTx(ctx.TxBytes(), ctx.BlockHeight(), innertx.CosmosDepth, acc.GetAddress(), toAcc, innertx.CosmosCallType, innertx.SendCallName, fees, err)
   219  	}
   220  	if err != nil {
   221  		return err
   222  	}
   223  	ak.SetAccount(ctx, acc)
   224  
   225  	return nil
   226  }
   227  
   228  func incrementSeq(ctx sdk.Context, msgEthTx *evmtypes.MsgEthereumTx, accAddress sdk.AccAddress, ak auth.AccountKeeper, acc exported.Account) {
   229  	if ctx.IsCheckTx() && !ctx.IsReCheckTx() && !baseapp.IsMempoolEnableRecheck() && !ctx.IsTraceTx() {
   230  		return
   231  	}
   232  
   233  	// get and set account must be called with an infinite gas meter in order to prevent
   234  	// additional gas from being deducted.
   235  	infGasMeter := sdk.GetReusableInfiniteGasMeter()
   236  	defer sdk.ReturnInfiniteGasMeter(infGasMeter)
   237  	ctx.SetGasMeter(infGasMeter)
   238  
   239  	// increment sequence of all signers
   240  	// eth tx only has one signer
   241  	if accAddress.Empty() {
   242  		accAddress = msgEthTx.AccountAddress()
   243  	}
   244  	var sacc exported.Account
   245  	if acc != nil && bytes.Equal(accAddress, acc.GetAddress()) {
   246  		// because we use infinite gas meter, we can don't care about the gas
   247  		sacc = acc
   248  	} else {
   249  		sacc = ak.GetAccount(ctx, accAddress)
   250  	}
   251  	seq := sacc.GetSequence()
   252  	if !baseapp.IsMempoolEnablePendingPool() {
   253  		seq++
   254  	} else if msgEthTx.Data.AccountNonce == seq {
   255  		seq++
   256  	}
   257  	if err := sacc.SetSequence(seq); err != nil {
   258  		panic(err)
   259  	}
   260  	ak.SetAccount(ctx, sacc)
   261  
   262  	return
   263  }
   264  
   265  func (avd AccountAnteDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) {
   266  	msgEthTx, ok := tx.(*evmtypes.MsgEthereumTx)
   267  	if !ok {
   268  		return ctx, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "invalid transaction type: %T", tx)
   269  	}
   270  
   271  	var acc exported.Account
   272  	var getAccGasUsed sdk.Gas
   273  
   274  	address := msgEthTx.AccountAddress()
   275  	if address.Empty() && ctx.From() != "" {
   276  		msgEthTx.SetFrom(ctx.From())
   277  		address = msgEthTx.AccountAddress()
   278  	}
   279  
   280  	if !simulate {
   281  		if address.Empty() {
   282  			panic("sender address cannot be empty")
   283  		}
   284  		if ctx.IsCheckTx() {
   285  			acc = avd.ak.GetAccount(ctx, address)
   286  			if acc == nil {
   287  				acc = avd.ak.NewAccountWithAddress(ctx, address)
   288  				avd.ak.SetAccount(ctx, acc)
   289  			}
   290  			// on InitChain make sure account number == 0
   291  			err = accountVerification(&ctx, acc, msgEthTx)
   292  			if err != nil {
   293  				return ctx, err
   294  			}
   295  		}
   296  
   297  		acc, getAccGasUsed = getAccount(&avd.ak, &ctx, address, acc)
   298  		if acc == nil {
   299  			return ctx, sdkerrors.Wrapf(
   300  				sdkerrors.ErrUnknownAddress,
   301  				"account %s (%s) is nil", common.BytesToAddress(address.Bytes()), address,
   302  			)
   303  		}
   304  
   305  		// account would not be updated
   306  		ctx, err = nonceVerification(ctx, acc, msgEthTx)
   307  		if err != nil {
   308  			return ctx, err
   309  		}
   310  
   311  		// consume gas for compatible
   312  		ctx.GasMeter().ConsumeGas(getAccGasUsed, "get account")
   313  
   314  		ctx.EnableAccountCache()
   315  		// account would be updated
   316  		err = ethGasConsume(avd.evmKeeper, avd.ak, avd.sk, &ctx, acc, getAccGasUsed, msgEthTx, simulate)
   317  		acc = nil
   318  		acc, _ = ctx.GetFromAccountCacheData().(exported.Account)
   319  		ctx.DisableAccountCache()
   320  		if err != nil {
   321  			return ctx, err
   322  		}
   323  	}
   324  
   325  	incrementSeq(ctx, msgEthTx, address, avd.ak, acc)
   326  
   327  	return next(ctx, tx, simulate)
   328  }