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
This commit is contained in:
parent
a3128f9099
commit
984c25ac40
@ -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 {
|
||||
lo = mid
|
||||
continue
|
||||
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
|
||||
} 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
|
||||
}
|
||||
|
@ -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 {
|
||||
lo = mid
|
||||
continue
|
||||
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
|
||||
} 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
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user