Celo中的随机数

1. 引言

在无需可信第三方的情况下,实现不可预测的伪随机数的方案有:

  • VRF
  • VDF
  • Commit-reveal

在这里插入图片描述

当前,Celo项目采用的是简单的commit-reveal方案来随机选择Validator.
对于某特定Validator propose的第 n n n个区块,该Validator会在该区块内附加值 ( r n , s n ) (r_n,s_n) (rn,sn),使得 keccak256 ( r n ) = s n − 1 \text{keccak256}(r_n)=s_{n-1} keccak256(rn)=sn1。对于该Validator propose的第一个区块 n = 1 n=1 n=1,则初始随机值 r 1 = 0 r_1=0 r1=0

  /**
   * @notice Implements step of the randomness protocol.
   * @param randomness Bytes that will be added to the entropy pool.
   * @param newCommitment The hash of randomness that will be revealed in the future.
   * @param proposer Address of the block proposer.
   */
  function _revealAndCommit(bytes32 randomness, bytes32 newCommitment, address proposer) internal {
    require(newCommitment != computeCommitment(0), "cannot commit zero randomness");

    // ensure revealed randomness matches previous commitment
    if (commitments[proposer] != 0) {
      require(randomness != 0, "randomness cannot be zero if there is a previous commitment");
      bytes32 expectedCommitment = computeCommitment(randomness);
      require(
        expectedCommitment == commitments[proposer],
        "commitment didn't match the posted randomness"
      );
    } else {
      require(randomness == 0, "randomness should be zero if there is no previous commitment");
    }

    // add entropy
    uint256 blockNumber = block.number == 0 ? 0 : block.number.sub(1);
    addRandomness(block.number, keccak256(abi.encodePacked(history[blockNumber], randomness)));

    commitments[proposer] = newCommitment;
  }
  /**
   * @notice Compute the commitment hash for a given randomness value.
   * @param randomness The value for which the commitment hash is computed.
   * @return Commitment parameter.
   */
  function computeCommitment(bytes32 randomness) public pure returns (bytes32) {
    return keccak256(abi.encodePacked(randomness));
  }

在每一个区块会reveal该Validator之前区块所commit的随机值,reveal的随机值会存入一个entropy pool中。所有已reveal的历史随机值会通过keccak256拼接在一起。

    // add entropy
    uint256 blockNumber = block.number == 0 ? 0 : block.number.sub(1);
    // 所有已reveal的历史随机值会通过`keccak256`拼接在一起。
    addRandomness(block.number, keccak256(abi.encodePacked(history[blockNumber], randomness)));

mapping(uint256 => bytes32) private history;

  /**
   * @notice Add a value to the randomness history.
   * @param blockNumber Current block number.
   * @param randomness The new randomness added to history.
   * @dev The calls to this function should be made so that on the next call, blockNumber will
   * be the previous one, incremented by one.
   */
  function addRandomness(uint256 blockNumber, bytes32 randomness) internal {
    history[blockNumber] = randomness;
    if (blockNumber % getEpochSize() == 0) {
      if (lastEpochBlock < historyFirst) {
        delete history[lastEpochBlock];
      }
      lastEpochBlock = blockNumber;
    } else {
      if (historySize == 0) {
        historyFirst = blockNumber;
        historySize = 1;
      } else if (historySize > randomnessBlockRetentionWindow) { // 控制history map存储的数据量,节约空间。
        deleteHistoryIfNotLastEpochBlock(historyFirst);
        deleteHistoryIfNotLastEpochBlock(historyFirst.add(1));
        historyFirst = historyFirst.add(2);
        historySize = historySize.sub(1);
      } else if (historySize == randomnessBlockRetentionWindow) {
        deleteHistoryIfNotLastEpochBlock(historyFirst);
        historyFirst = historyFirst.add(1);
      } else {
        // historySize < randomnessBlockRetentionWindow
        historySize = historySize.add(1);
      }
    }
  }

获取特定区块对应的随机值:

  /**
   * @notice Get randomness values of previous blocks.
   * @param blockNumber The number of block whose randomness value we want to know.
   * @param cur Number of the current block.
   * @return The associated randomness value.
   */
  function _getBlockRandomness(uint256 blockNumber, uint256 cur) internal view returns (bytes32) {
    require(blockNumber <= cur, "Cannot query randomness of future blocks");
    require(
      blockNumber == lastEpochBlock ||
        (blockNumber > cur.sub(historySize) &&
          (randomnessBlockRetentionWindow >= cur ||
            blockNumber > cur.sub(randomnessBlockRetentionWindow))),
      "Cannot query randomness older than the stored history"
    );
    return history[blockNumber];
  }

参考资料

[1] Celo身份注册中所使用的随机数

Guess you like

Origin blog.csdn.net/mutourend/article/details/121163681