Skip to content

Commit 94903d5

Browse files
rjl493456442karalabe
authored andcommitted
internal, accounts, eth: utilize vm failed flag to help gas estimation
1 parent f86c417 commit 94903d5

File tree

3 files changed

+33
-28
lines changed

3 files changed

+33
-28
lines changed

accounts/abi/bind/backends/simulated.go

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ func (b *SimulatedBackend) CallContract(ctx context.Context, call ethereum.CallM
168168
if err != nil {
169169
return nil, err
170170
}
171-
rval, _, err := b.callContract(ctx, call, b.blockchain.CurrentBlock(), state)
171+
rval, _, _, err := b.callContract(ctx, call, b.blockchain.CurrentBlock(), state)
172172
return rval, err
173173
}
174174

@@ -178,7 +178,7 @@ func (b *SimulatedBackend) PendingCallContract(ctx context.Context, call ethereu
178178
defer b.mu.Unlock()
179179
defer b.pendingState.RevertToSnapshot(b.pendingState.Snapshot())
180180

181-
rval, _, err := b.callContract(ctx, call, b.pendingBlock, b.pendingState)
181+
rval, _, _, err := b.callContract(ctx, call, b.pendingBlock, b.pendingState)
182182
return rval, err
183183
}
184184

@@ -204,8 +204,11 @@ func (b *SimulatedBackend) EstimateGas(ctx context.Context, call ethereum.CallMs
204204
defer b.mu.Unlock()
205205

206206
// Binary search the gas requirement, as it may be higher than the amount used
207-
var lo, hi uint64
208-
if call.Gas != nil {
207+
var (
208+
lo uint64 = params.TxGas - 1
209+
hi uint64
210+
)
211+
if call.Gas != nil && call.Gas.Uint64() >= params.TxGas {
209212
hi = call.Gas.Uint64()
210213
} else {
211214
hi = b.pendingBlock.GasLimit().Uint64()
@@ -216,11 +219,11 @@ func (b *SimulatedBackend) EstimateGas(ctx context.Context, call ethereum.CallMs
216219
call.Gas = new(big.Int).SetUint64(mid)
217220

218221
snapshot := b.pendingState.Snapshot()
219-
_, gas, err := b.callContract(ctx, call, b.pendingBlock, b.pendingState)
222+
_, _, failed, err := b.callContract(ctx, call, b.pendingBlock, b.pendingState)
220223
b.pendingState.RevertToSnapshot(snapshot)
221224

222-
// If the transaction became invalid or used all the gas (failed), raise the gas limit
223-
if err != nil || gas.Cmp(call.Gas) == 0 {
225+
// If the transaction became invalid or execution failed, raise the gas limit
226+
if err != nil || failed {
224227
lo = mid
225228
continue
226229
}
@@ -232,7 +235,7 @@ func (b *SimulatedBackend) EstimateGas(ctx context.Context, call ethereum.CallMs
232235

233236
// callContract implemens common code between normal and pending contract calls.
234237
// state is modified during execution, make sure to copy it if necessary.
235-
func (b *SimulatedBackend) callContract(ctx context.Context, call ethereum.CallMsg, block *types.Block, statedb *state.StateDB) ([]byte, *big.Int, error) {
238+
func (b *SimulatedBackend) callContract(ctx context.Context, call ethereum.CallMsg, block *types.Block, statedb *state.StateDB) ([]byte, *big.Int, bool, error) {
236239
// Ensure message is initialized properly.
237240
if call.GasPrice == nil {
238241
call.GasPrice = big.NewInt(1)
@@ -254,9 +257,8 @@ func (b *SimulatedBackend) callContract(ctx context.Context, call ethereum.CallM
254257
// about the transaction and calling mechanisms.
255258
vmenv := vm.NewEVM(evmContext, statedb, b.config, vm.Config{})
256259
gaspool := new(core.GasPool).AddGas(math.MaxBig256)
257-
// TODO utilize returned failed flag to help gas estimation.
258-
ret, gasUsed, _, _, err := core.NewStateTransition(vmenv, msg, gaspool).TransitionDb()
259-
return ret, gasUsed, err
260+
ret, gasUsed, _, failed, err := core.NewStateTransition(vmenv, msg, gaspool).TransitionDb()
261+
return ret, gasUsed, failed, err
260262
}
261263

262264
// SendTransaction updates the pending block to include the given transaction.

eth/api.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -523,15 +523,15 @@ func (api *PrivateDebugAPI) TraceTransaction(ctx context.Context, txHash common.
523523

524524
// Run the transaction with tracing enabled.
525525
vmenv := vm.NewEVM(context, statedb, api.config, vm.Config{Debug: true, Tracer: tracer})
526-
// TODO utilize failed flag
527-
ret, gas, _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas()))
526+
ret, gas, failed, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas()))
528527
if err != nil {
529528
return nil, fmt.Errorf("tracing failed: %v", err)
530529
}
531530
switch tracer := tracer.(type) {
532531
case *vm.StructLogger:
533532
return &ethapi.ExecutionResult{
534533
Gas: gas,
534+
Failed: failed,
535535
ReturnValue: fmt.Sprintf("%x", ret),
536536
StructLogs: ethapi.FormatLogs(tracer.StructLogs()),
537537
}, nil

internal/ethapi/api.go

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -580,12 +580,12 @@ type CallArgs struct {
580580
Data hexutil.Bytes `json:"data"`
581581
}
582582

583-
func (s *PublicBlockChainAPI) doCall(ctx context.Context, args CallArgs, blockNr rpc.BlockNumber, vmCfg vm.Config) ([]byte, *big.Int, error) {
583+
func (s *PublicBlockChainAPI) doCall(ctx context.Context, args CallArgs, blockNr rpc.BlockNumber, vmCfg vm.Config) ([]byte, *big.Int, bool, error) {
584584
defer func(start time.Time) { log.Debug("Executing EVM call finished", "runtime", time.Since(start)) }(time.Now())
585585

586586
state, header, err := s.b.StateAndHeaderByNumber(ctx, blockNr)
587587
if state == nil || err != nil {
588-
return nil, common.Big0, err
588+
return nil, common.Big0, false, err
589589
}
590590
// Set sender address or use a default if none specified
591591
addr := args.From
@@ -623,7 +623,7 @@ func (s *PublicBlockChainAPI) doCall(ctx context.Context, args CallArgs, blockNr
623623
// Get a new instance of the EVM.
624624
evm, vmError, err := s.b.GetEVM(ctx, msg, state, header, vmCfg)
625625
if err != nil {
626-
return nil, common.Big0, err
626+
return nil, common.Big0, false, err
627627
}
628628
// Wait for the context to be done and cancel the evm. Even if the
629629
// EVM has finished, cancelling may be done (repeatedly)
@@ -635,26 +635,28 @@ func (s *PublicBlockChainAPI) doCall(ctx context.Context, args CallArgs, blockNr
635635
// Setup the gas pool (also for unmetered requests)
636636
// and apply the message.
637637
gp := new(core.GasPool).AddGas(math.MaxBig256)
638-
// TODO utilize failed flag to help gas estimation
639-
res, gas, _, err := core.ApplyMessage(evm, msg, gp)
638+
res, gas, failed, err := core.ApplyMessage(evm, msg, gp)
640639
if err := vmError(); err != nil {
641-
return nil, common.Big0, err
640+
return nil, common.Big0, false, err
642641
}
643-
return res, gas, err
642+
return res, gas, failed, err
644643
}
645644

646645
// Call executes the given transaction on the state for the given block number.
647646
// It doesn't make and changes in the state/blockchain and is useful to execute and retrieve values.
648647
func (s *PublicBlockChainAPI) Call(ctx context.Context, args CallArgs, blockNr rpc.BlockNumber) (hexutil.Bytes, error) {
649-
result, _, err := s.doCall(ctx, args, blockNr, vm.Config{DisableGasMetering: true})
648+
result, _, _, err := s.doCall(ctx, args, blockNr, vm.Config{DisableGasMetering: true})
650649
return (hexutil.Bytes)(result), err
651650
}
652651

653652
// EstimateGas returns an estimate of the amount of gas needed to execute the given transaction.
654653
func (s *PublicBlockChainAPI) EstimateGas(ctx context.Context, args CallArgs) (*hexutil.Big, error) {
655654
// Binary search the gas requirement, as it may be higher than the amount used
656-
var lo, hi uint64
657-
if (*big.Int)(&args.Gas).Sign() != 0 {
655+
var (
656+
lo uint64 = params.TxGas - 1
657+
hi uint64
658+
)
659+
if (*big.Int)(&args.Gas).Uint64() >= params.TxGas {
658660
hi = (*big.Int)(&args.Gas).Uint64()
659661
} else {
660662
// Retrieve the current pending block to act as the gas ceiling
@@ -669,10 +671,10 @@ func (s *PublicBlockChainAPI) EstimateGas(ctx context.Context, args CallArgs) (*
669671
mid := (hi + lo) / 2
670672
(*big.Int)(&args.Gas).SetUint64(mid)
671673

672-
_, gas, err := s.doCall(ctx, args, rpc.PendingBlockNumber, vm.Config{})
674+
_, _, failed, err := s.doCall(ctx, args, rpc.PendingBlockNumber, vm.Config{})
673675

674-
// If the transaction became invalid or used all the gas (failed), raise the gas limit
675-
if err != nil || gas.Cmp((*big.Int)(&args.Gas)) == 0 {
676+
// If the transaction became invalid or execution failed, raise the gas limit
677+
if err != nil || failed {
676678
lo = mid
677679
continue
678680
}
@@ -683,10 +685,11 @@ func (s *PublicBlockChainAPI) EstimateGas(ctx context.Context, args CallArgs) (*
683685
}
684686

685687
// ExecutionResult groups all structured logs emitted by the EVM
686-
// while replaying a transaction in debug mode as well as the amount of
687-
// gas used and the return value
688+
// while replaying a transaction in debug mode as well as transaction
689+
// execution status, the amount of gas used and the return value
688690
type ExecutionResult struct {
689691
Gas *big.Int `json:"gas"`
692+
Failed bool `json:"failed"`
690693
ReturnValue string `json:"returnValue"`
691694
StructLogs []StructLogRes `json:"structLogs"`
692695
}

0 commit comments

Comments
 (0)