文/LeanCloud 工程师 王子亭git
在春节先后,常常在社区中看到有关年会抽奖程序的讨论,其中抽奖的公平性是被你们讨论得最多的点。可能有的人会说能够用 random.org 来取随机数,的确这个网站能够保证数字的随机性,但若是访问 random.org 的浏览器位于一台计算机上,如何证实这个随机数确实来自 random.org 呢?如何肯定这台计算机从软件到硬件、从操做系统到网络接口都没有被动过手脚呢?github
因此咱们须要一种「可验证」的随机数生成算法,即 这个算法不该该是运行在单个设备上的,而是能够在不一样的设备上屡次运行,而且老是获得相同的结果,这样才能被你们信服 —— 每一个人均可以在本身的设备上进行验算。算法
其实说来直接使用比特币下一个区块的 hash,或者股市收盘价格做为随机数是最简单、最具备可行性的作法了。但这并非说比特币(矿工能够有选择性地提交区块来影响随机数)或股市不能被操控,而只是相对于咱们的年会抽奖来讲,他们的体量都太大了,去操控比特币或股市是不划算的。后端
因而我想可否利用以前了解到的去中心化和区块链的知识,来实现一个去中心化的、可验算的、难以操控的抽奖程序,让这个抽奖程序以分布式的方式运行在全部参与者的设备上,会有一个后端服务器帮助客户端进行广播,但自己没有特权,客户端会对随机数的产生过程进行验算,确保没有人做弊。浏览器
其实在 Ethereum(一个相似比特币的区块链)上已经有了很是成熟的 随机数生成器,它基于「两阶段提交」来实现,在第一阶段每一个人生成一个随机数,并将这个随机数的 hash 广播出去;而后在第二阶段以前的参与者再广播随机数的明文,而后将全部参与者的随机数加到一块儿,造成一个没法被任何操纵的随机数。安全
这个算法的要点在于,在第一阶段中每一个人都选定了一个数字,但广播的倒是数字的 hash,也就是说你没办法知道其余人选定的数字,也就没法去构造特定的数字来影响结果;而在第二阶段你们才开始广播真正的数字,同时每一个人都会使用以前的 hash 进行验算,保证这时的数字与第一阶段相同。服务器
我将这个算法进行了细化,在浏览器中用 React 实现了客户端,再用 Node.js 实现了一个基于 WebSocket 的服务器来辅助广播。你们能够在 rollup.leanapp.cn 访问到这个原型(源代码和详细算法位于 jysperm/rollup),能够本身开多个浏览器窗口进行测试:
若是有人捣乱会怎么样,好比在第二阶段广播了错误的数字或者根本没有广播呢?在 Ethereum 上这会给参与者带来经济上的惩罚。而在咱们的原型中,咱们只能作到感知到这些做弊的状况,而后停止抽奖,若是有人执意捣乱,就会致使抽奖一直没法完成了。
那是否是说这个原型已经作到无懈可击了呢?并非,目前是使用单一的后端来实现广播,若是这个后端有选择性地不对特定的客户端广播一些消息,就会致使这个特定的客户端被孤立,和其余人产生不一样的抽奖结果,并且这个被孤立的客户端也无从证实到底是后端没有广播,仍是本身忽略了广播。网络
更好的设计多是经过真正 P2P 的方式进行广播,这样除非其余全部参与者联合起来孤立一部分人,不然其余参与者就能够从未参与攻击的人哪里获得正确的广播。然而真正的 P2P 实际上是没办法实现的 —— 你老是须要一个用做服务发现的节点,同时也要考虑通信信道的安全性,当前的密码学技术虽然可能保证消息不被篡改,但却没法保证消息不丢失。app
关于区块链的更多知识可参考我以前的文章 BlockChain 与 Ethereum 介绍,其中也有对 Ethereum 上的随机数生成器的详细讨论。dom