검증 가능한 추첨의 작동 방식
각 추첨은 누구나 당첨자를 다시 계산할 수 있도록 충분한 데이터를 공개합니다. 알고리즘과 신뢰 모델, 증명 가능한 범위를 설명합니다.
간단한 흐름
- 주최자가 참가 목록을 잠그면 RandNext가 목록 지문과 무작위 시드 커밋을 공개합니다.
- 주최자가 추첨 전에 공개 기록을 공유하고 참가자는 지문과 커밋을 저장할 수 있습니다.
- 추첨 시 시드와 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는 여러 상품의 실시간 추첨용으로 상품별 독립 비콘을 사용하고 중복 당첨을 막습니다.