在以太坊中,我们如何生成多个随机数?

9

当我调用这个智能合约时,希望它返回7或8个独一无二的随机数,范围在1到100之间。获取这样的结果最好的方法是什么?


取决于您想要用它做什么,但在区块链上进行随机化是很困难的,建议使用预言机。 - Raghav Sood
Oracle是用来干什么的?我的智能合约只需要返回随机数,没有其他要求。我该如何实现这个功能? - Joe Mutti
4个回答

12
如果你正在尝试使用以太坊区块链构建轮盘、彩票和纸牌游戏,由于以太坊区块链是确定性的,因此对于那些选择编写自己的伪随机数生成器(PRNG)的人来说,它会带来一定的困难。
如果你使用像block.coinbase、block.difficulty、block.timestamp等块变量作为熵源,所有这些块变量都可以被矿工操纵,因此由于矿工的激励,它们不能用作熵源。由于块变量在同一个块内明显共享,你可以轻松使用内部消息产生相同的结果。
其他方法包括使用当前或某个过去块的blockhash或将过去块的blockhash与私有种子结合使用。在这些情况下,使用block.blockhash(block.number)函数。然而,在EVM中执行交易时,由于明显的原因,正在创建的块的blockhash尚未知道,因此EVM将始终产生零。如果我们尝试使用先前块的blockhash,则攻击者可以创建一个具有相同代码的利用合约,以通过内部消息调用目标合约。两个合约的“随机”数字将相同。
即使我们将blockhash与私有种子结合使用,由于其透明的性质,区块链不应用于以明文形式存储机密。从合约存储中提取私有变量指针的值并将其作为利用的参数是微不足道的。
一些值得探索的领域包括外部预言机、Signidice、提交-揭示方法。

使用像Oraclize这样的外部预言机,智能合约可以从Web API(如货币汇率、天气预报和股票价格(如random.org))请求数据。这种方法的主要缺点是它是集中化的。Oraclize守护程序会篡改结果吗?我们可以相信random.org吗?

除了Oraclize之外,我们还可以使用BTCRelay,它是以太坊和比特币区块链之间的桥梁。使用BTCRelay,以太坊区块链中的智能合约可以请求未来的比特币块哈希并将其用作熵源。

Signidice是一种基于加密签名的算法,可用于涉及两个方​​​​​:玩家和庄家的智能合约中的随机数生成。该算法的工作原理如下:

  • 玩家通过调用智能合约下注。
  • 庄家看到下注情况,用其私钥对其进行签名,并将签名发送给智能合约。
  • 智能合约使用已知的公钥验证签名。
  • 然后使用此签名生成随机数。

Commit-reveal方法由两个阶段组成:

  • “提交”阶段,在此阶段,各方向智能合约提交其受密码保护的秘密。
  • “揭示”阶段,在此阶段,各方宣布明文种子,智能合约验证它们是否正确,并使用这些种子生成随机数。
更好的实现提交-揭示方法是Randao。提交-揭示可以与未来块哈希结合使用,使其更安全。
这基本上涵盖了使用以太坊进行随机数生成的所有方法。

4

正如Raghav所说,区块链上的随机数很困难。网络的公共性质使得生成一个不可预测的数字非常困难。

话虽如此,最佳解决方案之一是使用一个从外部(即:非基于区块链的)源获取随机数的oracle。请查看这个指南。 Ethtroll Dapp是这种方式的一个很好的例子,可以在这里查看代码。他们使用Oraclize从Random.org获取随机数。

使用oracle的问题是中心化因素。如果你按照我上面描述的方法设置Dapp,你就受制于两个不同中心化服务的不良职员——Oraclize和Random.org。尽管有人为了经济利益会做出非理性的行为,但操纵这些源的可能性较小。


4

使用Chainlink VRF进行操作。

使用区块哈希或类似方法作为随机种子存在一些问题。如果攻击者在您的合约之前就知道了区块哈希,他们可以利用这个信息在您试图处理的任何事情上获得恶意优势。此时,预言机可以提供帮助,但它们是中心化的易于出错,并且必须能够证明它们是随机的。

您需要拥有一个预言机网络,该网络可以:

  1. 证明生成的数字是随机的。
  2. 具有足够多的预言机/节点,即使一个失效/损坏了,您的智能合约也会持续存在。

当前示例演示如何解决第1点要求。您可以通过从支持Chainlink VRF的足够数量的节点提取来满足第2点要求。

有关精确实现,请参阅类似问题的回答

您需要向一个具有由您生成的种子的函数的节点发出请求:

 function rollDice(uint256 userProvidedSeed) public returns (bytes32 requestId) {
        require(LINK.balanceOf(address(this)) > fee, "Not enough LINK - fill contract with faucet");
        uint256 seed = uint256(keccak256(abi.encode(userProvidedSeed, blockhash(block.number)))); // Hash user seed and blockhash
        bytes32 _requestId = requestRandomness(keyHash, fee, seed);
        emit RequestRandomness(_requestId, keyHash, seed);
        return _requestId;
    }

当函数返回值后,您需要对其进行取余100并加1。如果您需要7或8个随机数,则需要调用此函数7或8次。

function fulfillRandomness(bytes32 requestId, uint256 randomness) external override {
        uint256 d6Result = randomness.mod(100).add(1);
        emit RequestRandomnessFulfilled(requestId, randomness);
    }


1
我有一个头脑风暴的想法,也许能帮助某些人。 这是一种简化的提交-揭示方法,只有一个参与者。 每个随机生成都需要一个标题。该标题应为标准且易于审核。 首先,我在智能合约上提交(“Alice's Lottery”)。如果标题重复(检查哈希值),则将被拒绝。 对于揭示,将需要等待至少1个额外的块确认,这2个块应来自不同的矿工,以确保矿工不会攻击此智能合约。 然后您执行Reveal(“Alberto's Lottery”)。 魔法发生在这里; 随机源将是标题,msg.sender,提交块的block.blockhash和block.blockhash(commitBlockNumber + 1),因为没有人可以预测未来的哈希值或哪个矿工会发现它[您还可以添加coinbase或时间戳以获取更多的随机值]。 还可以检查commitBlockNumber和commitBlockNumber + 1的时间戳是否太接近或太分散,这可能表明某些矿工正在尝试强制某个块,因此您可以拒绝此彩票。
当然,如果你能够密切关注像("Alice's Lottery") || ("AAlice's Lottery")这样的提交,你就可以探测到这个彩票正在被欺骗。此外,你还可以使用超过2个“间隔”块来实现这一点。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接