抽獎的公平性向來是抽獎活動中最受人質疑的部分。由於涉及隨機數的選取,網上的抽獎活動幾乎無法保證公平性,因為很難保證中獎號碼是隨機生成而非平台人工選取的,也很難保證平台是否刪掉了一部分用戶的中獎資格而未告知用戶。例如:前陣子支付寶錦鯉活動就有人懷疑中獎者是否是欽定的;前陣子王思聰在微博平台上的抽獎,中獎者男女比例為 1:112,甚為蹊蹺。
為了解決抽獎公平性的問題,消除暗箱操作的可能性,我們特別設計了以下抽獎過程。
什麼樣的抽獎是公平的
抽獎過程可以描述為從 0~N-1 (N為獎券總數) 的整數中抽取一個或多個隨機整數的過程。除了抽獎演算法和抽獎過程需要公開透明之外,一個公平的抽獎過程所使用的隨機數(其實是偽隨機數)應具有如下的性質:
- 隨機數的生成過程不需要依賴於用戶對本網站或者任何第三方平台的信任。
- 事先無法預測。
- 事後公開可查。
- 概率上滿足均勻分布。
為了保證性質1~3,我們選擇使用比特幣區塊的哈希值來作為我們的隨機數種子;性質4只要選取常用的哈希函數即可保證。
我們的抽獎演算法
假設獎券編號是連續發放的整數。我們的的抽獎演算法如下:
- 選取指定時刻(即抽獎時間)後被挖出的第一個比特幣區塊的哈希值作為隨機數種子,記作 S。
- 用 SHA-256 演算法計算 S 的的哈希值 H,然後把 H 作為16進位數字轉換為長整數 L。
- W = L % N 為中獎的獎券編號,其中 N 為總獎券數量,%為求餘數。
- 如需抽出 M 個中獎者,則設新種子為 S = H 並且重複 2、3 兩步,直到抽出 M 個不重複的中獎者為止。
上述抽獎步驟實際上是用完全公開可驗證的方法生成了一個或多個不可控的隨機數,其中最重要的隨機數種子由比特幣區塊的性質來保證它滿足我們所有的要求。只要知道了我們公布的抽獎時間和發放的獎券總數,任何人都可以在獎券停止發放後計算出一樣的偽隨機數,從而實現了可驗證的公平抽獎結果。有興趣驗證的讀者可以參考我們使用的源代碼自行驗證:
1 2 3 4 5 6 7 | def get_winners(min_n, max_n, num_win, key): res = key winners = set() while len(winners) < num_win: res = hashlib.sha256(res).hexdigest() winners.add(int(res, 16) % (max_n-min_n+1) + min_n) return winners |
此函數的輸入參數分別是:min_n 為獎券編號中最小的(通常我們會把它設成0);max_n 為獎券編號中最大的(取決於參與抽獎的用戶數);num_win 為指定的中獎人數;key 為指定的抽獎時間後被挖出的第一個比特幣區塊的哈希值。
其中的 key 值我們會在開獎後在這個這個頁面公示幾天,或者大家也自己可以去 blockchain.com/explorer 或者任何比特幣信息查詢網站查到相應的區塊哈希值。
我們的抽獎結果能保證完全公平嗎?
在使用了以上方法生成中獎號碼後,暗箱操作的可能性已經大大降低了,但是我們仍然無法完全證明我們的抽獎結果是公平的,原因在於我們無法證明發放的獎券總數的正確性。出於保護用戶隱私的目的,我們不能公開每一位參與抽獎的用戶的信息,所以理論上來說,我們可以通過增發不存在的獎券來降低用戶的中獎概率。為了增加這方面的透明度,我們製作了這個頁面,可以即時查看每一張獎券的編號和發放時間。如果獎券的發放有什麼異常,大家隨時都可以發現。
還有一種改變中獎概率的方式,就是自己參與比特幣挖礦,如果挖出了區塊並且算出自己沒有中獎,可以拋棄這個區塊不上報,以期待在沒有被其他人搶先的情況下下次挖出的區塊可以讓自己中獎。然而,挖出一個有效比特幣區塊的獎勵是 12.5 BTC,價值約為 50k USD 以上,礦工間競爭激烈也沒有人能保證不被其他礦工搶先,所以有點理智的人都不會幹這麼奇葩的事情。
雖然我們仍然無法完全證明抽獎活動的公平性,但至少我們已經無法操縱中獎號碼了。剩下的一部分公平性,一半靠大家監督,一半靠我們的良心。雖然不能嚴謹地證明,但我們希望大家能看到我們在這方面的努力,給我們支持與信任,幫助我們做的更好。謝謝大家一直以來的支持~