|
|
@ -41,6 +41,7 @@ import ( |
|
|
|
var _ bind.ContractBackend = (*SimulatedBackend)(nil) |
|
|
|
var _ bind.ContractBackend = (*SimulatedBackend)(nil) |
|
|
|
|
|
|
|
|
|
|
|
var errBlockNumberUnsupported = errors.New("SimulatedBackend cannot access blocks other than the latest block") |
|
|
|
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
|
|
|
|
// SimulatedBackend implements bind.ContractBackend, simulating a blockchain in
|
|
|
|
// the background. Its main purpose is to allow easily testing contract bindings.
|
|
|
|
// 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() |
|
|
|
b.mu.Lock() |
|
|
|
defer b.mu.Unlock() |
|
|
|
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 ( |
|
|
|
var ( |
|
|
|
lo uint64 = params.TxGas - 1 |
|
|
|
lo uint64 = params.TxGas - 1 |
|
|
|
hi uint64 |
|
|
|
hi uint64 |
|
|
|
|
|
|
|
cap uint64 |
|
|
|
) |
|
|
|
) |
|
|
|
if call.Gas != nil && call.Gas.Uint64() >= params.TxGas { |
|
|
|
if call.Gas != nil && call.Gas.Uint64() >= params.TxGas { |
|
|
|
hi = call.Gas.Uint64() |
|
|
|
hi = call.Gas.Uint64() |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
hi = b.pendingBlock.GasLimit().Uint64() |
|
|
|
hi = b.pendingBlock.GasLimit().Uint64() |
|
|
|
} |
|
|
|
} |
|
|
|
for lo+1 < hi { |
|
|
|
cap = hi |
|
|
|
// Take a guess at the gas, and check transaction validity
|
|
|
|
|
|
|
|
mid := (hi + lo) / 2 |
|
|
|
// Create a helper to check if a gas allowance results in an executable transaction
|
|
|
|
call.Gas = new(big.Int).SetUint64(mid) |
|
|
|
executable := func(gas uint64) bool { |
|
|
|
|
|
|
|
call.Gas = new(big.Int).SetUint64(gas) |
|
|
|
|
|
|
|
|
|
|
|
snapshot := b.pendingState.Snapshot() |
|
|
|
snapshot := b.pendingState.Snapshot() |
|
|
|
_, _, failed, err := b.callContract(ctx, call, b.pendingBlock, b.pendingState) |
|
|
|
_, _, failed, err := b.callContract(ctx, call, b.pendingBlock, b.pendingState) |
|
|
|
b.pendingState.RevertToSnapshot(snapshot) |
|
|
|
b.pendingState.RevertToSnapshot(snapshot) |
|
|
|
|
|
|
|
|
|
|
|
// If the transaction became invalid or execution failed, raise the gas limit
|
|
|
|
|
|
|
|
if err != nil || failed { |
|
|
|
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 |
|
|
|
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 |
|
|
|
return new(big.Int).SetUint64(hi), nil |
|
|
|
} |
|
|
|
} |
|
|
|