1. 主页
  2. 文档
  3. Ethernaut 题库闯关
  4. #3 投掷硬币

#3 投掷硬币

挑战:投掷硬币

今天需要 Hack 一个投掷硬币(CoinFlip)的游戏合约,要求连续10次猜出投掷硬币正确的结果。

以下是投掷硬币(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;
    }
  }
}

我们需要通过调用flip()函数传递正确的猜测,实现连续猜出硬币的翻转结果。

在查看解题思路之前,可以先自己想一想,自己会怎么做。

研究合约

首先我们注意到,所使用的Solidity编译器版本是< 0.8.x。这意味着该合约很容易出现数学下溢和溢出的错误。

这个合约导入并使用了OpenZeppelin SafeMath库,因此他们应该没有溢出问题。

合约有三个状态变量:

  • consecutiveWinsconstructor中被初始化为0。这个变量将保存我们连续猜对了多少次。
  • FACTOR,被声明为57896044618658097711785492504343953926634992332820282019728792003956564819968。Gas优化提示:可以声明为constant以节省Gas(见进一步阅读)。
  • lastHash,将由flip()函数每次更新。

合约中唯一的函数是flip(),让我们看看它的作用:

function flip(bool _guess) public returns (bool) {
    uint256 blockValue = uint256(blockhash(block.number.sub(1)));
    if (lastHash =...
这篇文章对您有用吗?

我们要如何帮助您?