CTF/Ethernaut

[Ethernaut] 3. Coin Flip

0xperiagoge 2024. 1. 25. 23:53

Ethernaut levevl 3의 요구사항

동전뒤집기해서 뭐가 나올지 생각해서 연승하는 동전뒤집기 게임이다. 연속으로 10번 맞춰야 한다. 

"Beyond the console"섹션 보라고한다.

리믹스 사용하라고 한다. 

 

코드를 쭉~ 보면 

uint256 coinFlip = blockValue / FACTOR;
bool side = coinFlip == 1 ? true : false;

이 부분이 있다. 

blockValue / FACTOR 한 값이 1이면 true.

FACTOR의 값은 정해져 있고, block.number에 따라서 blockValue가 바뀌면서 그렇게 나눈 것이 1이 되면 되는 것인데 

 uint256 FACTOR = 57896044618658097711785492504343953926634992332820282019728792003956564819968;
 

FACTOR가 엄청 큰 숫자로 되어있는데 저것보다 컸을 때 나누기를 하면 솔리디티에서 소수점은 날아가기 때문에 1이 되는 것이다. 

그렇기 때문에 blockValue의 값을 보다가 FACTOR보다 클 때 트랜잭션을 실행시키면 된다. 

그러면 문제로 나온 컨트랙트에 접근해서 flip을 실행하는 컨트랙트를 작성해서 보다가 조건이 충족했을 때만 실행해 주면 된다. 

 

그리고 무조건 true 일 때만 실행해야 하는 게 아니라. 내가 입력한 true, false값과 동일하면 된다.

생각해 보니까 외부컨트랙트에서 실행할 필요도 없이 값만 체크해서 문제로 나온 컨트랙트에 입력해서 트랜잭션 날리면 되는 것이었다.

  function flip() public view returns (uint256) {
    uint256 blockValue = uint256(blockhash(block.number - 1));
    return  blockValue / FACTOR;
  }

요런 함수 하나 만들어서 1이면 true 넣고 실행하고 0이면 false 넣고 실행

 

++

조금 더 생각해 보니 다른 방법 

  function flip() public returns (bool) {
    uint256 blockValue = uint256(blockhash(block.number - 1));
    uint256 coinFlip33 = blockValue / FACTOR;
    bool side = coinFlip33 == 1 ? true : false;
    bool result = ICoinFlip(coinFlip).flip(side);
    return result;
  }

컨트랙트 하나 만들고 side로 나오는 bool값은 바로 coinFlip에 넣어서 실행하는 코드.

그러면 문제랑 똑같기 때문에 그냥 계속하면 된다. 

 

 

 

level 3. Coin Flip 코드 

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

contract CoinFlip {

  uint256 public consecutiveWins;
  uint256 lastHash;
  uint256 FACTOR = 57896044618658097711785492504343953926634992332820282019728792003956564819968;

  constructor() {
    consecutiveWins = 0;
  }

  function flip(bool _guess) public returns (bool) {
    uint256 blockValue = uint256(blockhash(block.number - 1));

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

    lastHash = blockValue;
    uint256 coinFlip = blockValue / FACTOR;
    bool side = coinFlip == 1 ? true : false;

    if (side == _guess) {
      consecutiveWins++;
      return true;
    } else {
      consecutiveWins = 0;
      return false;
    }
  }
}