在介紹 Opcodes 前,先介紹一支 solidity 程式是如何執行的
我們先寫一組簡單的 solidity code
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
contract HelloWorld {
string public hello = "Hello World!";
}
compile (或用 remix 編譯)
# 用 docker compile
docker pull ethereum/solc:0.8.20
docker run --rm ethereum/solc:0.8.20
可以編譯成以下 bytecode 這些 bytecode 就是
60806040526040518060400160405280600c81526020017f48656c6c6f20576f726c642100000000000000000000000000000000000000008152505f90816100479190610293565b50348015610053575f80fd5b50610362565b5f81519050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b5f60028204905060018216806100d457607f821691505b6020821081036100e7576100e6610090565b5b50919050565b5f819050815f5260205f209050919050565b5f6020601f8301049050919050565b5f82821b905092915050565b5f600883026101497fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8261010e565b610153868361010e565b95508019841693508086168417925050509392505050565b5f819050919050565b5f819050919050565b5f61019761019261018d8461016b565b610174565b61016b565b9050919050565b5f819050919050565b6101b08361017d565b6101c46101bc8261019e565b84845461011a565b825550505050565b5f90565b6101d86101cc565b6101e38184846101a7565b505050565b5b81811015610206576101fb5f826101d0565b6001810190506101e9565b5050565b601f82111561024b5761021c816100ed565b610225846100ff565b81016020851015610234578190505b610248610240856100ff565b8301826101e8565b50505b505050565b5f82821c905092915050565b5f61026b5f1984600802610250565b1980831691505092915050565b5f610283838361025c565b9150826002028217905092915050565b61029c82610059565b67ffffffffffffffff8111156102b5576102b4610063565b5b6102bf82546100bd565b6102ca82828561020a565b5f60209050601f8311600181146102fb575f84156102e9578287015190505b6102f38582610278565b86555061035a565b601f198416610309866100ed565b5f5b828110156103305784890151825560018201915060208501945060208101905061030b565b8683101561034d5784890151610349601f89168261025c565b8355505b6001600288020188555050505b505050505050565b6102138061036f5f395ff3fe608060405234801561000f575f80fd5b5060043610610029575f3560e01c806319ff1d211461002d575b5f80fd5b61003561004b565b6040516100429190610160565b60405180910390f35b5f8054610057906101ad565b80601f0160208091040260200160405190810160405280929190818152602001828054610083906101ad565b80156100ce5780601f106100a5576101008083540402835291602001916100ce565b820191905f5260205f20905b8154815290600101906020018083116100b157829003601f168201915b505050505081565b5f81519050919050565b5f82825260208201905092915050565b5f5b8381101561010d5780820151818401526020810190506100f2565b5f8484015250505050565b5f601f19601f8301169050919050565b5f610132826100d6565b61013c81856100e0565b935061014c8185602086016100f0565b61015581610118565b840191505092915050565b5f6020820190508181035f8301526101788184610128565b905092915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b5f60028204905060018216806101c457607f821691505b6020821081036101d7576101d6610180565b5b5091905056fea2646970667358221220332b69fc79033f9063e1379e1102d13deb68163fdd8c398a20ecc0c6858351ee64736f6c63430008150033
再來將這組 contract deploy 到 remix 的 VM 上
接下來我們會利用到旁邊那個 Debug 按鈕
再來按下 step into,可以跑到我們第一行 bytecode ,可以看到是 60,而 0x60 的 Opcodes 正是 PUSH1,所以 6080就是將 80 推入 stack 中,可以看到下圖的 stack 0 會有個 0x80 的值儲存在裡面,所以我們可以看到這堆 bytecode 就是 EVM 的機器語言,在 EVM 會將這些 code 根據 Opcode 執行不同行為或運算,而每個 Opcode 都有不同的行為,接下來就來介紹 Opcode
在實際交易執行就會由下圖的方式做交易,先由 EOA 做訊息傳遞,而訊息內容就是先將 function 做 sha3 來讓 EVM 從 storage 讀取合約帳戶狀態並執行內部 EVM bytecode 並執行或運算來改變狀態.
如上所述,以太坊的 stack 會根據這些編譯過後的 bytecode 來運作,與大多數虛擬機採用的二進制表示法不同,為便於記憶與可讀性,EVM 所有的 Opcode 都以1 byte 表示,並附加一個名稱,而每個指令會介於 0~255(00 ~ FF) 之間,如同上面的 bytecode 所示,EVM 會讀取上述的指令並執行,如果指令無法執行就會被 revert
Opcode 分類
接下來下幾堂課就是開始介紹如何實現這些 opcode