EOS上的各种菠菜游戏频频被黑客攻陷,多数原因都是随机数算法被破解。实际上,由于区块链上没有稳定的熵源,很难产生真正意义上的随机数,但是可以最大限度地提高破解的难度。在不引入于预言机(oracle)等外部熵源的情况下,目前有两种可用的安全随机数生成算法,本文将逐一进行分析。
1.FarmEOS算法
FarmOS是目前比较火的游戏平台,已经上线了骰子、轮盘、扫雷和21点这四个游戏,后续还将推出更多游戏。根据白皮书上的描述,FarmOS的随机数生成算法采用了未来的区块信息,再结合多项未来不可控变量因素,从而保证安全性和公平性:
今年10月16日,FarmOS公开了它的随机数算法(实际上也是该项目开源的唯一代码):https://github.com/farmeos/farmeos。需要注意的是,实际上该算法还是有漏洞的,需要去除掉红框框出来的因子(EOSBet项目采用了相同的算法,黑客正是利用这个漏洞成功获利)。
我们先来分析一下随机数种子中的几个因子:
(1) tapos_block_prefix() :该交易refer的区块高度的低32位
(2) tapos_block_num():该交易refer它之前的哪个区块,是一个0~2^16之间的数字
(3) name:用户账户名
(4) game_id:一个每次自增1的值
(5) current_time():开奖区块的时间戳
(6) pool_eos.amount:当前合约中的EOS余额
从表面上看,这些因子都是用户不可控的,但实际上黑客利用对(1)/(2)和(6)的操控对EOSBet合约成功发起了两次攻击,我们分别分析一下。
1.1操纵交易refer的区块信息
首先科普一下什么叫TAPOS:TAPOS即Transaction As Proof Of Stake,是EOS的特点之一,每个交易的头部都必须指定一个refer的区块,我们看一下transaction_header的定义:
这样做的目的主要是为了阻止矿工(validator)把同一笔交易打包到不同的分叉链上,从而减小系统的分叉风险。tapos_block_num()就是获取其中的ref_block_num值,范围是0~2^16之间。tapos_block_prefix()则是根据这个偏移量,获取对应区块高度的低32位,参见代码:
从这里看,似乎是没问题的,因为黑客无法提前知道开奖的那笔交易会refer哪个区块。但是,由于合约开奖时采用的是deferred action,这种交易会refer当前区块的前一个区块,这是在代码里写死的:
或者画个图可能更清楚一些:
可以看到,开奖交易refer的是下注交易的前一个区块!也就是说,下注的人是可以提前知道这两个因子的,只需要调整好下注的金额,就有很大的概率能够中奖了。
有人会问:为什么不是用inline action直接开奖,而要用deferred action呢?这也是跟EOS的特性相关的:如果适用inline aciton,在执行的过程中不管出现任何错误,都会导致整个交易的回滚。而deferred action则是一个单独的交易,即使执行失败也不会影响之前的交易,因此开发者一般都会采用deferred action,这样即使开奖失败,也不会导致之前的下注交易被回滚。
那么怎么解决这个问题呢?其实也很简单,再向后延迟一个区块不就行了?参见下图:
这就是所谓的“二次延时开奖”,这样开奖交易refer的就是下注交易所在的区块了,下注者无法提前知晓当前区块的信息。你以为这样就完全解决问题了?Naive!我们来看看下面一种攻击方式。
1.2操纵当前合约中的EOS余额
首先介绍一下eosio.token的一个特性:在发生转账时,合约会先给发送方发通知,然后再给接收方发通知:
这样,下注账户就可以先于游戏合约获取通知,然后模拟出和游戏合约相同的deferred action。因此,下注人的二次延时交易将会和游戏合约的“二次延时开奖”交易位于同一区块,并且先于开奖交易执行。至此,下注人就获得了开奖交易refer的区块信息,可以计算出中奖需要的pool_eos.amount,然后往游戏合约中存入相应数量的EOS即可中奖。具体流程参见下图:
防御办法也很简单:从随机数种子中去除“合约当前余额”因子,也就是最前面我们提到的红框框出来的部分。
1.3总结
前面分析了那么多,总结一下:目前阶段必须采用“二次延迟开奖 + 用户不可控因子”的方式,才能保证安全生成随机数。当然,这一方法是否绝对安全,还需要经历更多时间的考验。
2.BM算法
这一算法源于BM在StackExchange上的一条回复,我们暂且就把它称为“BM算法”吧。原文如下:
这条回复写得太简略了,我们以一个骰子游戏为例,大致可以分解为以下几个步骤:
(1) 玩家(Player)开始新游戏
(2) 游戏后台(Casino)生成一个随机数种子casino_seed
(3) 游戏后台将hash(casino_seed)上传到合约中,这样庄家就无法篡改开奖点数了
(4) 玩家生成自己的随机数种子player_seed,也就是下注的点数
(5) 玩家将hash(player_seed)上传到合约中,这样玩家也无法篡改下注点数了
(6) 玩家将player_seed上传到合约中
(7) 合约验证该player_seed是否和之前上传的哈希值匹配
(8) 游戏后台将casino_seed上传到合约中
(9) 合约验证该casino_seed是否和之前上传的哈希值匹配
(10) 如果所有验证都通过,为中奖者开奖
可以看出,在这种流程设计下,项目方和游戏玩家双方都无法作弊,开奖流程完全由智能合约控制,符合区块链公开透明的去中心化精神。
参考:
https://github.com/farmeos/farmeos/blob/master/random.cpp
https://www.farmeos.io/assets/pdf/FarmEOS_PoB%26DoP白皮书1.0.5.pdf
https://github.com/EOSIO/Documentation/blob/master/TechnicalWhitePaper.md#transaction-as-proof-of-stake-tapos
https://blockgeeks.com/guides/eos-blockchain/#What_is_TAPOS
https://eosio.stackexchange.com/questions/41/how-can-i-generate-random-numbers-inside-a-smart-contract
https://www.weibo.com/ttarticle/p/show?id=2309404298445014841517
https://medium.com/dapppub/fairdicedesign-315a4e253ad6
领取专属 10元无门槛券
私享最新 技术干货