本文へスキップ
RandNext
Esc

検証可能な抽選の仕組み

各抽選は、誰でも当選者を再計算できる十分なデータを公開します。アルゴリズムと信頼モデル、証明できること・できないことを説明します。

概要

  1. 主催者がリストをロックし、RandNext が指紋とランダムシードの確約値を公開します。
  2. 主催者が抽選前に公開記録を共有し、参加者は指紋と確約値を保存できます。
  3. 抽選時にシードと drand ビーコン値が公開され、誰でも当選者を再計算できます。

1. リストの正規化

各行を Unicode NFC で正規化し、前後の空白と空行を削除します。設定に応じて重複を保持または統合し、順序は変えません。

2. シードと確約

ロック時に安全な 32 バイトのシードを生成し、その SHA-256 確約値だけを公開します。最終シードにはリスト指紋、抽選パラメータ、未来の drand 値も含まれます。

VERSION      = "randnext-draw-v1"                       (ASCII)
entries_hash = SHA256(entries_bytes)                     32 bytes
server_seed  = 32 bytes from a CSPRNG                    sealed until the draw time
commitment   = SHA256(server_seed)                       published when the list is locked
params       = "id=" + draw_id + "\nwinners=" + k + "\nentries=" + n      (ASCII)
final_seed   = SHA256( UTF8(VERSION) || 0x00 || server_seed || entries_hash || UTF8(params) )

3. 公平な選出

HMAC-SHA256、棄却法、部分 Fisher–Yates シャッフルにより、偏りなく順位順の当選位置を選びます。

block(i) = HMAC-SHA256(key = final_seed, msg = UTF8(VERSION) || uint32_be(i)),  i = 0, 1, 2, …
stream   = block(0) || block(1) || …   read 4 bytes at a time as big-endian uint32

uniform(m):                            # an integer in [0, m), m ≤ 2^32
  limit = floor(2^32 / m) * m
  repeat u = next_uint32() until u < limit
  return u mod m

a = [0, 1, …, n-1]                     # partial Fisher–Yates
for i in 0 … k-1:
  j = i + uniform(n - i)
  swap(a[i], a[j])
winners = a[0 … k-1]                   # a[0] is 1st place

自分で検証

ブラウザ検証ツールに抽選 ID を入力するか、JSON 記録とリストを保存して Python 参照スクリプトや独自実装で確認できます。

必要な信頼

検証は当選者がロック済みリストと公開アルゴリズムから得られたことを証明しますが、主催者が複数の抽選を作成していないことまでは証明しません。抽選前に記録リンクを保存してください。

アルゴリズムのバージョン

v2 は未来の drand 公開ランダムビーコンと署名を検証します。v3 は複数賞品のライブ抽選用で、賞品ごとに独立したビーコンを使い、重複当選を防ぎます。