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

Posted

技术标签:

【中文标题】我们如何在以太坊中生成多个随机数?【英文标题】:How can we generate multiple random number in ethereum? 【发布时间】:2019-02-27 06:02:53 【问题描述】:

我希望我的智能合约在调用合约时返回 7 或 8 个 UNIQUE 随机数,范围从 1 到 100。获得这种结果的最佳方法是什么?

【问题讨论】:

取决于你想用它做什么,但区块链上的随机化很难,使用预言机。 Oracle 是为了什么?我的智能合约工作只是返回随机数,没有别的。我怎样才能做到这一点? 【参考方案1】:

就像 Raghav 所说,区块链上的随机数很难。网络的公共性质使得生成无法预先计算的数字变得非常困难。

话虽如此,最好的解决方案之一是使用从外部(阅读:非基于区块链)来源获取随机数的预言机。看看this 指南。 Ethtroll Dapp 就是一个很好的例子,所以看看代码here。他们使用 Oraclize 从 Random.org 获取随机数。

使用预言机的一个问题是集中化因素。如果您按照我上面描述的方式设置您的 Dapp,那么您将受制于两个不同的中心化服务(Oraclize 和 Random.org)的一名流氓员工。尽管有人不太可能操纵这些来源中的任何一个,但人们会为了潜在的经济利益而做出非理性的行为。

【讨论】:

【参考方案2】:

如果您尝试使用以太坊区块链构建轮盘赌、彩票和纸牌游戏,由于以太坊区块链是确定性的,它会给那些选择编写自己的伪随机数生成器 (PRNG) 的人带来一定的困难.

目前使用的一些易受攻击的方法

如果你使用block.coinbase、block.difficulty、block.timestamp等区块变量作为熵的来源,所有这些区块变量都可以被矿工操纵,所以它们不能作为熵的来源。由于矿工的激励而产生的熵。由于块变量显然在同一个块内共享,您可以轻松地使用内部消息来产生相同的结果。

其他方法就像使用当前或过去某个块的块哈希 或过去区块的区块哈希与私人种子相结合。在这些情况下使用 block.blockhash(block.number) 函数。然而,在 EVM 中执行交易的那一刻,由于显而易见的原因,正在创建的区块的区块哈希尚不清楚,并且 EVM 将始终产生零。如果我们使用前一个块的块哈希来尝试它,攻击者可以使用相同的代码制作漏洞利用合约,以便通过内部消息调用目标合约。两个合约的“随机”数字将是相同的。

即使我们将blockhash与私有种子结合起来,本质上是透明的,区块链不能用于以明文形式存储秘密。从合约存储中提取私有变量指针的值并提供它是微不足道的作为漏洞利用的论据。

一些值得探索的领域

外部神谕 意义 提交-显示方法

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

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

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

玩家通过调用智能合约进行投注。 房子看到赌注,用它的私钥签名,然后将签名发送给智能合约。 智能合约使用已知公钥验证签名。 然后使用此签名生成随机数。

提交-显示方法包括两个阶段:

“提交”阶段,当各方将其受加密保护的秘密提交给智能合约时。 “揭示”阶段,当各方公布明文种子时,智能合约验证它们是否正确,种子用于生成随机数。

提交-显示方法的更好实现是Randao。 Commit-reveal 可以与未来的区块哈希相结合,使其更加安全。

这几乎涵盖了使用以太坊生成随机数的所有方法。

【讨论】:

【参考方案3】:

使用Chainlink VRF。

使用块哈希或类似方法作为随机播种方法存在许多问题。如果攻击者在你的合约之前知道区块哈希,他们可以使用该信息在你试图做的任何事情上获得恶意优势。预言机可以在这里提供帮助,但它们是失败的主要来源,必须能够证明它们是随机的。

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

    证明生成的数字是随机的。 拥有足够的预言机/节点,即使其中一个失败/损坏,您的智能合约也会持续存在。

此时,下面的例子展示了如何解决#1。您可以通过从足够数量的支持 Chainlink VRF 的节点中提取来解决 #2。

如需准确实现,see this answer from a similar question.

您需要使用一个函数向一个节点发出请求,该函数接收您生成的种子:

 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);
    

【讨论】:

【参考方案4】:

我有一个集思广益的想法,也许对某人有帮助。

这是一种简化的 Commit-reveal 方法,只有一个参与者。 每个随机生成都需要一个标题。 这个标题应该是标准的,并且易于审核

首先我在智能合约上提交("Alice's Lotery")。如果标题重复(检查哈希),它将被拒绝。 并且为了显示需要等待至少 1 个额外的块确认,这 2 个块应该来自不同的矿工,以确保矿工不会攻击这个智能合约。

然后你执行 Reveal("Alberto's Lottery")。 魔法在这里发生; random 的来源将是提交块的标题、msg.sender、block.blockhash 和 block.blockhash(commitBlockNumber+1),因为没有人可以预测未来的哈希,也没有哪个矿工会发现它[你可以添加 coinbase 或时间戳也可以获得更多的随机值]。 您还可以检查 commitBlockNumber 和 commitBlockNumber+1 的时间戳是否太接近或太分开,这可能表明某些矿工正在尝试强制某个区块,因此您可以拒绝此彩票。

当然,如果您可以通过 ("Alice's Lottery") || 之类的提交观看太多关闭的 tx (“AAlice's Lottery”)你可以探测到这个彩票被骗了。 你也可以用超过 2 个“间隔”块来做到这一点

【讨论】:

以上是关于我们如何在以太坊中生成多个随机数?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 PHP 中生成多个数组中的所有项目组合

如何在 Swift 中生成随机数?

如何搭建以太坊私有链

如何搭建以太坊私有链

如何搭建以太坊私有链

如何在 iPhone 的 xCode 中生成 3 位随机数