mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-17 01:13:45 +00:00
core, eth, internal: propagate VM error
This commit is contained in:
parent
bb57f1f1e5
commit
fb19f4c04f
5 changed files with 24 additions and 27 deletions
|
|
@ -272,8 +272,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)
|
||||||
ret, gasUsed, _, failed, err := core.NewStateTransition(vmenv, msg, gaspool).TransitionDb()
|
ret, gasUsed, _, vmerr, err := core.NewStateTransition(vmenv, msg, gaspool).TransitionDb()
|
||||||
return ret, gasUsed, failed, err
|
return ret, gasUsed, vmerr != nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// SendTransaction updates the pending block to include the given transaction.
|
// SendTransaction updates the pending block to include the given transaction.
|
||||||
|
|
|
||||||
|
|
@ -98,7 +98,7 @@ func ApplyTransaction(config *params.ChainConfig, bc *BlockChain, author *common
|
||||||
// about the transaction and calling mechanisms.
|
// about the transaction and calling mechanisms.
|
||||||
vmenv := vm.NewEVM(context, statedb, config, cfg)
|
vmenv := vm.NewEVM(context, statedb, config, cfg)
|
||||||
// Apply the transaction to the current state (included in the env)
|
// Apply the transaction to the current state (included in the env)
|
||||||
_, gas, failed, err := ApplyMessage(vmenv, msg, gp)
|
_, gas, vmerr, err := ApplyMessage(vmenv, msg, gp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -114,7 +114,7 @@ func ApplyTransaction(config *params.ChainConfig, bc *BlockChain, author *common
|
||||||
|
|
||||||
// Create a new receipt for the transaction, storing the intermediate root and gas used by the tx
|
// Create a new receipt for the transaction, storing the intermediate root and gas used by the tx
|
||||||
// based on the eip phase, we're passing wether the root touch-delete accounts.
|
// based on the eip phase, we're passing wether the root touch-delete accounts.
|
||||||
receipt := types.NewReceipt(root, failed, usedGas)
|
receipt := types.NewReceipt(root, vmerr != nil, usedGas)
|
||||||
receipt.TxHash = tx.Hash()
|
receipt.TxHash = tx.Hash()
|
||||||
receipt.GasUsed = new(big.Int).Set(gas)
|
receipt.GasUsed = new(big.Int).Set(gas)
|
||||||
// if the transaction created a contract, store the creation address in the receipt.
|
// if the transaction created a contract, store the creation address in the receipt.
|
||||||
|
|
|
||||||
|
|
@ -124,12 +124,13 @@ func NewStateTransition(evm *vm.EVM, msg Message, gp *GasPool) *StateTransition
|
||||||
// ApplyMessage returns the bytes returned by any EVM execution (if it took place),
|
// ApplyMessage returns the bytes returned by any EVM execution (if it took place),
|
||||||
// the gas used (which includes gas refunds) and an error if it failed. An error always
|
// the gas used (which includes gas refunds) and an error if it failed. An error always
|
||||||
// indicates a core error meaning that the message would always fail for that particular
|
// indicates a core error meaning that the message would always fail for that particular
|
||||||
// state and would never be accepted within a block.
|
// state and would never be accepted within a block. A VM error indicates an error whilst
|
||||||
func ApplyMessage(evm *vm.EVM, msg Message, gp *GasPool) ([]byte, *big.Int, bool, error) {
|
// executing the code.
|
||||||
|
func ApplyMessage(evm *vm.EVM, msg Message, gp *GasPool) ([]byte, *big.Int, error, error) {
|
||||||
st := NewStateTransition(evm, msg, gp)
|
st := NewStateTransition(evm, msg, gp)
|
||||||
|
|
||||||
ret, _, gasUsed, failed, err := st.TransitionDb()
|
ret, _, gasUsed, vmerr, err := st.TransitionDb()
|
||||||
return ret, gasUsed, failed, err
|
return ret, gasUsed, vmerr, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (st *StateTransition) from() vm.AccountRef {
|
func (st *StateTransition) from() vm.AccountRef {
|
||||||
|
|
@ -209,7 +210,7 @@ func (st *StateTransition) preCheck() error {
|
||||||
// TransitionDb will transition the state by applying the current message and returning the result
|
// TransitionDb will transition the state by applying the current message and returning the result
|
||||||
// including the required gas for the operation as well as the used gas. It returns an error if it
|
// including the required gas for the operation as well as the used gas. It returns an error if it
|
||||||
// failed. An error indicates a consensus issue.
|
// failed. An error indicates a consensus issue.
|
||||||
func (st *StateTransition) TransitionDb() (ret []byte, requiredGas, usedGas *big.Int, failed bool, err error) {
|
func (st *StateTransition) TransitionDb() (ret []byte, requiredGas, usedGas *big.Int, vmerr error, err error) {
|
||||||
if err = st.preCheck(); err != nil {
|
if err = st.preCheck(); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -223,18 +224,14 @@ func (st *StateTransition) TransitionDb() (ret []byte, requiredGas, usedGas *big
|
||||||
// TODO convert to uint64
|
// TODO convert to uint64
|
||||||
intrinsicGas := IntrinsicGas(st.data, contractCreation, homestead)
|
intrinsicGas := IntrinsicGas(st.data, contractCreation, homestead)
|
||||||
if intrinsicGas.BitLen() > 64 {
|
if intrinsicGas.BitLen() > 64 {
|
||||||
return nil, nil, nil, false, vm.ErrOutOfGas
|
return nil, nil, nil, nil, vm.ErrOutOfGas
|
||||||
}
|
}
|
||||||
if err = st.useGas(intrinsicGas.Uint64()); err != nil {
|
if err = st.useGas(intrinsicGas.Uint64()); err != nil {
|
||||||
return nil, nil, nil, false, err
|
return nil, nil, nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
evm = st.evm
|
evm = st.evm
|
||||||
// vm errors do not effect consensus and are therefor
|
|
||||||
// not assigned to err, except for insufficient balance
|
|
||||||
// error.
|
|
||||||
vmerr error
|
|
||||||
)
|
)
|
||||||
if contractCreation {
|
if contractCreation {
|
||||||
ret, _, st.gas, vmerr = evm.Create(sender, st.data, st.gas, st.value)
|
ret, _, st.gas, vmerr = evm.Create(sender, st.data, st.gas, st.value)
|
||||||
|
|
@ -249,7 +246,7 @@ func (st *StateTransition) TransitionDb() (ret []byte, requiredGas, usedGas *big
|
||||||
// sufficient balance to make the transfer happen. The first
|
// sufficient balance to make the transfer happen. The first
|
||||||
// balance transfer may never fail.
|
// balance transfer may never fail.
|
||||||
if vmerr == vm.ErrInsufficientBalance {
|
if vmerr == vm.ErrInsufficientBalance {
|
||||||
return nil, nil, nil, false, vmerr
|
return nil, nil, nil, vmerr, vmerr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
requiredGas = new(big.Int).Set(st.gasUsed())
|
requiredGas = new(big.Int).Set(st.gasUsed())
|
||||||
|
|
@ -257,7 +254,7 @@ func (st *StateTransition) TransitionDb() (ret []byte, requiredGas, usedGas *big
|
||||||
st.refundGas()
|
st.refundGas()
|
||||||
st.state.AddBalance(st.evm.Coinbase, new(big.Int).Mul(st.gasUsed(), st.gasPrice))
|
st.state.AddBalance(st.evm.Coinbase, new(big.Int).Mul(st.gasUsed(), st.gasPrice))
|
||||||
|
|
||||||
return ret, requiredGas, st.gasUsed(), vmerr != nil, err
|
return ret, requiredGas, st.gasUsed(), vmerr, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (st *StateTransition) refundGas() {
|
func (st *StateTransition) refundGas() {
|
||||||
|
|
|
||||||
|
|
@ -523,7 +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})
|
||||||
ret, gas, failed, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas()))
|
ret, gas, vmerr, 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)
|
||||||
}
|
}
|
||||||
|
|
@ -531,7 +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,
|
Failed: vmerr != nil,
|
||||||
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, bool, error) {
|
func (s *PublicBlockChainAPI) doCall(ctx context.Context, args CallArgs, blockNr rpc.BlockNumber, vmCfg vm.Config) ([]byte, *big.Int, error, 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, false, err
|
return nil, common.Big0, nil, 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, false, err
|
return nil, common.Big0, nil, 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,11 +635,11 @@ 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)
|
||||||
res, gas, failed, err := core.ApplyMessage(evm, msg, gp)
|
res, gas, vmerr, err := core.ApplyMessage(evm, msg, gp)
|
||||||
if err := vmError(); err != nil {
|
if err := vmError(); err != nil {
|
||||||
return nil, common.Big0, false, err
|
return nil, common.Big0, nil, err
|
||||||
}
|
}
|
||||||
return res, gas, failed, err
|
return res, gas, vmerr, 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.
|
||||||
|
|
@ -673,8 +673,8 @@ func (s *PublicBlockChainAPI) EstimateGas(ctx context.Context, args CallArgs) (*
|
||||||
// Create a helper to check if a gas allowance results in an executable transaction
|
// Create a helper to check if a gas allowance results in an executable transaction
|
||||||
executable := func(gas uint64) bool {
|
executable := func(gas uint64) bool {
|
||||||
(*big.Int)(&args.Gas).SetUint64(gas)
|
(*big.Int)(&args.Gas).SetUint64(gas)
|
||||||
_, _, failed, err := s.doCall(ctx, args, rpc.PendingBlockNumber, vm.Config{})
|
_, _, vmerr, err := s.doCall(ctx, args, rpc.PendingBlockNumber, vm.Config{})
|
||||||
if err != nil || failed {
|
if err != nil || vmerr != nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue