[Ethernaut CTF] Coin Flip

0xDave·2022년 10월 3일
0

Ethereum

목록 보기
24/112

소스코드


// 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;
    }
  }
}

해결 과제


This is a coin flipping game where you need to build up your winning streak by guessing the outcome of a coin flip. To complete this level you'll need to use your psychic abilities to guess the correct outcome 10 times in a row.

  Things that might help

    See the Help page above, section "Beyond the console"

10번 연속으로 동전의 앞뒷면을 맞추면 된다.

해결 과정


sub(1)

먼저 sub(A, B) 함수는 A에서 B를 뺀 결과를 리턴한다. 그런데 인자로 하나만 넣어주면 앞의 종속된 숫자에서 인자를 뺀 결과를 리턴한다. 즉 block.number.sub(1)는 현재 블록넘버에서 1을 뺀 결과를 리턴해주는 것.

첫 번째 시도

블록넘버가 지나기 전에 외부 컨트랙트에서 반복문 10번 돌려서 맞추면 되지 않을까?라는 생각에 바로 리믹스를 켰다.

디플로이까지 완료 후 gotcha() 함수를 실행시켰는데 revert가 났다. 아무래도 아래 코드 때문에 그런 것 같다. blockValue 값이 같은 상태에서 10번을 반복하는 것은 안 좋은 방법인 듯.

    if (lastHash == blockValue) {
      revert();
    }

두 번째 시도

똑같은 코드로 복사해서 필요한 로직만 남겼다. 이전에 디플로이된 CoinFlip 컨트랙트 주소를 가져와서 새로운 인스턴스를 만들고 같은 로직으로 나온 side를 flip 함수에 넣어주는 방식이다.


계속 gotcha() 함수를 호출하면 contract.consecutiveWins의 숫자가 올라간다. 시간이 좀 걸리긴 하지만 10번까지 완료하면 끝. 이번 예제를 통해서 랜덤소스를 block.number 또는 blockhash와 같이 모두가 접근 가능한 요소로 정하면 안된다는 것을 다시 한 번 깨달았다. 괜히 체인링크의 vrf를 사용하는 게 아니다. 모두가 접근 가능하다면 이렇게 로직을 베껴서 결과물을 도출할 수 있다. vrf 애용하자.

profile
Just BUIDL :)

0개의 댓글