在以太坊生成随机数的几种方式(含代码)

一、什么是随机数

随机数都是由随机数生成器(Random Number Generator)生成的。随机数分为”真随机数“和”伪随机数“两种。

1、 真随机数

真正的随机数是使用物理现象产生的:比如掷钱币、骰子、转轮、使用电子元件的噪音、核裂变等等,这样的随机数发生器叫做物理性随机数发生器,它们的缺点是技术要求比较高。 ----百度百科

根据百科上的定义可以看到,真随机数是依赖于物理随机数生成器的。使用较多的就是电子元件中的噪音等较为高级、复杂的物理过程来生成。

2、伪随机数

真正意义上的随机数(或者随机事件)在某次产生过程中是按照实验过程中表现的分布概率随机产生的,其结果是不可预测的,是不可见的。而计算机中的随机函数是按照一定算法模拟产生的,其结果是确定的,是可见的。我们可以这样认为这个可预见的结果其出现的概率是100%。所以用计算机随机函数所产生的“随机数”并不随机,是伪随机数。---百度百科

从定义我们可以了解到,伪随机数其实是有规律的。只不过这个规律周期比较长,但还是可以预测的。主要原因就是伪随机数是计算机使用算法模拟出来的,这个过程并不涉及到物理过程,所以自然不可能具有真随机数的特性。

二、以太坊上的随机数

1、为什么没有random方法?

以太坊作为区块链,是一种确定性的图灵机,所有分布式节点需要对链上状态改变达成共识,就需要交易在所有节点上的计算结果都是一样的。这意味着以太坊不能涉及随机性。如果存在随机的操作码,则所有矿工将获得不同的结果,网络将无法达成共识。

2、两种来源

以太坊上没有random方法,但并不代表在以太坊上对随机数没有需求。在一些业务场景下,特别是菠菜类Dapp,对随机数是有强需求的。

例如在彩票的场景下,现实生活中,彩票开奖是由彩票中心使用彩票机开奖的(看起来是随机生成的号码,但确一直被人怀疑)。在区块链上,我们需要中奖的彩票号是随机产生的,从而保证游戏的公平性和可信力。

在以太坊上,所使用的随机数主要有两种来源,一种是通过链上生成,一种是通过链下生成。

三、链上生成随机数

链上生成随机数的核心是在交易被打包到区块之前尽可能的选取不可预测的种子(数)来生成随机数

接下来介绍的几种方法,其区别也是随机数生成种子的可预测性不同,越不可预测,其安全性也就越高。

1、不怎么安全的随机数

在一笔交易中,这笔交易什么时候,被谁打包到区块中,对用户来说是不可知的,但是一旦被打包到区块中,这些值就是确定的了,因此我们可以利用区块的打包时间block.timestamp、区块的打包难度block.difficulty作为种子生成随机数。0-100随机数生成器代码如下:

<pre style="margin: 0px; tab-size: 4; white-space: pre-wrap;">function importSeedFromThird() public view returns (uint8) {
return uint8(
uint256(keccak256(abi.encodePacked(block.timestamp, block.difficulty))) % 100
);
}</pre>

其中:

  • abi.encodePacked 紧密打包数据的 bytes 而没有任何填充,因为如果没有填充,则无法从此函数中提取数据。函数返回 bytes 类型,可以转化为 uint256 类型。
  • keccak256 是和 sha3 类似的 hash 函数,只是采用不同的补齐模式。

虽然block.timestamp和block.difficulty对普通用户来说无法预测,但是对矿工来说,却是可以操控的。有足够的利益驱动,矿工可以持续对区块进行挖矿打包,直到计算出对自己有利的随机数,进而打包区块。

针对这种情况,我们需要加强我们的随机数生成器,可以通过引起业务数据来加强。

2、利用重复哈希加强安全性

通过对第一种生成的随机数作为数据源重复进行哈希运算,同样可以大大增大矿工的攻击成本,增强安全性。

重复哈希是将哈希函数的一次运行的输出用作下一次运行的输入,从而多次运行哈希函数的行为。如果初始输入值有稍微的变动,最终计算的结果也会有天壤之别。


d7414827c4a908520f38df75f02db83b.png

3、利用业务逻辑生成相对安全的随机数

将业务数据加入到随机数生成器中,可以解决矿工利用随机数生成器攻击Dapp。
这里以彩票合约为示例,用户Tjaden Hess(https://ethereum.stackexchange.com/users/131/tjaden-hess)在stackoverflow上对彩票合约提出过比较好的解决方案。其核心是使用玩家的地址和所选号码作为随机数生成器的种子。

彩票合约的逻辑是:

  • 新一期彩票投注开启,玩家提交以太坊地址和投注号码计算的哈希,之所以提交hash是为了保障在计算随机数(中奖号码)之前,无法预知投注号码
  • 按照区块数或者参与者达到上限,投注截止
  • 投注玩家提交自己的投注号码,合约会根据之前玩家提交的hash值进行校验。此时玩家投注的号码已不可改变
  • 组织者开奖,从投注号码中随机选择中奖号码(取随机数),并将奖金发放给中奖用户

彩票合约代码如下:

//THIS CONTRACT IS CONSUMING A LOT OF GAS

//THIS CONTRACT IS ONLY FOR DEMONSTRATING HOW RANDOM NUMBER CAN BE GENERATED

//DO NOT USE THIS FOR PRODUCTION

pragma solidity ^0.4.8;

contract Lottery {

    mapping (uint8 =&gt; address[]) playersByNumber ;
    mapping (address =&gt; bytes32) playersHash;

    uint8[] public numbers;

    address owner;

    function Lottery() public {
        owner = msg.sender;
        state = LotteryState.FirstRound;
    }
    
    enum LotteryState { FirstRound, SecondRound, Finished }    

    LotteryState state; 

    function enterHash(bytes32 x) public payable {
        require(state == LotteryState.FirstRound);
        require(msg.value &gt; .001 ether);
        playersHash[msg.sender] = x;
    }

    function runSecondRound() public {
        require(msg.sender == owner);
        require(state == LotteryState.FirstRound);
        state = LotteryState.SecondRound;
    }

    
    function enterNumber(uint8 number) public {
        require(number&lt;=250);
        require(state == LotteryState.SecondRound);
        require(keccak256(number, msg.sender) == playersHash[msg.sender]);
        playersByNumber[number].push(msg.sender);
        numbers.push(number);
    }

    function determineWinner() public {
        require(msg.sender == owner);
        state = LotteryState.Finished;        
        uint8 winningNumber = random();        
        distributeFunds(winningNumber);
        selfdestruct(owner);
    }

    function distributeFunds(uint8 winningNumber) private returns(uint256) {
        uint256 winnerCount = playersByNumber[winningNumber].length;
                require(winnerCount == 1);
        if (winnerCount &gt; 0) {
            uint256 balanceToDistribute = this.balance/(2*winnerCount);
            for (uint i = 0; i&lt;winnerCount; i++) {
                require(i==0);
                playersByNumber[winningNumber][i].transfer(balanceToDistribute);
            }

        }        
        return this.balance;
    }

    function random() private view returns (uint8) {
        uint8 randomNumber = numbers[0];
        for (uint8 i = 1; i &lt; numbers.length; ++i) {
            randomNumber ^= numbers[I];
        }
        return randomNumber;

    }
}

彩票合约代码来源于:https://gist.github.com/promentol/d94959bfaf10f6b64d3cbf9c293de468

四、链下生成随机数

链下方式生成随机数供链上使用,主要通过预言机 oracle来实现,而预言机又分为中心化预言机和去中心预言机。

1、中心化

使用中心化的方式生成随机数其首要前提是要保证随机数的可信性,这里推荐使用random,地址:https://www.random.org/

2、去中心化

目前有很多oracle服务提供随机数,如

五、总结

对于以太坊合约中使用随机数,永远没有最安全的方式,只有最适合业务场景的方式。

如果业务数据本身具有随机性,可选择利用业务数据作为随机数生成器的种子;

如果业务场景(合约)不涉及利益或者利益驱动比较小的情况下,使用区块变量+重复hash的方式完全可以满足需求;

在一些安全性要求非常高的场景下,可以选择预言机提供随机数服务,但会牺牲请求效率。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,634评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,951评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,427评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,770评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,835评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,799评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,768评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,544评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,979评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,271评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,427评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,121评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,756评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,375评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,579评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,410评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,315评论 2 352