本文由iOlite赞助创作。感谢您支持使SitePoint成为可能的合作伙伴。
Solidity 是一门相对较新的语言,由于没有完美的代码,它包含与代码及其预期用途相关的问题。本文将指导您在使用随机数作为以太坊智能合约输入时的最佳实践和陷阱。
关键要点
block.timestamp
和 block.difficulty
,矿工可以操纵这些值。Solidity 随机数生成
Solidity 无法创建随机数。实际上,所有创建随机数的算法都是伪随机的——没有语言能够创建完全随机的数。Solidity 的问题在于复杂算法的成本太高,因此使用更基本的解决方案。除此之外,Solidity 代码应该是确定性的,因为它将在多个节点上运行。我们需要一种能够生成一次随机数并在多个节点上使用它的算法。诸如时钟时间之类的信息不可用于生成随机数,因此我们必须寻找其他选项。作为开发人员,您应该意识到这个问题,因为攻击者能够在某些特定情况下预测结果。
最常用的算法之一是“线性同余生成器”(LCG)。它是 oldest 算法之一,快速且易于理解。LCG 对于嵌入式系统来说是一个不错的选择,因为它们只有有限的内存。但是,它不适用于密码安全应用。尽管如此,它仍在智能合约中使用,因为快速算法在 gas 成本方面更便宜。
该算法本身执行以下步骤:
让我们探讨使用彩票智能合约示例创建随机数的不同方法。用户可以通过向合约发送 0.1 以太币以及 0 到 250 之间的整数来加入彩票。
block.timestamp
& block.difficulty
每当矿工确认交易时,都会分配一个 block.timestamp
。我们的彩票合约的任何玩家都无法控制它。让我们来看一下这段用于创建随机数的代码。
<code class="language-solidity">function random() private view returns (uint8) { return uint8(uint256(keccak256(block.timestamp, block.difficulty))%251); }</code>
此处查找 Gist。
这段代码首先对区块时间戳和难度进行哈希运算。接下来,我们将哈希值转换为整数并将其除以 251 以获得 0 到 250 之间的整数。但是,这段代码的问题在于我们不应该相信矿工来选择获胜者。
我们需要更多任意数据来选择我们的获胜者。我们可以使用已进入我们的彩票智能合约的玩家的地址,但我们必须将其隐藏起来,因为他们可能会滥用它。由于所有信息都记录在区块链上,因此无法隐藏此信息。
可以提交给我们的彩票智能合约的数字。用户必须将他们选择的数字与他们的以太坊地址一起进行哈希运算。这给了我们一个相当随机的数字。
话虽如此,随机性是可以实现的,但您只需要使用预言机从区块链外部获取随机数即可。使用外部数据的问题在于,证明该数字实际上是随机的非常困难,并确保链下实体不会以任何方式操纵随机数。这就是 Chainlink VRF 发挥作用的地方。Chainlink VRF(可验证随机函数)是我们如何在 Solidity 中获得可证明随机数的方法。
Chainlink VRF 向区块链添加一个事件,Chainlink 节点从中读取该事件,并返回一个随机数。通过所谓的 VRF 协调器进行链上随机性检查。这使用来自预言机的特定密钥哈希和来自用户的种子短语以及一些密码学来确保数字是真正的随机数。这样,我们可以得到一个无偏见的随机数。
开发人员需要考虑何时选择获胜者。诸如时钟时间之类的信息在以太坊虚拟机中不可用,因为代码将在多个节点上以不同的时间运行。这使得选择获胜者更加困难。一种方法是在您的智能合约中实现一个函数,该函数将关闭彩票并选择获胜者。这不像我们希望的那样去中心化。合约的所有者可以在确定他们的朋友会获胜时关闭彩票。我们要避免这种作弊行为。
更好的选择是使用以太坊闹钟。它是一项允许调度交易在以太坊区块链上的稍后时间执行的服务。这项服务完全是无需信任的,这意味着整个服务都作为智能合约运行。基本上,以太坊闹钟使用区块编号来调度交易。注意,这并不意味着合约会自行启动。它依赖于用户对调用“选择获胜者”函数感兴趣(以太奖励)。当然,如果没有人调用您的函数,您的彩票将失败。
Random.org 提供一个 API,通过 JSON 提供随机数据源。以太坊智能合约可以使用此数据源来馈送选择随机数的算法。由于安全性很重要,可以使用数字签名。随机数据将由 Random.org 签名。您可以验证数据的完整性,以便您可以证明它确实来自 Random.org 并且数据未被篡改。
RANDAO 是区块链领域中的一个新项目,它完全专注于提供随机数。他们结合使用预言机和智能合约来为您提供随机数。但是,RANDAO 服务目前非常慢。如果您有一个经常使用的应用程序,这不是理想的选择。
您还可以在代码中使用监视器,该监视器检查区块编号,直到它与您设置的目标编号匹配。
<code class="language-solidity">function random() private view returns (uint8) { return uint8(uint256(keccak256(block.timestamp, block.difficulty))%251); }</code>
来源。Gist。
iOlite 正在创建一个接受自然语言来创建智能合约的产品。它使用斯坦福自然语言处理 (NLP) 引擎,称为快速适应引擎 (FAE)。iOlite 依赖于 Solidity 专家进行社区培训。Solidity 专家(贡献者)可以定义包含一个或多个句子的结构,并将其附加到相应的智能合约代码。
斯坦福 NLP 引擎旨在理解复杂的语言。语言复杂程度取决于机器训练量。经过适当的训练后,该引擎将能够创建复杂的智能合约。FAE 能够创建此类合约,因为复杂的合约实际上并不那么复杂。专家可以将请求拆分为多个较小的代码片段,并将其附加到一个句子。
当有人输入多个句子时,它将查找相应的结构/句子来构建“复杂”合约。贡献者将通过新结构的挖掘过程获得 iOlite 代币奖励。
使用 iOlite 的好处是智能合约专家可以为您解决诸如随机数生成之类的难题。您可以在 iOlite.io 找到更多信息。
结论
如您所见,生成真正的随机输入并非易事。不要依赖 block.timestamp
、now
和 block.blockhash
作为随机性来源。一个好的解决方案包括组合几个伪随机数据输入和使用预言机或智能合约以使其更可靠。您需要 100% 确定没有人可以篡改输入到智能合约中的数据。
请谨慎操作,并在实施随机数生成逻辑之前三思而后行。
Solidity 中随机数生成的常见问题解答 (FAQ)
Solidity(用于编写以太坊智能合约的编程语言)没有内置函数来生成随机数。这是因为区块链(以太坊的基础技术)本质上是确定性的。这意味着给定一组输入,输出将始终相同。这种确定性对于维护区块链的完整性和安全性至关重要。但是,它使生成真正的随机数成为一项挑战,因为随机性的概念本质上是非确定性的。
开发人员使用几种方法在 Solidity 中生成伪随机数。一种常用方法是使用 keccak256 哈希函数,其输入难以预测,例如当前区块时间戳和区块难度。另一种方法是使用预言机服务,该服务从链下源提供随机数。但是,每种方法都有其自身的局限性和潜在安全风险。
虽然 keccak256 哈希函数可用于生成伪随机数,但它存在一些潜在的安全风险。由于哈希函数的输入(例如当前区块时间戳和区块难度)在区块链上公开可用,因此恶意矿工可能会操纵这些值以影响随机数生成的输出。
预言机服务可以从链下源提供随机数。这些服务充当区块链和外部世界之间的桥梁,允许智能合约与区块链本身不可用的数据进行交互。但是,使用预言机服务会引入一定程度的信任,因为智能合约必须依赖预言机来提供准确且无偏见的随机数。
承诺-揭示方案是一种以去中心化和安全的方式生成随机数的方法。在承诺-揭示方案中,参与者首先承诺一个秘密数字,然后所有秘密数字同时被揭示,并根据这些秘密生成一个随机数。这种方法可以防止任何单个参与者能够影响随机数生成的输出。
真正的随机数对于许多类型的智能合约至关重要,例如用于机会游戏、彩票和其他需要随机性的应用程序的智能合约。如果这些合约中使用的随机数可以预测或影响,则可能导致不公平的结果,甚至允许恶意行为者利用该合约。
Solidity 中的 blockhash 函数可用于生成伪随机数。此函数返回给定区块编号的哈希值,该值是不可预测的,并且随每个区块而变化。但是,此方法有其局限性。例如,blockhash 函数仅适用于最近的 256 个区块,并且在挖掘之前无法知道未来区块的 blockhash。
使用当前区块时间戳作为生成随机数的输入有一个主要局限性。矿工对他们挖掘的区块的时间戳具有一定的影响力,这意味着他们可能会操纵时间戳以影响随机数生成的输出。
RANDAO(随机数 DAO)信标是一种去中心化且透明的生成随机数的方法。RANDAO 信标中的参与者承诺秘密数字,然后这些数字被揭示并组合起来生成一个随机数。这种方法旨在防止任何单个参与者能够影响随机数生成的输出。
正在进行关于改进 Solidity 和其他区块链平台中随机数生成的研究和提案。例如,以太坊 2.0(以太坊网络的即将到来的升级)预计将包括一个内置的随机数生成器。但是,在这些改进实施之前,开发人员必须继续使用现有方法及其固有的局限性和潜在安全风险。
以上是坚固性陷阱:以太坊的随机数生成的详细内容。更多信息请关注PHP中文网其他相关文章!