154 lines
5.0 KiB
Go
154 lines
5.0 KiB
Go
// 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
|
|
}
|