accounts, internal: return an error if no suitable estimated gas found

This commit is contained in:
rjl493456442 2017-11-14 13:05:13 +08:00
parent b81a9cd829
commit e6a9ff9d4f
2 changed files with 54 additions and 23 deletions

View file

@ -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.
@ -206,30 +207,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
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
}
}
return new(big.Int).SetUint64(hi), nil
}

View file

@ -654,7 +654,7 @@ func (s *PublicBlockChainAPI) EstimateGas(ctx context.Context, args CallArgs) (*
// Binary search the gas requirement, as it may be higher than the amount used
var (
lo uint64 = params.TxGas - 1
hi uint64
hi, gasLimit uint64
)
if (*big.Int)(&args.Gas).Uint64() >= params.TxGas {
hi = (*big.Int)(&args.Gas).Uint64()
@ -666,21 +666,35 @@ 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 !estimate(mid) {
// If the transaction became invalid or execution failed, raise the gas limit
if err != nil || failed {
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")
}
}
return (*hexutil.Big)(new(big.Int).SetUint64(hi)), nil
}