forked from cerc-io/plugeth
internal, accounts, eth: utilize vm failed flag to help gas estimation
This commit is contained in:
parent
f86c4177d5
commit
94903d572b
@ -168,7 +168,7 @@ func (b *SimulatedBackend) CallContract(ctx context.Context, call ethereum.CallM
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
rval, _, err := b.callContract(ctx, call, b.blockchain.CurrentBlock(), state)
|
rval, _, _, err := b.callContract(ctx, call, b.blockchain.CurrentBlock(), state)
|
||||||
return rval, err
|
return rval, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -178,7 +178,7 @@ func (b *SimulatedBackend) PendingCallContract(ctx context.Context, call ethereu
|
|||||||
defer b.mu.Unlock()
|
defer b.mu.Unlock()
|
||||||
defer b.pendingState.RevertToSnapshot(b.pendingState.Snapshot())
|
defer b.pendingState.RevertToSnapshot(b.pendingState.Snapshot())
|
||||||
|
|
||||||
rval, _, err := b.callContract(ctx, call, b.pendingBlock, b.pendingState)
|
rval, _, _, err := b.callContract(ctx, call, b.pendingBlock, b.pendingState)
|
||||||
return rval, err
|
return rval, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -204,8 +204,11 @@ func (b *SimulatedBackend) EstimateGas(ctx context.Context, call ethereum.CallMs
|
|||||||
defer b.mu.Unlock()
|
defer b.mu.Unlock()
|
||||||
|
|
||||||
// Binary search the gas requirement, as it may be higher than the amount used
|
// Binary search the gas requirement, as it may be higher than the amount used
|
||||||
var lo, hi uint64
|
var (
|
||||||
if call.Gas != nil {
|
lo uint64 = params.TxGas - 1
|
||||||
|
hi uint64
|
||||||
|
)
|
||||||
|
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()
|
||||||
@ -216,11 +219,11 @@ func (b *SimulatedBackend) EstimateGas(ctx context.Context, call ethereum.CallMs
|
|||||||
call.Gas = new(big.Int).SetUint64(mid)
|
call.Gas = new(big.Int).SetUint64(mid)
|
||||||
|
|
||||||
snapshot := b.pendingState.Snapshot()
|
snapshot := b.pendingState.Snapshot()
|
||||||
_, gas, 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 used all the gas (failed), raise the gas limit
|
// If the transaction became invalid or execution failed, raise the gas limit
|
||||||
if err != nil || gas.Cmp(call.Gas) == 0 {
|
if err != nil || failed {
|
||||||
lo = mid
|
lo = mid
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -232,7 +235,7 @@ func (b *SimulatedBackend) EstimateGas(ctx context.Context, call ethereum.CallMs
|
|||||||
|
|
||||||
// callContract implemens common code between normal and pending contract calls.
|
// callContract implemens common code between normal and pending contract calls.
|
||||||
// state is modified during execution, make sure to copy it if necessary.
|
// state is modified during execution, make sure to copy it if necessary.
|
||||||
func (b *SimulatedBackend) callContract(ctx context.Context, call ethereum.CallMsg, block *types.Block, statedb *state.StateDB) ([]byte, *big.Int, error) {
|
func (b *SimulatedBackend) callContract(ctx context.Context, call ethereum.CallMsg, block *types.Block, statedb *state.StateDB) ([]byte, *big.Int, bool, error) {
|
||||||
// Ensure message is initialized properly.
|
// Ensure message is initialized properly.
|
||||||
if call.GasPrice == nil {
|
if call.GasPrice == nil {
|
||||||
call.GasPrice = big.NewInt(1)
|
call.GasPrice = big.NewInt(1)
|
||||||
@ -254,9 +257,8 @@ func (b *SimulatedBackend) callContract(ctx context.Context, call ethereum.CallM
|
|||||||
// about the transaction and calling mechanisms.
|
// about the transaction and calling mechanisms.
|
||||||
vmenv := vm.NewEVM(evmContext, statedb, b.config, vm.Config{})
|
vmenv := vm.NewEVM(evmContext, statedb, b.config, vm.Config{})
|
||||||
gaspool := new(core.GasPool).AddGas(math.MaxBig256)
|
gaspool := new(core.GasPool).AddGas(math.MaxBig256)
|
||||||
// TODO utilize returned failed flag to help gas estimation.
|
ret, gasUsed, _, failed, err := core.NewStateTransition(vmenv, msg, gaspool).TransitionDb()
|
||||||
ret, gasUsed, _, _, err := core.NewStateTransition(vmenv, msg, gaspool).TransitionDb()
|
return ret, gasUsed, failed, err
|
||||||
return ret, gasUsed, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SendTransaction updates the pending block to include the given transaction.
|
// SendTransaction updates the pending block to include the given transaction.
|
||||||
|
@ -523,8 +523,7 @@ func (api *PrivateDebugAPI) TraceTransaction(ctx context.Context, txHash common.
|
|||||||
|
|
||||||
// Run the transaction with tracing enabled.
|
// Run the transaction with tracing enabled.
|
||||||
vmenv := vm.NewEVM(context, statedb, api.config, vm.Config{Debug: true, Tracer: tracer})
|
vmenv := vm.NewEVM(context, statedb, api.config, vm.Config{Debug: true, Tracer: tracer})
|
||||||
// TODO utilize failed flag
|
ret, gas, failed, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas()))
|
||||||
ret, gas, _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas()))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("tracing failed: %v", err)
|
return nil, fmt.Errorf("tracing failed: %v", err)
|
||||||
}
|
}
|
||||||
@ -532,6 +531,7 @@ func (api *PrivateDebugAPI) TraceTransaction(ctx context.Context, txHash common.
|
|||||||
case *vm.StructLogger:
|
case *vm.StructLogger:
|
||||||
return ðapi.ExecutionResult{
|
return ðapi.ExecutionResult{
|
||||||
Gas: gas,
|
Gas: gas,
|
||||||
|
Failed: failed,
|
||||||
ReturnValue: fmt.Sprintf("%x", ret),
|
ReturnValue: fmt.Sprintf("%x", ret),
|
||||||
StructLogs: ethapi.FormatLogs(tracer.StructLogs()),
|
StructLogs: ethapi.FormatLogs(tracer.StructLogs()),
|
||||||
}, nil
|
}, nil
|
||||||
|
@ -580,12 +580,12 @@ type CallArgs struct {
|
|||||||
Data hexutil.Bytes `json:"data"`
|
Data hexutil.Bytes `json:"data"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *PublicBlockChainAPI) doCall(ctx context.Context, args CallArgs, blockNr rpc.BlockNumber, vmCfg vm.Config) ([]byte, *big.Int, error) {
|
func (s *PublicBlockChainAPI) doCall(ctx context.Context, args CallArgs, blockNr rpc.BlockNumber, vmCfg vm.Config) ([]byte, *big.Int, bool, error) {
|
||||||
defer func(start time.Time) { log.Debug("Executing EVM call finished", "runtime", time.Since(start)) }(time.Now())
|
defer func(start time.Time) { log.Debug("Executing EVM call finished", "runtime", time.Since(start)) }(time.Now())
|
||||||
|
|
||||||
state, header, err := s.b.StateAndHeaderByNumber(ctx, blockNr)
|
state, header, err := s.b.StateAndHeaderByNumber(ctx, blockNr)
|
||||||
if state == nil || err != nil {
|
if state == nil || err != nil {
|
||||||
return nil, common.Big0, err
|
return nil, common.Big0, false, err
|
||||||
}
|
}
|
||||||
// Set sender address or use a default if none specified
|
// Set sender address or use a default if none specified
|
||||||
addr := args.From
|
addr := args.From
|
||||||
@ -623,7 +623,7 @@ func (s *PublicBlockChainAPI) doCall(ctx context.Context, args CallArgs, blockNr
|
|||||||
// Get a new instance of the EVM.
|
// Get a new instance of the EVM.
|
||||||
evm, vmError, err := s.b.GetEVM(ctx, msg, state, header, vmCfg)
|
evm, vmError, err := s.b.GetEVM(ctx, msg, state, header, vmCfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, common.Big0, err
|
return nil, common.Big0, false, err
|
||||||
}
|
}
|
||||||
// Wait for the context to be done and cancel the evm. Even if the
|
// Wait for the context to be done and cancel the evm. Even if the
|
||||||
// EVM has finished, cancelling may be done (repeatedly)
|
// EVM has finished, cancelling may be done (repeatedly)
|
||||||
@ -635,26 +635,28 @@ func (s *PublicBlockChainAPI) doCall(ctx context.Context, args CallArgs, blockNr
|
|||||||
// Setup the gas pool (also for unmetered requests)
|
// Setup the gas pool (also for unmetered requests)
|
||||||
// and apply the message.
|
// and apply the message.
|
||||||
gp := new(core.GasPool).AddGas(math.MaxBig256)
|
gp := new(core.GasPool).AddGas(math.MaxBig256)
|
||||||
// TODO utilize failed flag to help gas estimation
|
res, gas, failed, err := core.ApplyMessage(evm, msg, gp)
|
||||||
res, gas, _, err := core.ApplyMessage(evm, msg, gp)
|
|
||||||
if err := vmError(); err != nil {
|
if err := vmError(); err != nil {
|
||||||
return nil, common.Big0, err
|
return nil, common.Big0, false, err
|
||||||
}
|
}
|
||||||
return res, gas, err
|
return res, gas, failed, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call executes the given transaction on the state for the given block number.
|
// Call executes the given transaction on the state for the given block number.
|
||||||
// It doesn't make and changes in the state/blockchain and is useful to execute and retrieve values.
|
// It doesn't make and changes in the state/blockchain and is useful to execute and retrieve values.
|
||||||
func (s *PublicBlockChainAPI) Call(ctx context.Context, args CallArgs, blockNr rpc.BlockNumber) (hexutil.Bytes, error) {
|
func (s *PublicBlockChainAPI) Call(ctx context.Context, args CallArgs, blockNr rpc.BlockNumber) (hexutil.Bytes, error) {
|
||||||
result, _, err := s.doCall(ctx, args, blockNr, vm.Config{DisableGasMetering: true})
|
result, _, _, err := s.doCall(ctx, args, blockNr, vm.Config{DisableGasMetering: true})
|
||||||
return (hexutil.Bytes)(result), err
|
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.
|
||||||
func (s *PublicBlockChainAPI) EstimateGas(ctx context.Context, args CallArgs) (*hexutil.Big, error) {
|
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
|
// Binary search the gas requirement, as it may be higher than the amount used
|
||||||
var lo, hi uint64
|
var (
|
||||||
if (*big.Int)(&args.Gas).Sign() != 0 {
|
lo uint64 = params.TxGas - 1
|
||||||
|
hi uint64
|
||||||
|
)
|
||||||
|
if (*big.Int)(&args.Gas).Uint64() >= params.TxGas {
|
||||||
hi = (*big.Int)(&args.Gas).Uint64()
|
hi = (*big.Int)(&args.Gas).Uint64()
|
||||||
} else {
|
} else {
|
||||||
// Retrieve the current pending block to act as the gas ceiling
|
// Retrieve the current pending block to act as the gas ceiling
|
||||||
@ -669,10 +671,10 @@ func (s *PublicBlockChainAPI) EstimateGas(ctx context.Context, args CallArgs) (*
|
|||||||
mid := (hi + lo) / 2
|
mid := (hi + lo) / 2
|
||||||
(*big.Int)(&args.Gas).SetUint64(mid)
|
(*big.Int)(&args.Gas).SetUint64(mid)
|
||||||
|
|
||||||
_, gas, err := s.doCall(ctx, args, rpc.PendingBlockNumber, vm.Config{})
|
_, _, failed, err := s.doCall(ctx, args, rpc.PendingBlockNumber, vm.Config{})
|
||||||
|
|
||||||
// If the transaction became invalid or used all the gas (failed), raise the gas limit
|
// If the transaction became invalid or execution failed, raise the gas limit
|
||||||
if err != nil || gas.Cmp((*big.Int)(&args.Gas)) == 0 {
|
if err != nil || failed {
|
||||||
lo = mid
|
lo = mid
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -683,10 +685,11 @@ func (s *PublicBlockChainAPI) EstimateGas(ctx context.Context, args CallArgs) (*
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ExecutionResult groups all structured logs emitted by the EVM
|
// ExecutionResult groups all structured logs emitted by the EVM
|
||||||
// while replaying a transaction in debug mode as well as the amount of
|
// while replaying a transaction in debug mode as well as transaction
|
||||||
// gas used and the return value
|
// execution status, the amount of gas used and the return value
|
||||||
type ExecutionResult struct {
|
type ExecutionResult struct {
|
||||||
Gas *big.Int `json:"gas"`
|
Gas *big.Int `json:"gas"`
|
||||||
|
Failed bool `json:"failed"`
|
||||||
ReturnValue string `json:"returnValue"`
|
ReturnValue string `json:"returnValue"`
|
||||||
StructLogs []StructLogRes `json:"structLogs"`
|
StructLogs []StructLogRes `json:"structLogs"`
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user