(https://ethernaut.openzeppelin.com/level/0xdCeA38B2ce1768E1F409B6C65344E81F16bEc38d)
這一題的結構跟前一題一樣,目標也一樣,但3 個modifier的要求不同:
modifier gateOne() {
require(msg.sender != tx.origin);
_;
}
modifier gateTwo() {
uint x;
assembly { x := extcodesize(caller()) }
require(x == 0);
_;
}
modifier gateThree(bytes8 _gateKey) {
require(uint64(bytes8(keccak256(abi.encodePacked(msg.sender)))) ^ uint64(_gateKey) == uint64(0) - 1);
_;
}
gateOne:
與前一關的gateOne相同,需要用一個部署的合約去呼叫enter。
gateTwo:
- assembly: solidiy定義了assembly code(類似組合語言的作用),這個assembly code可在solidity code中做為 inline assembly,其Syntax為包在assembly{} 內,舉例包括如下:
- functional-style opcodes: mul(1, add(2,3)) 而不須用push1 3 push1 2 add push1 1 mul
- assembly-local variables: let x:= add(2,3)
- (可參考這裡)
- extcodesize(contract address): 此函數傳回該address的合約的byte code長度,而這題傳入的是caller(),要怎麼讓我們自己的寫的合約能呼叫,還能長度為零呢? 先公布答案,在consturctor中呼叫,完整說明請看文章最後備註(合約部署) 。
gateThree:
- ^這個符號為XOR的意思。
- A^B=C 則 A^C=B
- uint64(0)-1 表示uint64的最大值 (0xffffffffffffffff, 亦即0-1,overflow後的結果)
總和以上,我們要解這題於是在Remix上寫上如下合約
contract goGateKeeper_Two{
constructor(address _instance) public {
IGateKeeper_Two two= IGateKeeper_Two(_instance);
bytes8 _gateKey = bytes8(uint64(bytes8(keccak256(abi.encodePacked(address(this))))) ^ (uint64(0) - 1));
two.enter(_gateKey);
}
}
一樣,我們可以在console輸入 await contract.entrant() 確認一下是否已經變成entrant囉,上傳,過關,敲鑼,打鼓,吃雞。
*備註
在Ethereum yellow paper中,說明到智慧合約的創建:
整個創建的流程如下:
- 當一個合約送到以太坊網路,會產生一個transaction,這個 transaction會包含如上圖中的變數:
- Sender (s): 創建contract(送出transaction的地址),可能是錢包位址也可能是合約位址(合約創建的合約)。
- Original transactor (o): 原本的創建者,若合約是由合約創建的,則 o != s。(可想成 msg.sender 與 tx.origin的不同)。
- Available gas (g): gas allocated to this contract。
- Gas price (p): gas的市場價格,用以將gas轉成Ethers的值。
- Endowment (v): value 轉至此合約的數量(in Wei) as seed。defalut is 0。
- Initialization EVM code (i): 就是constructor內的code及初始化的變數,in bytecode format。
- 根據上述的變數,新的合約的位址將被(pre) 計算出來。此時的contract’s state still empty。
- 第三步,創建實體的contract。
- 過程中,state changed、資料儲存、gas使用。
- 當合約完成初始化上述步驟後,將其code存起來並關連到上述的contract address。
- 最後,將部署的成功/失敗訊息及剩餘的gas (asynchronously)返回給 sender(s)。
由上可發現,直到第5步之前,雖然已經有了合約的address,但並沒有任何code存在此address!因此,此時的extcodesize回傳會為0。