(https://ethernaut.openzeppelin.com/level/0x200d3d9Ac7bFd556057224e7aEB4161fED5608D0)
這一關的目標是寫一個contract,其中包含一個函數whatIsTheMeaningOfLife()來回答正確的數字*。看起來很簡單啊,我們會寫smart contract,這題沒甚麼。但是,題目還有一個小小的要求,真的是小小的,就是這個合約必須是小小的,最多只能是10 opcodes的大小。 哪泥?
opcodes
首先說明一下甚麼是Opcodes,我們知道以太坊ethernum是運行在EVM(Ethereum Virtual Machine)上,而在EVM上當然讀的是bytecodes,就是長得像這樣的0x8ab375ddf1d00892ef52的hexadecimal(0~9,a~f的16進制數),機器看得懂,人看不太懂,開發者寫的是.sol的code,就是我們講得智能合約的程式,而中間則有一層assembly code,可以用來解釋程式成為一串的bytecodes;例如 push1 2a,就是將2a放到目前的stack (EVM是一個stack machine,stack machine是一種後進先出的儲存機制)。因為push1的bytecodes是60,因此程式上寫 x = 42 ,opcodes翻譯成 push 2a,再翻譯成EVM看得懂的602a。
合約生成的bytecode產生流程
這邊用說的比較繁雜,使用前人畫的圖來輔助說明:
一開始我們寫Solidity合約存成.sol檔,經過編譯後,變成上面解釋的ByteCode的形狀,而一個合約的code其實包含兩部分,第一部分是creation code,這部分是告訴EVM要deploy一個新的合約及相關的參數,包括consturctor的內容,第二部分則是會存在EVM的runtime code,可以想成函數的部分,而當我們用web3.the.sendTransaction({data: bytecode})將我們的程式進行deploy(或是用remix的deploy),EVM會先根據creation code產生合約,並產生contract的address,然後將Runtime code存在Ethereum中可重複呼叫執行。
有了上述,基本上這關我們要建立我們合約的creation code及runtime code,其中runtime code就是傳回42這個數字(return of whatIsTheMeaningOfLife()),而這邊的creation code基本上因為contractor並沒有做甚麼事,整個合約就是runtime code,因此creation code就是將runtime code存到EVM上(也就是最後return runtime code)
runtime code:
# (bytes) | OPCODE | Stack (left to right = top to bottom) | Meaning | bytecode |
---|---|---|---|---|
00 | PUSH1 2a | push 2a (hexadecimal) = 42 (decimal) to the stack | 602a | |
02 | PUSH1 00 | 2a | push 00 to the stack | 6000 |
05 | MSTORE | 00, 2a | mstore(0, 2a) , store 2a = 42 at memory position 0 | 52 |
06 | PUSH1 20 | push 20 (hexadecimal) = 32 (decimal) to the stack (for 32 bytes of data) | 6020 | |
08 | PUSH1 00 | 20 | push 00 to the stack | 6000 |
10 | RETURN | 00, 20 | return(memory position, number of bytes) , return 32 bytes stored in memory position 0 | f3 |
creation code:
# (bytes) | OPCODE | Stack (left to right = top to bottom) | Meaning | bytecode |
---|---|---|---|---|
00 | PUSH10 602a60005260206000f3 | push the 10 bytes of runtime bytecode to the stack | 69602a60005260206000f3 | |
03 | PUSH 00 | 602a60005260206000f3 | push 0 to the stack | 6000 |
05 | MSTORE | 0, 602a60005260206000f3 | mstore(0, 0x602a60005260206000f3) 0 | 52 |
06 | PUSH a | push a = 10 (decimal) to the stack | 600a | |
08 | PUSH 16 | a | push 16 = 22 (decimal) to the stack | 6016 |
10 | RETURN | 16, a | return(0x16, 0x0a) | f3 |
從上面兩個table,我們可以組出0x69602a60005260206000f3600052600a6016f3,接下來就用web3.eth.sendTransaction()將此合約deploy上去,deploy上去後會得到contractAddress,然後呼叫本關的setSolver(),最後submit,得到人生所有問題的解答~(console中輸入如下:)
await web3.eth.sendTransaction({ from:player, data: '0x69602a60005260206000f3600052600a6016f3' });
//會取得contractAddress
await contract.setSolver("<所取得的contractAddress>");
附註:
一群超空間的智慧生物為了得到終極問題的解答,打造了一部超級電腦「Deep Thought 深思」,花了750萬年的時間運算,終於得到答案。
「終極問題的解答——生命、宇宙、萬事萬物的唯一解是:42!」
這是出自一本非常有名的科幻小說: 銀河便車指南,作者為道格拉斯亞當斯(Douglas Noël Adams),這部科幻小說也拍成過電影喔(電影名稱中文叫做星際大奇航)。你怎麼能不愛科幻呢~