From 984c25ac406e86255b289a3d7fec4cd91a17707f Mon Sep 17 00:00:00 2001 From: gary rong Date: Tue, 14 Nov 2017 10:26:31 -0600 Subject: accounts, internal: fail if no suitable estimated gas found (#15477) * accounts, internal: return an error if no suitable estimated gas found * accounts, internal: minor polishes on the gas estimator --- accounts/abi/bind/backends/simulated.go | 37 +++++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 11 deletions(-) (limited to 'accounts/abi/bind') diff --git a/accounts/abi/bind/backends/simulated.go b/accounts/abi/bind/backends/simulated.go index e1d5745ca..09288d401 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 allowance or always failing transaction") // SimulatedBackend implements bind.ContractBackend, simulating a blockchain in // the background. Its main purpose is to allow easily testing contract bindings. @@ -203,32 +204,46 @@ func (b *SimulatedBackend) EstimateGas(ctx context.Context, call ethereum.CallMs b.mu.Lock() defer b.mu.Unlock() - // 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 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) + cap = hi + + // Create a helper to check if a gas allowance results in an executable transaction + executable := 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 { + 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, errGasEstimationFailed } - // Otherwise assume the transaction succeeded, lower the gas limit - hi = mid } return new(big.Int).SetUint64(hi), nil } -- cgit v1.2.3