github.com/ethereumproject/go-ethereum@v5.5.2+incompatible/core/execution.go (about)

     1  // Copyright 2014 The go-ethereum Authors
     2  // This file is part of the go-ethereum library.
     3  //
     4  // The go-ethereum library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The go-ethereum library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package core
    18  
    19  import (
    20  	"fmt"
    21  	"math/big"
    22  
    23  	"github.com/ethereumproject/go-ethereum/common"
    24  	"github.com/ethereumproject/go-ethereum/core/vm"
    25  	"github.com/ethereumproject/go-ethereum/crypto"
    26  )
    27  
    28  var (
    29  	callCreateDepthMax = 1024 // limit call/create stack
    30  	errCallCreateDepth = fmt.Errorf("Max call depth exceeded (%d)", callCreateDepthMax)
    31  )
    32  
    33  // Call executes within the given contract
    34  func Call(env vm.Environment, caller vm.ContractRef, addr common.Address, input []byte, gas, gasPrice, value *big.Int) (ret []byte, err error) {
    35  	ret, _, err = exec(env, caller, &addr, &addr, env.Db().GetCodeHash(addr), input, env.Db().GetCode(addr), gas, gasPrice, value)
    36  	return ret, err
    37  }
    38  
    39  // CallCode executes the given address' code as the given contract address
    40  func CallCode(env vm.Environment, caller vm.ContractRef, addr common.Address, input []byte, gas, gasPrice, value *big.Int) (ret []byte, err error) {
    41  	callerAddr := caller.Address()
    42  	ret, _, err = exec(env, caller, &callerAddr, &addr, env.Db().GetCodeHash(addr), input, env.Db().GetCode(addr), gas, gasPrice, value)
    43  	return ret, err
    44  }
    45  
    46  // DelegateCall is equivalent to CallCode except that sender and value propagates from parent scope to child scope
    47  func DelegateCall(env vm.Environment, caller vm.ContractRef, addr common.Address, input []byte, gas, gasPrice *big.Int) (ret []byte, err error) {
    48  	callerAddr := caller.Address()
    49  	originAddr := env.Origin()
    50  	callerValue := caller.Value()
    51  	ret, _, err = execDelegateCall(env, caller, &originAddr, &callerAddr, &addr, env.Db().GetCodeHash(addr), input, env.Db().GetCode(addr), gas, gasPrice, callerValue)
    52  	return ret, err
    53  }
    54  
    55  // Create creates a new contract with the given code
    56  func Create(env vm.Environment, caller vm.ContractRef, code []byte, gas, gasPrice, value *big.Int) (ret []byte, address common.Address, err error) {
    57  	ret, address, err = exec(env, caller, nil, nil, crypto.Keccak256Hash(code), nil, code, gas, gasPrice, value)
    58  	// Here we get an error if we run into maximum stack depth,
    59  	// See: https://github.com/ethereum/yellowpaper/pull/131
    60  	// and YP definitions for CREATE instruction
    61  	if err != nil {
    62  		return nil, address, err
    63  	}
    64  	return ret, address, err
    65  }
    66  
    67  func exec(env vm.Environment, caller vm.ContractRef, address, codeAddr *common.Address, codeHash common.Hash, input, code []byte, gas, gasPrice, value *big.Int) (ret []byte, addr common.Address, err error) {
    68  	evm := env.Vm()
    69  	// Depth check execution. Fail if we're trying to execute above the
    70  	// limit.
    71  	if env.Depth() > callCreateDepthMax {
    72  		caller.ReturnGas(gas, gasPrice)
    73  
    74  		return nil, common.Address{}, errCallCreateDepth
    75  	}
    76  
    77  	if !env.CanTransfer(caller.Address(), value) {
    78  		caller.ReturnGas(gas, gasPrice)
    79  
    80  		return nil, common.Address{}, ValueTransferErr("insufficient funds to transfer value. Req %v, has %v", value, env.Db().GetBalance(caller.Address()))
    81  	}
    82  
    83  	var createAccount bool
    84  	if address == nil {
    85  		// Create a new account on the state
    86  		nonce := env.Db().GetNonce(caller.Address())
    87  		env.Db().SetNonce(caller.Address(), nonce+1)
    88  		addr = crypto.CreateAddress(caller.Address(), nonce)
    89  		address = &addr
    90  		createAccount = true
    91  	}
    92  
    93  	snapshotPreTransfer := env.SnapshotDatabase()
    94  	var (
    95  		from = env.Db().GetAccount(caller.Address())
    96  		to   vm.Account
    97  	)
    98  	if createAccount {
    99  		to = env.Db().CreateAccount(*address)
   100  	} else {
   101  		if !env.Db().Exist(*address) {
   102  			to = env.Db().CreateAccount(*address)
   103  		} else {
   104  			to = env.Db().GetAccount(*address)
   105  		}
   106  	}
   107  	env.Transfer(from, to, value)
   108  
   109  	// initialise a new contract and set the code that is to be used by the
   110  	// EVM. The contract is a scoped environment for this execution context
   111  	// only.
   112  	contract := vm.NewContract(caller, to, value, gas, gasPrice)
   113  	contract.SetCallCode(codeAddr, codeHash, code)
   114  	defer contract.Finalise()
   115  
   116  	ret, err = evm.Run(contract, input)
   117  	// if the contract creation ran successfully and no errors were returned
   118  	// calculate the gas required to store the code. If the code could not
   119  	// be stored due to not enough gas set an error and let it be handled
   120  	// by the error checking condition below.
   121  	if err == nil && createAccount {
   122  		dataGas := big.NewInt(int64(len(ret)))
   123  		// create data gas
   124  		dataGas.Mul(dataGas, big.NewInt(200))
   125  		if contract.UseGas(dataGas) {
   126  			env.Db().SetCode(*address, ret)
   127  		} else {
   128  			err = vm.CodeStoreOutOfGasError
   129  		}
   130  	}
   131  
   132  	// When an error was returned by the EVM or when setting the creation code
   133  	// above we revert to the snapshot and consume any gas remaining. Additionally
   134  	// when we're in homestead this also counts for code storage gas errors.
   135  	if err != nil && (env.RuleSet().IsHomestead(env.BlockNumber()) || err != vm.CodeStoreOutOfGasError) {
   136  		contract.UseGas(contract.Gas)
   137  
   138  		env.RevertToSnapshot(snapshotPreTransfer)
   139  	}
   140  
   141  	return ret, addr, err
   142  }
   143  
   144  func execDelegateCall(env vm.Environment, caller vm.ContractRef, originAddr, toAddr, codeAddr *common.Address, codeHash common.Hash, input, code []byte, gas, gasPrice, value *big.Int) (ret []byte, addr common.Address, err error) {
   145  	evm := env.Vm()
   146  	// Depth check execution. Fail if we're trying to execute above the
   147  	// limit.
   148  	if env.Depth() > callCreateDepthMax {
   149  		caller.ReturnGas(gas, gasPrice)
   150  		return nil, common.Address{}, errCallCreateDepth
   151  	}
   152  
   153  	snapshot := env.SnapshotDatabase()
   154  
   155  	var to vm.Account
   156  	if !env.Db().Exist(*toAddr) {
   157  		to = env.Db().CreateAccount(*toAddr)
   158  	} else {
   159  		to = env.Db().GetAccount(*toAddr)
   160  	}
   161  
   162  	// Iinitialise a new contract and make initialise the delegate values
   163  	contract := vm.NewContract(caller, to, value, gas, gasPrice).AsDelegate()
   164  	contract.SetCallCode(codeAddr, codeHash, code)
   165  	defer contract.Finalise()
   166  
   167  	ret, err = evm.Run(contract, input)
   168  	if err != nil {
   169  		contract.UseGas(contract.Gas)
   170  
   171  		env.RevertToSnapshot(snapshot)
   172  	}
   173  
   174  	return ret, addr, err
   175  }
   176  
   177  // generic transfer method
   178  func Transfer(from, to vm.Account, amount *big.Int) {
   179  	from.SubBalance(amount)
   180  	to.AddBalance(amount)
   181  }