Solidity

EVM 如何找到被呼叫函式的入口?

  • October 17, 2021

我閱讀了 go-ethereum 的原始碼,對 EVM 如何找到被呼叫函式的入口感到困惑。正如規範所說,事務中的數據欄位指定函式和參數。因此,在我的理解中,特定的功能應該在 EVM 中執行,並帶有相關的輸入數據,而不是整個合約程式碼。但是,我找不到相關的程式碼。在執行循環中,PC 從 0 開始:

pc = uint64(0) // program counter
for ; ; instrCount++ {



   // Get the memory location of pc
   op = contract.GetOp(pc)
   // calculate the new memory size and gas price for the current executing opcode
   newMemSize, cost, err = calculateGasAndSize(evm.env, contract, caller, op, statedb, mem, stack)
   if err != nil {
       return nil, err
   }

   // Use the calculated gas. When insufficient gas is present, use all gas and return an
   // Out Of Gas error
   if !contract.UseGas(cost) {
       return nil, OutOfGasError
   }

   // Resize the memory calculated previously
   mem.Resize(newMemSize.Uint64())
   // Add a log message
   if evm.cfg.Debug {
       evm.logger.captureState(pc, op, contract.Gas, cost, mem, stack, contract, evm.env.Depth(), nil)
   }

   if opPtr := evm.jumpTable[op]; opPtr.valid {
       if opPtr.fn != nil {
           opPtr.fn(instruction{}, &pc, evm.env, contract, mem, stack)
       } else {
           switch op {
           case PC:
               opPc(instruction{data: new(big.Int).SetUint64(pc)}, &pc, evm.env, contract, mem, stack)
           case JUMP:
               if err := jump(pc, stack.pop()); err != nil {
                   return nil, err
               }

               continue
           case JUMPI:
               pos, cond := stack.pop(), stack.pop()

               if cond.Cmp(common.BigTrue) >= 0 {
                   if err := jump(pc, pos); err != nil {
                       return nil, err
                   }

                   continue
               }
           case RETURN:
               offset, size := stack.pop(), stack.pop()
               ret := mem.GetPtr(offset.Int64(), size.Int64())

               return ret, nil
           case SUICIDE:
               opSuicide(instruction{}, nil, evm.env, contract, mem, stack)

               fallthrough
           case STOP: // Stop the contract
               return nil, nil
           }
       }
   } else {
       return nil, fmt.Errorf("Invalid opcode %x", op)
   }

   pc++

}

那麼EVM是如何執行具體功能的呢?

具體功能應該在 EVM 中使用相關的輸入數據執行,而不是整個合約程式碼

  1. EVM 將執行合約程式碼。 EVM 只執行字節碼,對函式一無所知。Solidity、Serpent 和 web3.js 實現相同的應用程序二進制介面,這就是函式和數據的編碼方式:什麼是 ABI,為什麼需要它與合約互動?

如此處所述,ABI 是乙太坊協議和 EVM 之上的抽象。

  1. EVM 編譯器根據 ABI**生成模擬功能的合約程式碼。**這是一個例子。

例子

對於遵循 ABI 的函式,編譯器的工作是生成正確的 EVM 字節碼。

一個非常粗略的範例,僅用於概念目的,EVM 編譯器將生成的“跳轉表”字節碼:

method_id = first 4 bytes of msg.data
if method_id == 0x25d8dcf2 jump to 0x11
if method_id == 0xaabbccdd jump to 0x22
if method_id == 0xffaaccee jump to 0x33
other code <- Solidity fallback function code could be here
0x11:
code for function with method id 0x25d8dcf2
0x22:
code for function with method id 0xaabbccdd
0x33:
code for function with method id 0xffaaccee

可以看到msg.data(Method ID 是 ABI 給出的術語)的前 4 個字節用於檢查跳轉到哪個函式並執行。

從範例中,您還可以看到,您可以讓自己的編譯器生成具有不同邏輯的字節碼(也許您想使用 msg.data 的前 8 個字節),但是呼叫者必須遵循這些約定,而不是簡單地使用像 web3.js 這樣的庫。

範例來自:

什麼時候呼叫回退函式?

ABI 如何儲存在字節碼中?

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