aboutsummaryrefslogtreecommitdiffstats
path: root/internal/ethapi
diff options
context:
space:
mode:
Diffstat (limited to 'internal/ethapi')
-rw-r--r--internal/ethapi/api.go39
1 files changed, 26 insertions, 13 deletions
diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go
index 1ffb5a180..59a29d722 100644
--- a/internal/ethapi/api.go
+++ b/internal/ethapi/api.go
@@ -649,12 +649,14 @@ func (s *PublicBlockChainAPI) Call(ctx context.Context, args CallArgs, blockNr r
return (hexutil.Bytes)(result), err
}
-// EstimateGas returns an estimate of the amount of gas needed to execute the given transaction.
+// EstimateGas returns an estimate of the amount of gas needed to execute the
+// given transaction against the current pending block.
func (s *PublicBlockChainAPI) EstimateGas(ctx context.Context, args CallArgs) (*hexutil.Big, error) {
- // Binary search the gas requirement, as it may be higher than the amount used
+ // Determine the lowest and highest possible gas limits to binary search in between
var (
- lo uint64 = params.TxGas - 1
- hi uint64
+ lo uint64 = params.TxGas - 1
+ hi uint64
+ cap uint64
)
if (*big.Int)(&args.Gas).Uint64() >= params.TxGas {
hi = (*big.Int)(&args.Gas).Uint64()
@@ -666,20 +668,31 @@ func (s *PublicBlockChainAPI) EstimateGas(ctx context.Context, args CallArgs) (*
}
hi = block.GasLimit().Uint64()
}
- for lo+1 < hi {
- // Take a guess at the gas, and check transaction validity
- mid := (hi + lo) / 2
- (*big.Int)(&args.Gas).SetUint64(mid)
+ cap = hi
+ // Create a helper to check if a gas allowance results in an executable transaction
+ executable := func(gas uint64) bool {
+ (*big.Int)(&args.Gas).SetUint64(gas)
_, _, failed, err := s.doCall(ctx, args, rpc.PendingBlockNumber, vm.Config{})
-
- // If the transaction became invalid or execution failed, raise the gas limit
if err != nil || failed {
+ return false
+ }
+ return true
+ }
+ // Execute the binary search and hone in on an executable gas limit
+ for lo+1 < hi {
+ mid := (hi + lo) / 2
+ if !executable(mid) {
lo = mid
- continue
+ } else {
+ hi = mid
+ }
+ }
+ // Reject the transaction as invalid if it still fails at the highest allowance
+ if hi == cap {
+ if !executable(hi) {
+ return nil, fmt.Errorf("gas required exceeds allowance or always failing transaction")
}
- // Otherwise assume the transaction succeeded, lower the gas limit
- hi = mid
}
return (*hexutil.Big)(new(big.Int).SetUint64(hi)), nil
}