文/LeanCloud 工程師 王子亭git
在春節先後,常常在社區中看到有關年會抽獎程序的討論,其中抽獎的公平性是被你們討論得最多的點。可能有的人會說能夠用 random.org 來取隨機數,的確這個網站能夠保證數字的隨機性,但若是訪問 random.org 的瀏覽器位於一臺計算機上,如何證實這個隨機數確實來自 random.org 呢?如何肯定這臺計算機從軟件到硬件、從操做系統到網絡接口都沒有被動過手腳呢?github
因此咱們須要一種「可驗證」的隨機數生成算法,即 這個算法不該該是運行在單個設備上的,而是能夠在不一樣的設備上屢次運行,而且老是獲得相同的結果,這樣才能被你們信服 —— 每一個人均可以在本身的設備上進行驗算。算法
其實說來直接使用比特幣下一個區塊的 hash,或者股市收盤價格做爲隨機數是最簡單、最具備可行性的作法了。但這並非說比特幣(礦工能夠有選擇性地提交區塊來影響隨機數)或股市不能被操控,而只是相對於咱們的年會抽獎來講,他們的體量都太大了,去操控比特幣或股市是不划算的。後端
因而我想可否利用以前瞭解到的去中心化和區塊鏈的知識,來實現一個去中心化的、可驗算的、難以操控的抽獎程序,讓這個抽獎程序以分佈式的方式運行在全部參與者的設備上,會有一個後端服務器幫助客戶端進行廣播,但自己沒有特權,客戶端會對隨機數的產生過程進行驗算,確保沒有人做弊。瀏覽器
其實在 Ethereum(一個相似比特幣的區塊鏈)上已經有了很是成熟的 隨機數生成器,它基於「兩階段提交」來實現,在第一階段每一個人生成一個隨機數,並將這個隨機數的 hash 廣播出去;而後在第二階段以前的參與者再廣播隨機數的明文,而後將全部參與者的隨機數加到一塊兒,造成一個沒法被任何操縱的隨機數。安全
這個算法的要點在於,在第一階段中每一個人都選定了一個數字,但廣播的倒是數字的 hash,也就是說你沒辦法知道其餘人選定的數字,也就沒法去構造特定的數字來影響結果;而在第二階段你們纔開始廣播真正的數字,同時每一個人都會使用以前的 hash 進行驗算,保證這時的數字與第一階段相同。服務器
我將這個算法進行了細化,在瀏覽器中用 React 實現了客戶端,再用 Node.js 實現了一個基於 WebSocket 的服務器來輔助廣播。你們能夠在 rollup.leanapp.cn 訪問到這個原型(源代碼和詳細算法位於 jysperm/rollup),能夠本身開多個瀏覽器窗口進行測試:網絡
那是否是說這個原型已經作到無懈可擊了呢?並非,目前是使用單一的後端來實現廣播,若是這個後端有選擇性地不對特定的客戶端廣播一些消息,就會致使這個特定的客戶端被孤立,和其餘人產生不一樣的抽獎結果,並且這個被孤立的客戶端也無從證實到底是後端沒有廣播,仍是本身忽略了廣播。app
更好的設計多是經過真正 P2P 的方式進行廣播,這樣除非其餘全部參與者聯合起來孤立一部分人,不然其餘參與者就能夠從未參與攻擊的人哪裏獲得正確的廣播。然而真正的 P2P 實際上是沒辦法實現的 —— 你老是須要一個用做服務發現的節點,同時也要考慮通信信道的安全性,當前的密碼學技術雖然可能保證消息不被篡改,但卻沒法保證消息不丟失。dom
關於區塊鏈的更多知識可參考我以前的文章BlockChain 與 Ethereum 介紹,其中也有對 Ethereum 上的隨機數生成器的詳細討論。