两年多前进入区块链这个行业,学习了比特币的技术实现,同时通过编写容量证明的共识机制,对于加密货币的知识有了一定的了解,也不由得发自内心的感叹加密货币机制的设计之精妙。而以太坊的出现,让人们可以在该网络上编写智能合约,最大限度的将去中心化加密货币的优势展现出来。一个不可能消失的基础设施,在这个基础上制定规则将被无差别执行,只要规则制定完成,无人可以更改,这是一种全新的去第三方的信任机制,在算力不被控制的情况下,无人可以作弊。
Etheroulette 是我在一年前做的一个以智能合约为基础的可自证清白的轮盘游戏。它运行在以太坊网络上,通过零知识证明来保证玩家、庄家以及矿工都无法操纵游戏结果。它是如何做到的呢?本文从描述其内部的机制来揭示其原理。
Roulette 起源于欧洲的一个赌博游戏,在一个轮盘上有 37 或 38 个数字,玩家可以按照不同的规则来进行押注,押的数字或个数不同将会使用不同的赔率来进行游戏,庄家使用一个球在轮盘上滚动,最终球将会停在这些数字中的其中一个,押中了该数字的玩家将按照赔率获得奖励。
例如下图,圆形的区域是小球滚动的区域,最终会停留在某个数字的格子里,而绿色底的区域,则是玩家押注的区域,玩家将自己的筹码放到对应的押注区。玩家押注的方式很多,你可以用一个筹码压 12 个数字,或者 18 个数字,或者押所有红色的数字,当然你觉得你运气好你也可以只押一个数字。
游戏复刻了所有的传统 Roulette 的押注方式以及赔率,除了没有真实的球在轮盘中滚动外,得出的结果是保证绝对随机的,并且玩家、庄家以及矿工都无法操纵结果。合约代码已经上链并且可以被任何人审察。
以太 Roulette 为了增强刺激和趣味性,还增加了奖池的概念,当一个玩家赢钱时,他会将赢的钱的非常小的一部分投入到这个奖池中,然后他有一定的机会能赢取这个奖池中的所有的钱。这个奖池中的钱会累积得越来越多,真正获得这个奖池的人会成为一个大赢家,然后这个奖池里的钱又会从零开始累积,直到下次被另一个幸运的玩家赢走。
以太 Roulette 游戏上传到 Rinkeby 网络上,你可以获取免费的测试 ETH 来进行游戏,完全不需要消耗真正的 ETH。
在传统的在线依赖庄家产生随机结果的游戏永远都面临着一个这样的问题:玩家永远都无法保证庄家产生的随机数是真正的公平的。比如,当玩家要在在线轮盘游戏中压所有的红色数字,你无法保证庄家不会故意产生出一个黑色的数字作为结果,以操纵整个游戏的走向,他们可以做得更加巧妙一些让你在无法察觉的情况下尽最大可能倾斜赔率,让庄家获得极大的优势。
因为这些问题的存在,所以玩家是不会去玩一个无法自证清白的在线赌博游戏的,因为结果和赢率并不是像玩家所看上去的那样。就算是庄家公平的生成游戏结果,但是因为安全问题无法把服务器公开让玩家查证(需要太多的技术手段并且庄家完全可以在你检查完成后再换上不公平的代码),所以玩家也无法相信游戏结果。
所以在线的公平的赌博游戏需要:
公正的产生随机值
向玩家证明自己没有作弊
同时保证玩家也无法操纵游戏结果
首先要明确的一点就是,合约上传到以太坊网络后是无法再更改,想要更改合约需要废弃掉原来的合约,然后将新的合约更新到一个新的地址上,所以,一旦合约更新到网络,那么该份合约代码将永远存在,除非合约所有者将其标记为删除状态(Kill)。以太Roulette的Rinkeby上的合约可以通过点击这个 链接 查看。
http://etherscan.io 这个网站是专门用于查看以太坊网络区块的,因为区块链的数据都是公开透明,所以这个网站可以让任何人查看以太坊上的任何数据,包括交易,区块,时间等等。
注:以下的游戏机制分两种角色,第一种叫做:玩家,是指来玩游戏的人,第二种叫做庄家,是指与来玩游戏的玩家对赌的人。
首先,玩家向庄家请求要开始一盘游戏;
庄家收到请求后,马上产生一个随机数 R,然后这个随机数保存在服务器上,并且将这个随机数计算哈希值,然后将这个哈希值返回给玩家;
玩家收到了哈希值后,开始在轮盘上下注,然后将下注的数据连带这个哈希值和押注的 ETH 一同发送给以太坊网络上的游戏合约;
庄家的服务器通过以太坊网络知道了这个随机数 R 对应的游戏已经下注,于是在下一个区块产生的时候,将随机数 R 发送给以太坊网络上的游戏合约要求揭示游戏结果;
以太坊网络上的游戏合约在收到要求揭示结果的调用后,先将随机数 R 进行哈希计算,然后找出保存在以太坊网络中的对应该随机数 R 的哈希值的游戏,确认游戏在等待揭示的状态后,拿出随机数 R 和当前的区块的哈希值再次哈希,将结果与 38 取模,得到的结果即为最后揭示的游戏值。
当揭示的值得出来后,若玩家赢得了游戏,那么以太坊上的合约将会自动把对应的 ETH 分配到玩家的账号中,玩家可以随时调用以太坊的合约来将这些 ETH 转出。
在整个游戏中,庄家只会产生一个随机数 R,这个数字会在玩家下注前就产生出来,并且它并不是最终的结果产生的被模数,它只能算作是一个种子数。那么意味着:1、庄家在不知道玩家会押哪个数字前,就已经把种子数产生好了;2、庄家无法通过该种子数获得最终的揭示数字。
这整个游戏中最精妙的地方,就是在于,庄家不知道玩家要押什么数字的时候就已经把结果固定了,并且庄家自己都不知道结果是什么,游戏最终揭示的数字取决于合约执行执行揭示操作时所在的区块的哈希值和这个种子数字一起的哈希结果。
最重要的操作,都是在以太坊的游戏合约里完成的,押注的时候收取用户ETH以及数字揭示后计算揭示数字后的转账操作,庄家只负责固定结果和调用合约上的揭示功能。而玩家只负责押注这一个动作,剩下的都不需要关注。
前端使用了 React 配合 web3 对以太坊智能合约进行访问,事实上,玩家需要使用 Firefox 或 Chrome 浏览器并安装上 Metamask 钱包插件才可以进行游戏,而前端的 web3 将会访问这个插件提供的接口来进行押注等信息。同时前端还使用 axios 来访问庄家服务器来获取这个随机数 R。
后端使用了 nodejs 同时也使用了 web3 对以太坊智能合约进行访问。产生随机数 R 并且保存到数据库中,这样在接收到以太坊的网络事件后会在数据库里查找对应的随机数 R 并调用合约上的揭示功能。