Remix

理解一個簡單的合約是如何分解成字節碼的

  • September 13, 2018

我試圖了解合約在字節碼方面的外觀,並僅根據黃皮書努力做到這一點。特別是,考慮以下空合約:

pragma solidity ^0.4.17;

contract Simplest {
}

使用Remix編譯器,我可以編譯成以下字節碼:

6080604052348015600f57600080fd5b50603580601d6000396000f3006080604052600080fd00a165627a7a7230582053a24015f887e1dd9fbd5e5cadb397bb5fb34e8aab7b5782d9e28dfd4e9862810029

轉換為以下操作碼:

PUSH1 0x80 PUSH1 0x40 MSTORE CALLVALUE DUP1 ISZERO PUSH1 0xF JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH1 0x35 DUP1 PUSH1 0x1D PUSH1 0x0 CODECOPY PUSH1 0x0 RETURN STOP PUSH1 0x80 PUSH1 0x40 MSTORE PUSH1 0x0 DUP1 REVERT STOP LOG1 PUSH6 0x627A7A723058 KECCAK256 MSTORE8 LOG2 BLOCKHASH ISZERO 0xf8 DUP8 0xe1 0xdd SWAP16 0xbd 0x5e 0x5c 0xad 0xb3 SWAP8 0xbb 0x5f 0xb3 0x4e DUP11 0xab PUSH28 0x5782D9E28DFD4E986281002900000000000000000000000000000000

1)這代表什麼?這是否意味著建構子的字節碼?

2)有幾個序列對我來說沒有意義,特別是:

ISZERO 0xf8 DUP8 0xe1 0xdd SWAP16

為什麼要0xf8, 0xe1, 0xdd遵循不帶任何參數的操作碼?他們的目的是什麼?

  1. 合約編譯完成後,合約“功能”的字節碼在哪裡?

我懷疑這三個問題非常相關,因此將它們一起問。

你在這裡得到的字節碼是合約的建構子/構造字節碼。當您創建合約時,建構子會執行,處理您所做的任何初始參數或語句,並“創建”合約程式碼。

完成此操作的方式是通過 return 語句。部署的合約程式碼將是建構子返回的任何內容。因此,您發布的字節碼將包括建構子的字節碼,以及已部署合約本身的程式碼。它還包括第三部分 - 一些元數據(Solidity 功能)。分解它,這裡有 3 個不同的部分:

建構子字節碼(基本上是一個部署腳本):

0x6080604052348015600f57600080fd5b50603580601d6000396000f300

這轉換為以下操作碼,我已經對其進行了一些註釋:

// Set free memory pointer (0x40) to 0x80
[1] PUSH1 0x80
[3] PUSH1 0x40
[4] MSTORE
// Check msg.value
[5] CALLVALUE
[6] DUP1
[7] ISZERO
// If msg.value == 0, JUMP to 0x0F (15)
[9] PUSH1 0x0f
[10] JUMPI
// Otherwise, the next 3 instructions do: revert(0, 0)
[12] PUSH1 0x00
[13] DUP1
[14] REVERT
// Here's 0x0F, which has a corresponding JUMPDEST
[15] JUMPDEST
[16] POP
// Now we need the constructor to set up the deployment bytecode:
// 0x35 is presumably the length of the bytecode to return
[18] PUSH1 0x35
[19] DUP1
// 0x1D is the location in the bytecode from which the CODECOPY starts (29)
[21] PUSH1 0x1d
// 0x00 is where the code is copied to, in memory
[23] PUSH1 0x00
[24] CODECOPY
// Now we have the bytecode of the contract in memory, starting at 0x00 (and 0x35 bytes long). We DUP1'd the length earlier, so we can just push 0x00 and RETURN. The RETURN opcode will return 0x35 bytes, starting at position 0x00 in memory.
[26] PUSH1 0x00
[27] RETURN
[28] STOP 
// A final STOP at 28 (0x1C) marks the end of the constructor bytecode. The code copied started at 0x1D, so the next chunk of bytecode will be the deployed code

現在這裡是合約字節碼(最後是元數據):

0x6080604052600080fd00a165627a7a7230582053a24015f887e1dd9fbd5e5cadb397bb5fb34e8aab7b5782d9e28dfd4e9862810029

同樣,翻譯成操作碼:

// Set free memory pointer (0x40) to 0x80
[30] PUSH1 0x80
[32] PUSH1 0x40
[33] MSTORE
// The next 3 opcodes simply push 0 twice, then REVERT(0, 0)
[35] PUSH1 0x00
[36] DUP1
[37] REVERT
// And, a final STOP to mark the end of contract bytecode!
[38] STOP
// This next bit is confusing when translated directly from opcodes, because it's simply the metadata Solidity appends to the end of bytecode. It's not meant to be executed
[39] LOG1
[46] PUSH6 0x627a7a723058
[47] SHA3
[48] MSTORE8
[49] LOG2
[50] BLOCKHASH
[51] ISZERO
[52] 'f8'(Unknown Opcode)
[53] DUP8
[54] 'e1'(Unknown Opcode)
[55] 'dd'(Unknown Opcode)
[56] SWAP16
[57] 'bd'(Unknown Opcode)
[58] '5e'(Unknown Opcode)
[59] '5c'(Unknown Opcode)
[60] 'ad'(Unknown Opcode)
[61] 'b3'(Unknown Opcode)
[62] SWAP8
[63] 'bb'(Unknown Opcode)
[64] '5f'(Unknown Opcode)
[65] 'b3'(Unknown Opcode)
[66] '4e'(Unknown Opcode)
[67] DUP11
[68] 'ab'(Unknown Opcode)

以下是有關合約元數據的更多資訊:https ://solidity.readthedocs.io/en/v0.4.24/metadata.html

所以,直接回答你的問題:

  1. 是的,這是建構子字節碼。由於建構子需要返回部署字節碼,因此建構子字節碼也包含部署字節碼。
  2. 您所指的序列來自契約的元數據,並不意味著要執行。
  3. 您編譯的合約沒有任何功能,因此整個“執行時字節碼”是已部署字節碼的 10 操作碼部分,它設置空閒記憶體指針並立即恢復。帶有函式的典型合約會做一些稍微不同的事情——它將獲取 calldata 的前 4 個字節(使用CALLDATALOAD),並將它們與一系列函式選擇器進行比較。當它找到一個匹配項時,它將JUMP到該函式在程式碼中的位置。如果未找到匹配項,則執行回退功能。如果不存在,它會恢復!

希望清除它!隨時提出更多問題!

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