// Copyright 2021 Evmos Foundation // This file is part of Evmos' Ethermint library. // // The Ethermint library is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // The Ethermint library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License // along with the Ethermint library. If not, see https://github.com/evmos/ethermint/blob/main/LICENSE package keeper import ( "math/big" errorsmod "cosmossdk.io/errors" sdkmath "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" errortypes "github.com/cosmos/cosmos-sdk/types/errors" authante "github.com/cosmos/cosmos-sdk/x/auth/ante" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core" ethtypes "github.com/ethereum/go-ethereum/core/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" "github.com/cerc-io/laconicd/x/evm/types" ) // GetCoinbaseAddress returns the block proposer's validator operator address. func (k Keeper) GetCoinbaseAddress(ctx sdk.Context, proposerAddress sdk.ConsAddress) (common.Address, error) { validator, found := k.stakingKeeper.GetValidatorByConsAddr(ctx, GetProposerAddress(ctx, proposerAddress)) if !found { return common.Address{}, errorsmod.Wrapf( stakingtypes.ErrNoValidatorFound, "failed to retrieve validator from block proposer address %s", proposerAddress.String(), ) } coinbase := common.BytesToAddress(validator.GetOperator()) return coinbase, nil } // GetProposerAddress returns current block proposer's address when provided proposer address is empty. func GetProposerAddress(ctx sdk.Context, proposerAddress sdk.ConsAddress) sdk.ConsAddress { if len(proposerAddress) == 0 { proposerAddress = ctx.BlockHeader().ProposerAddress } return proposerAddress } // DeductTxCostsFromUserBalance deducts the fees from the user balance. Returns an // error if the specified sender address does not exist or the account balance is not sufficient. func (k *Keeper) DeductTxCostsFromUserBalance( ctx sdk.Context, fees sdk.Coins, from common.Address, ) error { // fetch sender account signerAcc, err := authante.GetSignerAcc(ctx, k.accountKeeper, from.Bytes()) if err != nil { return errorsmod.Wrapf(err, "account not found for sender %s", from) } // deduct the full gas cost from the user balance if err := authante.DeductFees(k.bankKeeper, ctx, signerAcc, fees); err != nil { return errorsmod.Wrapf(err, "failed to deduct full gas cost %s from the user %s balance", fees, from) } return nil } // VerifyFee is used to return the fee for the given transaction data in sdk.Coins. It checks that the // gas limit is not reached, the gas limit is higher than the intrinsic gas and that the // base fee is higher than the gas fee cap. func VerifyFee( txData types.TxData, denom string, baseFee *big.Int, homestead, istanbul, isCheckTx bool, ) (sdk.Coins, error) { isContractCreation := txData.GetTo() == nil gasLimit := txData.GetGas() var accessList ethtypes.AccessList if txData.GetAccessList() != nil { accessList = txData.GetAccessList() } intrinsicGas, err := core.IntrinsicGas(txData.GetData(), accessList, isContractCreation, homestead, istanbul) if err != nil { return nil, errorsmod.Wrapf( err, "failed to retrieve intrinsic gas, contract creation = %t; homestead = %t, istanbul = %t", isContractCreation, homestead, istanbul, ) } // intrinsic gas verification during CheckTx if isCheckTx && gasLimit < intrinsicGas { return nil, errorsmod.Wrapf( errortypes.ErrOutOfGas, "gas limit too low: %d (gas limit) < %d (intrinsic gas)", gasLimit, intrinsicGas, ) } if baseFee != nil && txData.GetGasFeeCap().Cmp(baseFee) < 0 { return nil, errorsmod.Wrapf(errortypes.ErrInsufficientFee, "the tx gasfeecap is lower than the tx baseFee: %s (gasfeecap), %s (basefee) ", txData.GetGasFeeCap(), baseFee) } feeAmt := txData.EffectiveFee(baseFee) if feeAmt.Sign() == 0 { // zero fee, no need to deduct return sdk.Coins{}, nil } return sdk.Coins{{Denom: denom, Amount: sdkmath.NewIntFromBigInt(feeAmt)}}, nil } // CheckSenderBalance validates that the tx cost value is positive and that the // sender has enough funds to pay for the fees and value of the transaction. func CheckSenderBalance( balance sdkmath.Int, txData types.TxData, ) error { cost := txData.Cost() if cost.Sign() < 0 { return errorsmod.Wrapf( errortypes.ErrInvalidCoins, "tx cost (%s) is negative and invalid", cost, ) } if balance.IsNegative() || balance.BigInt().Cmp(cost) < 0 { return errorsmod.Wrapf( errortypes.ErrInsufficientFunds, "sender balance < tx cost (%s < %s)", balance, txData.Cost(), ) } return nil }