Solidity生成随机数及其安全性

一、概述

使用Solidity编写合约的时候,难免需要随机数,本文记录一种简单的方法,同时也看到此方法存在安全方面的问题。

二、随机数简单生成法

使用keccak256哈希函数。

// 生成一个0到100的随机数:
uint randNonce = 0;
uint random = uint(keccak256(now, msg.sender, randNonce)) % 100;
randNonce++;
uint random2 = uint(keccak256(now, msg.sender, randNonce)) % 100;

这个方法首先拿到 now 的时间戳、 msg.sender、 以及一个自增数 nonce (一个仅会被使用一次的数,这样我们就不会对相同的输入值调用一次以上哈希函数了)。
然后利用 keccak 把输入的值转变为一个哈希值, 再将哈希值转换为 uint, 然后利用 % 100 来取最后两位, 就生成了一个0到100之间随机数了。

三、安全性讨论

这个方法很容易被不诚实的节点攻击。

在以太坊上, 当你在和一个合约上调用函数的时候, 你会把它广播给一个节点或者在网络上的 transaction 节点们。 网络上的节点将收集很多事务, 试着成为第一个解决计算密集型数学问题的人,作为“工作证明”,然后将“工作证明”(Proof of Work, PoW)和事务一起作为一个 block 发布在网络上。
一旦一个节点解决了一个PoW, 其他节点就会停止尝试解决这个 PoW, 并验证其他节点的事务列表是有效的,然后接受这个节点转而尝试解决下一个节点。

这就让我们的随机数函数变得可利用了。

我们假设我们有一个硬币翻转合约——正面你赢双倍钱,反面你输掉所有的钱。假如它使用上面的方法来决定是正面还是反面 (random >= 50 算正面, random < 50 算反面)。
如果我正运行一个节点,我可以 只对我自己的节点 发布一个事务,且不分享它。 我可以运行硬币翻转方法来偷窥我的输赢 — 如果我输了,我就不把这个事务包含进我要解决的下一个区块中去。我可以一直运行这个方法,直到我赢得了硬币翻转并解决了下一个区块,然后获利。

四、安全地生成随机数

(1)利用 oracle 来访问以太坊区块链之外的随机数函数
(2)参见https://ethereum.stackexchange.com/questions/191/how-can-i-securely-generate-a-random-number-in-my-smart-contract

五、Ref

(1)https://cryptozombies.io/zh/lesson/4/chapter/4
(2)https://ethereum.stackexchange.com/questions/191/how-can-i-securely-generate-a-random-number-in-my-smart-contract

你可能感兴趣的:(BlockChain)