嘗試拆解Solidity編譯後的bytecode(二)
以下範例皆參考 openzeppelin opcode 的系列文章(共六篇)撰寫。
-
Deconstructing a Solidity Contract — Part II: Creation vs. Runtime
-
Deconstructing a Solidity Contract — Part III: The Function Selector
-
Deconstructing a Solidity Contract — Part IV: Function Wrappers
-
Deconstructing a Solidity Contract — Part V: Function Bodies
-
Deconstructing a Solidity Contract — Part VI: The Metadata Hash
本篇文章建議有 solidity 開發經驗的讀者閱讀,初學者不適合本篇文章。本文與Solidity opcode基礎有相關性,建議閱讀後再讀本篇文章。
本文與嘗試拆解Solidity編譯後的bytecode(一)有聯貫性,沒有讀過第一篇的讀者大機率看不懂本文!
前情提要
在第一篇文章,我們得知 creation code 是合約的 constructor。合約佈署時,先執行 creation code,執行完成後會返回 runtime code。runtime code代表非 constructor 的程式碼,也就是 developer 寫的各個 function,最終會留在以太鏈的 bytecode。
今天我們就要來了解 runtime code 的「架構」!
前置作業
用 keccak256 算出合約內 3 個 function 的 function signature
-
totalSupply()的function signature:0x18160ddd
-
balanceOf(address)的function signature:0x70a08231
-
transfer(address,uint256)的function signature:0xa9059cbb
專有名詞
-
Function selector,透過 function signature 選出欲執行的 function,選出後 jump 至 function wrapper
-
Function wrapper,檢查 function 的 input 參數,檢查後 jump至 function body,執行完 function body後,回傳 return value
-
Function body,實際執行 function 的程式
執行過程
runtime code編譯後的結果,建議可以再搭配remix debug工具自己執行一次
-
calldataload 載入calldata(與solidity的calldata只的是一樣的東西)
-
calldata 會攜帶 function signature,透過 function selector即可解析出該執行哪個function(注意框起來的 16 進制碼,該16進制碼即是各個 function signature。EQ判斷是否mapping成功,成功則執行jump)
-
選出執行哪個 function 後 jump 至 function wrapper
-
function wrapper 檢查完 input 參數後,jump 至 function body
-
function body 執行完後 jump 回 function wrapper
-
在 function wrapper 回傳 return value
Openzeppelin的opcode拆解視覺化圖: https://gists.rawgit.com/ajsantander/23c032ec7a722890feed94d93dff574a/raw/a453b28077e9669d5b51f2dc6d93b539a76834b8/BasicToken.svg
以上可參考openzeppelin提供的opcode拆解視覺化圖及文章開頭的文章,function wrapper和function body建議搭配opcode拆解視覺化圖看較為清楚