您好,登錄后才能下訂單哦!
本篇文章為大家展示了如何理解Solidity的Bytecode和Opcode,內容簡明扼要并且容易理解,絕對能使你眼前一亮,通過這篇文章的詳細介紹希望你能有所收獲。
Solidity的Bytecode和Opcode
隨著我們更深入地編寫智能合約,我們將遇到諸如“ PUSH1”,“ SSTORE”,“ CALLVALUE”等術語。 他們是什么,我們什么時候應該使用到他們?
要了解這些命令,我們必須更深入地了解以太坊虛擬機(EVM)。本文將會嘗試盡可能簡單地解釋一些EVM基礎。希望大家都有所收獲。
像許多其他流行的編程語言一樣,Solidity是一種高級編程語言。 我們可以讀懂,但是機器卻不能夠。 如果大家學過諸如java,c++等編程語言,應該會很容易明白這個道理。
當我們安裝諸如geth之類的以太坊客戶端時,它還附帶了以太坊虛擬機,這是專門為運行智能合約而創建的輕量級操作系統。
當我們使用solc編譯器編譯Solidity代碼時,它將代碼轉換為只有EVM可以理解的字節碼。
讓我們以一個非常簡單的合同為例:
pragma solidity ^0.4.26; contract OpcodeContract { uint i = (10 + 2) * 2; }
如果我們在remix瀏覽器中運行此代碼,然后單擊合同詳細信息,則會看到很多信息。
在這種情況下,編譯后的代碼為:
BYTECODE { "linkReferences": {}, "object": "60806040526018600055348015601457600080fd5b5060358060226000396000f3006080604052600080fd00a165627a7a72305820db1d567e501f1682876df36eea80a02d25a8b2adb186da705e2e98e134b08cc60029", "opcodes": "PUSH1 0x80 PUSH1 0x40 MSTORE PUSH1 0x18 PUSH1 0x0 SSTORE CALLVALUE DUP1 ISZERO PUSH1 0x14 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH1 0x35 DUP1 PUSH1 0x22 PUSH1 0x0 CODECOPY PUSH1 0x0 RETURN STOP PUSH1 0x80 PUSH1 0x40 MSTORE PUSH1 0x0 DUP1 REVERT STOP LOG1 PUSH6 0x627A7A723058 KECCAK256 0xdb SAR JUMP PUSH31 0x501F1682876DF36EEA80A02D25A8B2ADB186DA705E2E98E134B08CC6002900 ", "sourceMap": "25:54:0:-;;;64:12;55:21;;25:54;8:9:-1;5:2;;;30:1;27;20:12;5:2;25:54:0;;;;;;;" }
其中object就是編譯后的代碼。他們是最終合同的十六進制表示形式,也稱為字節碼。
在remix瀏覽器的“ Web3 Deploy”部分下,我們看到:
var opcodecontractContract = web3.eth.contract([]); var opcodecontract = opcodecontractContract.new( { from: web3.eth.accounts[0], data: '0x60806040526018600055348015601457600080fd5b5060358060226000396000f3006080604052600080fd00a165627a7a72305820db1d567e501f1682876df36eea80a02d25a8b2adb186da705e2e98e134b08cc60029', gas: '4700000' }, function (e, contract){ console.log(e, contract); if (typeof contract.address !== 'undefined') { console.log('Contract mined! address: ' + contract.address + ' transactionHash: ' + contract.transactionHash); } })
簡單來說,這意味著當我們部署合同時,我們需要將編譯后的16進制碼當成data傳遞,并且建議的gas為4700000。
任何以“ 0x”開頭的內容都表示該值采用十六進制格式。 十六進制前面的“ 0x”并不是強制的,因為EVM會將任何值都視為十六進制。
我們還看到了操作代碼(又稱Opcode):
"opcodes": "PUSH1 0x80 PUSH1 0x40 MSTORE PUSH1 0x18 PUSH1 0x0 SSTORE CALLVALUE DUP1 ISZERO PUSH1 0x14 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH1 0x35 DUP1 PUSH1 0x22 PUSH1 0x0 CODECOPY PUSH1 0x0 RETURN STOP PUSH1 0x80 PUSH1 0x40 MSTORE PUSH1 0x0 DUP1 REVERT STOP LOG1 PUSH6 0x627A7A723058 KECCAK256 0xdb SAR JUMP PUSH31 0x501F1682876DF36EEA80A02D25A8B2ADB186DA705E2E98E134B08CC6002900 ",
操作碼是程序的低級可讀指令。 所有操作碼都具有對應的十六進制值,例如“ MSTORE”為“ 0x52”,SSTORE”為“ 0x55”……等等。
具體的操作碼對應的數值可以參考以太坊相關資料。
EVM虛擬機是一中堆棧虛擬機,所謂堆棧就是后進先出結構,用計算機科學術語來說,我們稱為LIFO。
舉個例子,上面的智能合約,如果在普通算術中,我們這樣寫方程式:
// Answer is 14. we do multiplication before addition. 10 + 2 * 2
在EVM堆棧虛擬機中,它按照LIFO原理工作,所以我們需要這樣寫:
2 2 * 10 +
這意味著,首先將“ 2”放入堆棧,然后再放入另一個“ 2”,然后再進行乘法運算。 結果是“ 4”放在在堆棧頂部。 現在在“ 4”的頂部加上數字“ 10”,最后將兩個數字加在一起。 堆棧的最終值為14。
這種算術類型稱為后綴表示法。
將數據放入堆棧的動作稱為“ PUSH”指令,將數據從堆棧中刪除的動作稱為“ POP”指令。 很明顯,我們在上面的示例中看到的最常見的操作碼是“ PUSH1”,這意味著將1個字節的數據放入堆棧中。
因此,此指令:
PUSH1 0x80
表示將1字節值“ 0x80”放入堆棧中。 “ PUSH1”的十六進制值恰是“ 0x60”。 刪除非強制性的“ 0x”,我們可以將此邏輯以字節碼形式寫為“ 6080”。
讓我們更進一步。
PUSH1 0x80 PUSH1 0x40 MSTORE
再次查看以太坊的操作碼圖表,我們看到MSTORE(0x52)接受2個輸入,但不產生任何輸出。 上面的操作碼表示:
PUSH1(0x60):將0x80放入堆棧。
PUSH1(0x40):將0x40放入堆棧。
MSTORE(0x52):分配0x80的內存空間并移至0x40的位置。
結果字節碼為:
6080604052
實際上,在任何固定字節碼的開頭,我們總會看到這個魔術數字“ 6080604052”,因為它是智能合約引導的方式。
請注意,這里不能將0x40或0x60解釋為實數40或60。由于它們是十六進制,所以40實際上等于十進制的64(16 x 4),而80等于十進制的128(16 x 8)。
簡而言之,“ PUSH1 0x80 PUSH1 0x40 MSTORE”正在做的是分配128個字節的內存并將指針移到第64個字節的開頭。現在,我們有64個字節用于暫存空間,而64個字節用于臨時內存存儲。
在EVM中,有3個地方可以存儲數據。首先,在堆棧中,按照上面的示例,我們剛剛使用了“ PUSH”操作碼在此處存儲數據。其次,在使用“ MSTORE”操作碼的內存(RAM)中,最后在使用“ SSTORE”存儲數據的磁盤存儲中。將數據存儲到磁盤存儲所需的gas最昂貴,而將數據存儲到堆棧中的gas則最便宜。
我們在Solidity中的智能合約中,有時候也會用到Assembly Language,這個Assembly Language就是使用這樣的匯編Opcode來操作EVM字節碼。他理解起來比較難,但是通過使用它可以節省燃料和做一些無法通過Solidity完成的事情。
上述內容就是如何理解Solidity的Bytecode和Opcode,你們學到知識或技能了嗎?如果還想學到更多技能或者豐富自己的知識儲備,歡迎關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。