Solidity随机数发生器原理与实现

在这个教程中,我们介绍随机数的基本概念以及实现原理, 学习如何在以太坊智能合约中实现自己的Solidity随机数发生器, 并提供完整的solidity随机数发生器实现源码。

不同开发语言的以太坊教程: Java | Php | Python | .Net / C# | Golang | Node.JS | Flutter / Dart

1、什么是随机数

想象一下你身边的随机噪音或者任何随机发生的事情,可以说检测到这些随机模式 的概率为0。这就是随机数的意思 —— 一个可以生成指定范围内的任意不确定 数字的模式。

当我这么说的时候,考虑一下1~100之间的某个数。由于不确定性,我 猜不出来你的脑子里现在想的是哪个数。

随机数是从某个特定分布中选中的数,通常也会要求这些数之间彼此独立, 这样在前后两个数之间就不会存在相关性。计算机生成的随机数有时会 称为伪随机数,这一术语中保留了随机,表示其不可预测性。当没有额外 的约定时,通常随机数采样自均匀分布。当然也可以使用其他的概率分布, 例如,Box-Muller变换可以将一对满足均匀分布的随机数转换为一个二维 正态分布。

2、什么是伪随机数

伪随机数也被称为确定性随机位。这些数是使用被称为伪随机数发生器/PRNG 的计算机算法生成的,它们是确定性的,因此并不是真的随机,这些伪随机数 在经过一定的序列之后可能会重复。

为了生成随机数,我们需要使用一个数来初始化算法,这个数被称为种子/SEED。 通常使用补齐的系统时间作为种子,为了确保这些伪随机数有足够的随机性, 我们会加一些盐/SALT糖/SUGAR—— 可以将它视为你的伪随机数的密码 —— 以避免哈希 碰撞,也就意味着会生成重复的数。

3、区块链上如何实现伪随机数发生器

从前面的描述中,我们知道计算机里的随机数并不是真的随机,并且至少需要 一个系统时间来初始化算法,如果你要用它来开发某些有很强的随机要求的应用, 例如博彩游戏、计算机仿真、密码学等,这就有可能发生哈希碰撞。

这就是为什么在区块链上基本上不可能生成随机数的原因,我们需要获取 区块时间或区块哈希之类的作为种子,由于早期第一代和第二代区块链的出块 速度很慢,那么发生哈希碰撞的机会非常大。

但是,第三代的区块链已经可以达到很快的出块速度了,比如0.5~1秒,这样 我们就可以利用区块时间戳或区块哈希来初始化伪随机数发生器。

3、Solidity随机数发生器

下面的Solidity合约代码展示了如何在以太坊区块链上实现一个Solidity随机数发生器。

你可以说这是一个真随机数发生器/TRNG,因为我们没有使用算法 来生成随机数,而只是将区块时间与种子、盐、糖组合后计算哈希。我们 选择的这三个值可以是相同的并且是确定性的,但是区块哈希是不确定性的。 下面的代码支持以太坊、波场或其他任何支持Solidity的第三代区块链:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
pragma solidity ^0.5.8;

/**
* @title True random number generator
* @notice This is a contract to generate a true random number on blockchai.
* Though true random number generators doesn't require seed. But, to simplify
* the functions i used seed and other terms used in PRNGs,
* seed should be enough to generate a random number, but to randomize the pattern
* even more i added two more functions with salt and sugar.
*/
contract PRNG {
/**
* @notice Generates a random number between 0 - 100
* @param seed The seed to generate different number if block.timestamp is same
* for two or more numbers.
*/
function importSeedFromThird(uint256 seed) public view returns (uint8) {
uint8 randomNumber = uint8(
uint256(keccak256(abi.encodePacked(block.timestamp, seed))) % 100
);
return randomNumber;
}

/**
* @notice Generates a random number between 0 - 100
* @param seed The seed to generate different number if block.timestamp is same
* for two or more numbers.
* @param salt The salt to randomize the pattern
*/
function importSeedFromThirdSalt(uint256 seed, uint256 salt)
public
view
returns (uint8)
{
uint8 randomNumber = uint8(uint256(keccak256(abi.encodePacked(block.timestamp, seed, salt))) % 100);
return randomNumber;
}

/**
* @notice Generates a random number between 0 - 100
* @param seed The seed to generate different number if block.timestamp is same
* for two or more numbers.
* @param salt The salt to randomize the pattern
* @param sugar The sugar same as salt but for more randomization
*/
function importSeedFromThirdSaltSugar(
uint256 seed,
uint256 salt,
uint256 sugar
) public view returns (uint8) {
uint8 randomNumber = uint8(uint256(keccak256(abi.encodePacked(block.timestamp, seed, salt, sugar))) % 100);
return randomNumber;
}
}

在上面的合约中,我们实现了三个函数,分别使用不同的参数生成Solidity随机数:

  • importSeedFromThird:只需要种子的Solidity随机数发生器
  • importSeedFromThirdSalt:需要种子、盐的Solidity随机数发生器
  • importSeedFromThirdSaltSugar:需要种子、盐、糖的Solidity随机数发生器

abi.encodePacked以不对齐的方式将数据打包。 eccak256哈希函数和sha3类似,只是采用不同的补齐模式,具体可以参考这里

因此,在上面的函数中,我们实际上将包含时间、种子、盐、糖的数据进行了编码 从而获得更强的随机性,并尽可能让随机数不可预测。 当然我们可以从上面函数实现中删除时间戳,但是这样的话,我们就 完全控制了随机数的发生,从而破坏了区块链的透明特性。


原文链接:Random Number Generation on Blockchain

汇智网翻译整理,转载请标明出处