Ethernaut – level 18 MagicNumber

(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產生流程

這邊用說的比較繁雜,使用前人畫的圖來輔助說明:

Source: https://listed.to/@r1oga/

一開始我們寫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)OPCODEStack (left to right = top to bottom)Meaningbytecode
00PUSH1 2apush 2a (hexadecimal) = 42 (decimal) to the stack602a
02PUSH1 002apush 00 to the stack6000
05MSTORE00, 2amstore(0, 2a), store 2a = 42 at memory position 052
06PUSH1 20push 20 (hexadecimal) = 32 (decimal) to the stack (for 32 bytes of data)6020
08PUSH1 0020push 00 to the stack6000
10RETURN00, 20return(memory position, number of bytes), return 32 bytes stored in memory position 0f3

creation code:

# (bytes)OPCODEStack (left to right = top to bottom)Meaningbytecode
00PUSH10 602a60005260206000f3push the 10 bytes of runtime bytecode to the stack69602a60005260206000f3
03PUSH 00602a60005260206000f3push 0 to the stack6000
05MSTORE0, 602a60005260206000f3mstore(0, 0x602a60005260206000f3)052
06PUSH apush a = 10 (decimal) to the stack600a
08PUSH 16apush 16 = 22 (decimal) to the stack6016
10RETURN16, areturn(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),這部科幻小說也拍成過電影喔(電影名稱中文叫做星際大奇航)。你怎麼能不愛科幻呢~