axiomzen/eth-random

Add an example to that implements the schema

flockonus opened this issue · 3 comments

Should make use of the described technique to implement what the README describes

Hello! Here is my first attempt to implement the technique described in the README.
Please note that this is a first draft and many things and verifications are missing.

pragma solidity 0.4.24;


/**
 * @title First attempt to implement a secure RNG, as described here https://github.com/axiomzen/eth-random
 * @author clemlak https://github.com/clemlak
 * @dev This contract is WIP, please don't use it in production!
 */
contract RNG {
  /**
   * @dev Given a number returns a slice of any bits, at certain offset
   * @author flockonus (https://gist.github.com/flockonus/cb75838d78cf544744e7ac95ab3ec431)
   * @param number A number to be sliced
   * @param nbits How many bits long is the new number
   * @param offset How many bits to skip
   * @return A slice of the number, as an uint
   */
  function sliceNumber(uint256 number, uint256 nbits, uint256 offset) public pure returns (uint256) {
    /* Mask is made by shifting left an offset number of times */
    uint256 mask = uint256((2 ** nbits) - 1) << offset;

    /* AND number with mask, and trim to max of nbits bits */
    return uint256((number & mask) >> offset);
  }

  /**
   * @dev Returns an array with random numbers, as described here https://github.com/axiomzen/eth-random
   * @param count How many random numbers will be returned
   * @param power How big the numbers will be (in bits)
   * @return An array with random numbers
   */
  function getRandomNumbers(uint256 count, uint256 power) public view returns (uint256[]) {
    /* We start from the block number and its hash */
    uint256 ddb = uint256(blockhash(block.number - 1));

    uint256 r = uint256(keccak256(abi.encodePacked(ddb - 1)));

    /* If r equals 0, we try with an higher block number */
    while (r == 0) {
      ddb += 256;

      r = uint256(keccak256(abi.encodePacked(ddb - 1)));
    }

    /* We create an array to store our random numbers */
    uint256[] memory numbers = new uint256[](count);

    for (uint256 i = 0; i < count; i += 1) {
      numbers[i] = sliceNumber(r, power, i);
    }

    return numbers;
  }
}

@clemlak the outline for the reveal looks good. There might require some tests around the r == 0 case, but should be little variation required.

numbers[i] = sliceNumber(r, power, i); should probably be numbers[i] = sliceNumber(r, power, i * power); since the offset is in bits

Now one important step missing is a commit function. At a previous time, an internal storage variable must be set for the DDB (> current block number), which then getRandomNumbers can be used for. Otherwise, an attacker could just get the random numbers once it observes a favorable current blockhash.

Thanks for the prompt draft

I've an alternative solution with pure bytecode approach:
https://github.com/chiro-hiro/thedivine