Evm

呼叫值操作碼,用於什麼?

  • August 23, 2018

有人知道操作碼 CALLVALUE 的用途嗎?我想很好地理解它。你有一個我可以很容易理解的例子嗎?

當合約呼叫發生時,它會在堆棧上接收一些參數,這些參數中Value包含了該合約將接收到的用於對其進行一些操作的 ETH 數量。

emv.Call函式如下所示:

func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas uint64, value *big.Int) (ret []byte, leftOverGas uint64, err error) {
   if evm.vmConfig.NoRecursion && evm.depth > 0 {
       return nil, gas, nil
   }

   // Fail if we're trying to execute above the call depth limit
   if evm.depth > int(params.CallCreateDepth) {
       return nil, gas, ErrDepth
   }
   // Fail if we're trying to transfer more than the available balance
   if !evm.Context.CanTransfer(evm.StateDB, caller.Address(), value) {
       return nil, gas, ErrInsufficientBalance
   }

   var (
       to       = AccountRef(addr)
       snapshot = evm.StateDB.Snapshot()
   )
   if !evm.StateDB.Exist(addr) {
       precompiles := PrecompiledContractsHomestead
       if evm.ChainConfig().IsByzantium(evm.BlockNumber) {
           precompiles = PrecompiledContractsByzantium
       }
       if precompiles[addr] == nil && evm.ChainConfig().IsEIP158(evm.BlockNumber) && value.Sign() == 0 {
           // Calling a non existing account, don't do antything, but ping the tracer
           if evm.vmConfig.Debug && evm.depth == 0 {
               evm.vmConfig.Tracer.CaptureStart(caller.Address(), addr, false, input, gas, value)
               evm.vmConfig.Tracer.CaptureEnd(ret, 0, 0, nil)
           }
           return nil, gas, nil
       }
       evm.StateDB.CreateAccount(addr)
   }
   evm.Transfer(evm.StateDB, caller.Address(), to.Address(), value)

   // Initialise a new contract and set the code that is to be used by the EVM.
   // The contract is a scoped environment for this execution context only.
   contract := NewContract(caller, to, value, gas)
   contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr))

   start := time.Now()

   // Capture the tracer start/end events in debug mode
   if evm.vmConfig.Debug && evm.depth == 0 {
       evm.vmConfig.Tracer.CaptureStart(caller.Address(), addr, false, input, gas, value)

       defer func() { // Lazy evaluation of the parameters
           evm.vmConfig.Tracer.CaptureEnd(ret, gas-contract.Gas, time.Since(start), err)
       }()
   }
   ret, err = run(evm, contract, input)

   // When an error was returned by the EVM or when setting the creation code
   // above we revert to the snapshot and consume any gas remaining. Additionally
   // when we're in homestead this also counts for code storage gas errors.
   if err != nil {
       evm.StateDB.RevertToSnapshot(snapshot)
       if err != errExecutionReverted {
           contract.UseGas(contract.Gas)
       }
   }
   return ret, contract.Gas, err
}

如果您注意到,它會獲取Valuein 參數並將其儲存在合約結構中:

// Contract represents an ethereum contract in the state database. It contains
// the the contract code, calling arguments. Contract implements ContractRef
type Contract struct {
   // CallerAddress is the result of the caller which initialised this
   // contract. However when the "call method" is delegated this value
   // needs to be initialised to that of the caller's caller.
   CallerAddress common.Address
   caller        ContractRef
   self          ContractRef

   jumpdests destinations // result of JUMPDEST analysis.

   Code     []byte
   CodeHash common.Hash
   CodeAddr *common.Address
   Input    []byte

   Gas   uint64
   value *big.Int

   Args []byte

   DelegateCall bool
}

Value是儲存的位置:

value *big.Int

現在在evm.Call()函式中你會看到這一行:

contract := NewContract(caller, to, value, gas)

此函式儲存以Value供進一步執行:

// NewContract returns a new contract environment for the execution of EVM.
func NewContract(caller ContractRef, object ContractRef, value *big.Int, gas uint64) *Contract {
   c := &Contract{CallerAddress: caller.Address(), caller: caller, self: object, Args: nil}

   if parent, ok := caller.(*Contract); ok {
       // Reuse JUMPDEST analysis from parent context if available.
       c.jumpdests = parent.jumpdests
   } else {
       c.jumpdests = make(destinations)
   }

   // Gas should be a pointer so it can safely be reduced through the run
   // This pointer will be off the state transition
   c.Gas = gas
   // ensures a value is set
   c.value = value

   return c
}

當 EVM 遇到 CALLVALUE 操作碼時,它會獲取Value之前儲存在Contract結構體中的資訊:

func opCallValue(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
   stack.push(evm.interpreter.intPool.get().Set(contract.value))
   return nil, nil
}

因此,簡而言之,CALLVALUE為您提供通過父合約或交易轉移到合約的 ETH 數量。抱歉解釋了這麼久。

引用自:https://ethereum.stackexchange.com/questions/57263