在以太坊的智能合约中,没有提供像其他面向对象编程一样的生成随机数的工具类或方法。其实,所谓的随机数也是伪随机的,没有哪一种语言能够真正的生成随机数。
对于solidity来说通过复杂的算法生成随机数成本太高,同时智能合约又运行在多个节点上,因此,不能使用像时钟时间来生成随机数。
我们在 Solidity 里如何生成随机数呢?真正的答案是你不能,或者最起码,你无法安全地做到这一点。因为区块链的全部内容对所有参与者来说是透明的, 这就让这个问题变得很难,但是我们有一些方案可以让随机数的使用变得更加安全。
在一笔交易中,这笔交易什么时候,被谁打包到区块中,对用户来说是不可知的,但是一旦被打包到区块中,这些值就是确定的了,因此我们可以利用区块的打包时间block.timestamp、区块的打包难度block.difficulty作为种子生成随机数。0-100随机数生成器代码如下:

在以太坊上, 当你在和一个合约上调用函数的时候, 你会把它广播给一个节点或者在网络上的 transaction 节点们。 网络上的节点将收集很多事务, 试着成为第一个解决计算密集型数学问题的人,作为“工作证明”,然后将“工作证明”(Proof of Work, PoW)和事务一起作为一个 block 发布在网络上。
一旦一个节点解决了一个PoW, 其他节点就会停止尝试解决这个 PoW, 并验证其他节点的事务列表是有效的,然后接受这个节点转而尝试解决下一个节点。
这就让我们的随机数函数变得可利用了
我们假设我们有一个硬币翻转合约——正面你赢双倍钱,反面你输掉所有的钱。假如它使用上面的方法来决定是正面还是反面 (random >= 50 算正面, random < 50 算反面)。
如果我正运行一个节点,我可以 只对我自己的节点 发布一个事务,且不分享它。 我可以运行硬币翻转方法来偷窥我的输赢 — 如果我输了,我就不把这个事务包含进我要解决的下一个区块中去。我可以一直运行这个方法,直到我赢得了硬币翻转并解决了下一个区块,然后获利。
当然, 因为网络上成千上万的以太坊节点都在竞争解决下一个区块,我能成功解决下一个区块的几率非常之低。 这将花费我们巨大的计算资源来开发这个获利方法 — 但是如果奖励异常地高(比如我可以在硬币翻转函数中赢得 1个亿), 那就很值得去攻击了。
所以尽管这个方法在以太坊上不安全,在实际中,除非我们的随机函数有一大笔钱在上面,你游戏的用户一般是没有足够的资源去攻击的。但是要谨记它是不安全的。
将业务数据加入到随机数生成器中,可以解决矿工利用随机数生成器攻击Dapp。这里以彩票合约为示例,用户Tjaden Hess(https://ethereum.stackexchange.com/users/131/tjaden-hess)在stackoverflow上对彩票合约提出过比较好的解决方案。其核心是使用玩家的地址和所选号码作为随机数生成器的种子。
彩票合约的逻辑是:
新一期彩票投注开启,玩家提交以太坊地址和投注号码计算的哈希,之所以提交hash是为了保障在计算随机数(中奖号码)之前,无法预知投注号码
按照区块数或者参与者达到上限,投注截止
投注玩家提交自己的投注号码,合约会根据之前玩家提交的hash值进行校验。此时玩家投注的号码已不可改变
组织者开奖,从投注号码中随机选择中奖号码(取随机数),并将奖金发放给中奖用户
通过对第一种生成的随机数作为数据源重复进行哈希运算,同样可以大大增大矿工的攻击成本,增强安全性。
重复哈希是将哈希函数的一次运行的输出用作下一次运行的输入,从而多次运行哈希函数的行为。如果初始输入值有稍微的变动,最终计算的结果也会有天壤之别。
链下方式生成随机数供链上使用,主要通过预言机 oracle来实现,而预言机又分为中心化预言机和去中心预言机。
使用中心化的方式生成随机数其首要前提是要保证随机数的可信性,这里推荐使用random,地址:https://www.random.org/
目前有很多oracle服务提供随机数,如
randao提供了commit reveral合约和BLS合约两种方式提供随机数,其白皮书详见:https://randao.org/whitepaper/Randao_v0.85.pdf
Niguez随机引擎,使用说明详见:https://github.com/niguezrandomityengine/guide/blob/master/guide.md
这种情况通常是中心化的解决方案,通过一个可信的 Oracle 来提供独立的随机数源。智能合约发送请求给独立于区块链系统之外的 Oracle,当 Oracle 监听到链上相关请求后,生成随机数并调用回调函数将结果返回区块链。
这个方案的主要问题式是中心化的解决方案,与基于区块链的分布式精神相悖,另外还有其他的弱点,比如信息在 p2p 网络中传递过程中的延时问题,对于不同等级的应用需求没法提供差异化的服务等。
例:Chainlink最近推出一款革命性的产品,VRF—Verifiable Random Function可验证随机数,给智能合约带来了真正安全的随机数。
首先,智能合约应用,也就是我们的Dapp,需要先发起一个获取随机数的请求,这个请求需要给定一个合约地址,这个合约称为VRFCoordinator合约。
与VRFCoordinator合约所关联的Chainlink链下节点,会(通过椭圆曲线数字签名算法)生成一个随机数,以及一个证明。
Chainlink节点将上面生成的随机数和证明发送到VRFCoordinator合约中。
VRFCoordinator合约收到随机数和证明后,会对通过证明来验证所生成随机数的合法性。
随机数验证成功后,会将随机数发送回用户的智能合约应用
整个过程中有两次的交易提交确认的过程,用户合约需要支付LINK给VRF合约作为交易费用。


