From e6a9ff9d4fe54cef18936b6902791a533dd7e596 Mon Sep 17 00:00:00 2001 From: rjl493456442 Date: Tue, 14 Nov 2017 13:05:13 +0800 Subject: [PATCH] accounts, internal: return an error if no suitable estimated gas found --- accounts/abi/bind/backends/simulated.go | 41 +++++++++++++++++-------- internal/ethapi/api.go | 36 +++++++++++++++------- 2 files changed, 54 insertions(+), 23 deletions(-) diff --git a/accounts/abi/bind/backends/simulated.go b/accounts/abi/bind/backends/simulated.go index e1d5745caa..38128f3674 100644 --- a/accounts/abi/bind/backends/simulated.go +++ b/accounts/abi/bind/backends/simulated.go @@ -41,6 +41,7 @@ import ( var _ bind.ContractBackend = (*SimulatedBackend)(nil) var errBlockNumberUnsupported = errors.New("SimulatedBackend cannot access blocks other than the latest block") +var errGasEstimationFailed = errors.New("gas required exceeds block limit or always failing transaction") // SimulatedBackend implements bind.ContractBackend, simulating a blockchain in // the background. Its main purpose is to allow easily testing contract bindings. @@ -205,30 +206,46 @@ func (b *SimulatedBackend) EstimateGas(ctx context.Context, call ethereum.CallMs // Binary search the gas requirement, as it may be higher than the amount used var ( - lo uint64 = params.TxGas - 1 - hi uint64 + lo uint64 = params.TxGas - 1 + hi, gasLimit uint64 ) if call.Gas != nil && call.Gas.Uint64() >= params.TxGas { hi = call.Gas.Uint64() } else { hi = b.pendingBlock.GasLimit().Uint64() } - for lo+1 < hi { - // Take a guess at the gas, and check transaction validity - mid := (hi + lo) / 2 - call.Gas = new(big.Int).SetUint64(mid) + gasLimit = hi + // Determine whether the gas is adequate based on the result of the execution. + estimate := func(gas uint64) bool { + call.Gas = new(big.Int).SetUint64(gas) snapshot := b.pendingState.Snapshot() _, _, failed, err := b.callContract(ctx, call, b.pendingBlock, b.pendingState) b.pendingState.RevertToSnapshot(snapshot) - - // If the transaction became invalid or execution failed, raise the gas limit if err != nil || failed { - lo = mid - continue + return false + } + return true + } + + for lo+1 < hi { + // Take a guess at the gas, and check transaction validity + mid := (hi + lo) / 2 + if !estimate(mid) { + // If the transaction became invalid or execution failed, raise the gas limit + lo = mid + } else { + // Otherwise assume the transaction succeeded, lower the gas limit + hi = mid + } + } + + if hi == gasLimit { + // Regard the transaction is invalid if the required gas exceeds block limit + // or simply a bad transaction. Since this type transaction will always fail. + if !estimate(hi) { + return nil, errGasEstimationFailed } - // Otherwise assume the transaction succeeded, lower the gas limit - hi = mid } return new(big.Int).SetUint64(hi), nil } diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 1ffb5a1803..aae4700b42 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -653,8 +653,8 @@ func (s *PublicBlockChainAPI) Call(ctx context.Context, args CallArgs, blockNr r 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 var ( - lo uint64 = params.TxGas - 1 - hi uint64 + lo uint64 = params.TxGas - 1 + hi, gasLimit uint64 ) if (*big.Int)(&args.Gas).Uint64() >= params.TxGas { hi = (*big.Int)(&args.Gas).Uint64() @@ -666,20 +666,34 @@ func (s *PublicBlockChainAPI) EstimateGas(ctx context.Context, args CallArgs) (* } hi = block.GasLimit().Uint64() } + gasLimit = hi + + estimate := func(gas uint64) bool { + (*big.Int)(&args.Gas).SetUint64(gas) + _, _, failed, err := s.doCall(ctx, args, rpc.PendingBlockNumber, vm.Config{}) + if err != nil || failed { + return false + } + return true + } + for lo+1 < hi { // Take a guess at the gas, and check transaction validity mid := (hi + lo) / 2 - (*big.Int)(&args.Gas).SetUint64(mid) - - _, _, 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 { + if !estimate(mid) { + // If the transaction became invalid or execution failed, raise the gas limit lo = mid - continue + } else { + // Otherwise assume the transaction succeeded, lower the gas limit + hi = mid + } + } + if hi == gasLimit { + // Regard the transaction is invalid if the required gas exceeds block limit + // or simply a bad transaction. Since this type transaction will always fail. + if !estimate(hi) { + return nil, fmt.Errorf("gas required exceeds block limit or always failing transaction") } - // Otherwise assume the transaction succeeded, lower the gas limit - hi = mid } return (*hexutil.Big)(new(big.Int).SetUint64(hi)), nil }