aboutsummaryrefslogtreecommitdiffstats
path: root/accounts/abi/bind/backends/simulated.go
diff options
context:
space:
mode:
Diffstat (limited to 'accounts/abi/bind/backends/simulated.go')
-rw-r--r--accounts/abi/bind/backends/simulated.go39
1 files changed, 27 insertions, 12 deletions
diff --git a/accounts/abi/bind/backends/simulated.go b/accounts/abi/bind/backends/simulated.go
index 0621e81c2..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.
@@ -59,7 +60,7 @@ type SimulatedBackend struct {
// for testing purposes.
func NewSimulatedBackend(alloc core.GenesisAlloc) *SimulatedBackend {
database, _ := ethdb.NewMemDatabase()
- genesis := core.Genesis{Config: params.AllProtocolChanges, Alloc: alloc}
+ genesis := core.Genesis{Config: params.AllEthashProtocolChanges, Alloc: alloc}
genesis.MustCommit(database)
blockchain, _ := core.NewBlockChain(database, genesis.Config, ethash.NewFaker(), vm.Config{})
backend := &SimulatedBackend{database: database, blockchain: blockchain, config: genesis.Config}
@@ -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
}