关于区块链:智能合约安全随机数

40次阅读

共计 2133 个字符,预计需要花费 6 分钟才能阅读完成。

本次咱们将带大家理解智能合约中一个常常被用到的货色——随机数。
智能合约的开发中经常会用到随机数,例如 Lottery 和当初风行的 NFT 数字藏品的属性等都须要用到随机数。目前来说常见的随机数获取有两种:应用区块变量生成随机数,应用预言机来生成随机数。上面咱们理解一下这两者的特点:

 1)应用区块变量生成随机数 
咱们先理解一下常见的区块变量有哪些:block.basefee(uint):以后区块的根本费用 block.chainid(uint):以后链 idblock.coinbase():以后区块矿工地址 address payableblock.difficulty(uint):以后区块难度 block.gaslimit(uint):以后区块 gaslimitblock.number(uint):以后区块号 block.timestamp(uint):自 Unix 纪元以来的以后区块工夫戳(以秒为单位)blockhash(uint blockNumber) returns (bytes32):给定区块的哈希,仅实用于 256 个最近的区块 其中 block.difficulty, blockhash, block.number 和 block.timestamp 这四个是用得比拟多的。
由区块数据生成的随机数可能会限度普通用户预测随机数的可能性,然而并不能限度矿工作恶,矿工能够决定一个区块是否被播送,他们挖出了一个区块不是肯定要播送进来也能够间接扔掉,这个就叫矿工的选择性打包。他们能够继续尝试生成随机数,直至失去想要的后果再播送进来。当然,矿工会这样做的前提是有足够的的利益引诱,例如能够取得一个很大的处分池中的处分,因而应用区块变量获取随机数的办法更适宜于一些随机数不属于外围业务的利用。

 2)应用预言机生成随机数 
预言机是专门为生成随机数种子而搭建的链上或者链下的服务。除了应用第三方服务,也能够由 DApp 开发商本人搭建一个链下服务提供随机数,这种在链上获取链下数据的场景通常是通过链上预言机的形式来实现。当然这种办法也会有一些平安危险,例如依赖第三方给出的随机数种子的话同样会存在第三方舞弊或者行贿的情景,即便是本人搭建的随机数服务也可能因为故障等起因无奈应用,我的项目方也有可能操控随机数对 DApp 的运行和用户造成重大的损失。因而应用链下服务获取随机数的办法依赖于是否有一个可信又稳固的第三方服务,如果有,那么这个办法相较于应用区块链变量生成随机数的办法,随机数的不可预测性会更强一些。

接下来咱们还是用合约代码来给大家演示弱随机数可能带来的危害。
 
破绽示例 

 
破绽剖析 
首先咱们先来理解一下代码中的两个函数,abi.encodePacked 和 keccak256:
l abi.encodePacked 对参数进行编码,solidity 提供两种编码方法 encode 和  encodePacked,前者对每一个参数进行 32 字节补齐,后者不进行补齐而是间接将待编码参数连接起来。
l keccak256 哈希算法,能够将任意长度的输出压缩成 64 位的 16 进制的数,且哈希碰撞的概率近乎为 0。接下来咱们来看合约代码,这个合约是一个猜数字赢以太的游戏,咱们能够看到,部署者应用上个区块的区块哈希和区块工夫作为随机数种子生成随机数,咱们只须要模仿他的随机数生成办法就能够失去处分。
上面咱们来看攻打合约:

攻打合约

  上面咱们先来分析攻击流程:攻击者调用 Attack.attack() 函数,它模仿了 GuessTheRandomNumber 合约中随机数的生成形式生成随机数后调用 guessTheRandomNumber.guess() 并将生成的随机数传入,因为从 Attack.attack() 生成随机数到调用 guessTheRandomNumber.guess() 都是在同一区块中实现的,且在同一区块中 block.number 和 block.timestamp 这两个参数是不变的,所以,Attack.attack() 和 guessTheRandomNumber.guess() 这两个函数生成的随机数的后果是雷同的,从而攻击者能够顺利通过 if(_guess == answer) 判断失去处分。

修复倡议 
如果随机数属于非核心业务的话能够应用将来区块哈希来生成随机数也就是将猜数和领奖离开做异步解决。针对这次的破绽合约写一个优化版本,大家能够看下:

  增加了 deadline 参数将 guess 和 claim 做异步解决后,在部署合约后的 72 小时内能够调用 guess() 猜随机数,在 72 小时后 guess() 敞开 claim() 开启,玩家能够通过 claim() 来验证本人是否猜中。当然,这个修复合约并不是完满的解决方案,正如前置常识中提到的,如果矿工来玩的话他能够在打包的时候晓得本人是否猜中,如果猜中打包上链,如果没有猜中放弃打包(置信没有任何一个矿工违心为了失去一个以太而付出这么大的代价)。所以最优的解决办法还是接入出名预言机来获取随机数。
如果想理解更多的智能合约和区块链常识,欢送到区块链交换社区 CHAINPIP 社区,一起交流学习~ 社区地址:https://www.chainpip.com/ 

正文完
 0