feat: gasUsed use result.UsedGas (#45)

This commit is contained in:
Zhang Guyu 2022-10-11 11:49:11 +08:00 committed by GitHub
parent 69c7e7b917
commit aedf42a155
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -2,7 +2,6 @@ package eth
import (
"context"
"errors"
"fmt"
"hash"
"math"
@ -19,8 +18,6 @@ import (
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/internal/ethapi"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rpc"
// "github.com/DeBankDeFi/eth/txtrace"
@ -249,7 +246,6 @@ func (api *PreExecAPI) TraceMany(ctx context.Context, origins []PreArgs) ([]PreR
return nil, err
}
for i := 0; i < len(origins); i++ {
stateOld := state.Copy()
origin := origins[i]
if origin.Nonce == nil {
preResList = append(preResList, PreResult{
@ -347,150 +343,7 @@ func (api *PreExecAPI) TraceMany(ctx context.Context, origins []PreArgs) ([]PreR
Msg: (preRes.Trace)[0].Error,
}
}
if preRes.Error.Code == 0 {
gasLimit := header.GasLimit
gasCap := api.e.APIBackend.RPCGasCap()
if gasLimit > gasCap {
gasLimit = gasCap
}
usedGas := api.doEstimateGas(ctx, txArgs, header, stateOld, gasLimit)
log.Debug("doEstimateGas", "usedGas", usedGas)
if usedGas > 0 {
preRes.GasUsed = usedGas
}
}
preResList = append(preResList, preRes)
}
return preResList, nil
}
func (api *PreExecAPI) doEstimateGas(ctx context.Context, args ethapi.TransactionArgs, header *types.Header, state *state.StateDB, gasLimit uint64) uint64 {
// Binary search the gas requirement, as it may be higher than the amount used
var (
lo uint64 = params.TxGas - 1
hi uint64
)
// Use zero address if sender unspecified.
if args.From == nil {
args.From = new(common.Address)
}
// Determine the highest gas limit can be used during the estimation.
if args.Gas != nil && uint64(*args.Gas) >= params.TxGas {
hi = uint64(*args.Gas)
} else {
hi = gasLimit
}
// Normalize the max fee per gas the call is willing to spend.
var feeCap *big.Int
if args.GasPrice != nil && (args.MaxFeePerGas != nil || args.MaxPriorityFeePerGas != nil) {
return 0
} else if args.GasPrice != nil {
feeCap = args.GasPrice.ToInt()
} else if args.MaxFeePerGas != nil {
feeCap = args.MaxFeePerGas.ToInt()
} else {
feeCap = common.Big0
}
// Recap the highest gas limit with account's available balance.
if feeCap.BitLen() != 0 {
balance := state.GetBalance(*args.From) // from can't be nil
available := new(big.Int).Set(balance)
if args.Value != nil {
if args.Value.ToInt().Cmp(available) >= 0 {
return 0
}
available.Sub(available, args.Value.ToInt())
}
allowance := new(big.Int).Div(available, feeCap)
// If the allowance is larger than maximum uint64, skip checking
if allowance.IsUint64() && hi > allowance.Uint64() {
transfer := args.Value
if transfer == nil {
transfer = new(hexutil.Big)
}
log.Warn("Gas estimation capped by limited funds", "original", hi, "balance", balance,
"sent", transfer.ToInt(), "maxFeePerGas", feeCap, "fundable", allowance)
hi = allowance.Uint64()
}
}
// Recap the highest gas allowance with specified gascap.
if gasLimit != 0 && hi > gasLimit {
hi = gasLimit
}
cap := hi
// Create a helper to check if a gas allowance results in an executable transaction
executable := func(gas uint64) (bool, *core.ExecutionResult, error) {
args.Gas = (*hexutil.Uint64)(&gas)
result, err := api.doCallCopy(ctx, args, header, state)
log.Debug("doCallCopy", "result", result, "err", err)
if err != nil {
if errors.Is(err, core.ErrIntrinsicGas) {
return true, nil, nil // Special case, raise gas limit
}
return true, nil, err // Bail out
}
return result.Failed(), result, nil
}
// Execute the binary search and hone in on an executable gas limit
for lo+1 < hi {
mid := (hi + lo) / 2
failed, _, err := executable(mid)
// If the error is not nil(consensus error), it means the provided message
// call or transaction will never be accepted no matter how much gas it is
// assigned. Return the error directly, don't struggle any more.
if err != nil {
return 0
}
if failed {
lo = mid
} else {
hi = mid
}
}
// Reject the transaction as invalid if it still fails at the highest allowance
if hi == cap {
failed, result, err := executable(hi)
if err != nil {
return 0
}
if failed {
if result != nil && result.Err != vm.ErrOutOfGas {
if len(result.Revert()) > 0 {
return 0
}
return 0
}
// Otherwise, the specified gas cap is too low
return 0
}
}
return hi
}
func (api *PreExecAPI) doCallCopy(ctx context.Context, args ethapi.TransactionArgs, header *types.Header, state *state.StateDB) (*core.ExecutionResult, error) {
stateNew := state.Copy()
// Get a new instance of the EVM.
msg, err := args.ToMessage(api.e.APIBackend.RPCGasCap(), header.BaseFee)
if err != nil {
return nil, err
}
evm, vmError, err := api.e.APIBackend.GetEVM(ctx, msg, stateNew, header, &vm.Config{NoBaseFee: true})
if err != nil {
return nil, err
}
// Execute the message.
gp := new(core.GasPool).AddGas(math.MaxUint64)
result, err := core.ApplyMessage(evm, msg, gp)
if err := vmError(); err != nil {
return nil, err
}
if err != nil {
return result, fmt.Errorf("err: %w (supplied gas %d)", err, msg.Gas())
}
return result, nil
}