네 번째 입력값
저희 설명을 포함해 공정성 검증(provably fair) 게임에 대한 모든 설명은 세 가지 입력값을 언급합니다: 하우스가 사전에 커밋하는 서버 시드, 사용자가 직접 설정할 수 있는 클라이언트 시드, 그리고 베팅마다 1씩 증가하는 논스입니다. 이 값들을 키드 해시에 넣으면 라운드의 무작위성이 나옵니다. 이 설명은 동전 던지기 게임에는 완전하지만, 그 외 거의 모든 게임에는 불완전합니다. 해시는 하나의 숫자를 만들어내는데, 대부분의 게임은 여러 개가 필요하기 때문입니다.
네 번째 입력값은 커서입니다. 이 사이트에서 해시되는 메시지는 "클라이언트 시드-논스"가 아니라 "클라이언트 시드-논스-커서"이며, 커서는 라운드 내부의 카운터입니다: 게임이 필요로 하는 첫 번째 난수는 0, 두 번째는 1, 이런 식으로 이어집니다. 각 커서 값은 새로운 256비트 다이제스트를 생성하며, 이는 이웃 값들과 어떤 식으로도 악용할 수 있는 관계가 없고, 엔진은 각 다이제스트의 첫 8바이트를 0과 1 사이의 숫자로 읽습니다.
엔진 검증됨_shared/demoLocal.ts: uAt(pair, cursor)는 u64(HMAC-SHA256(serverSeed, `${clientSeed}-${nonce}-${cursor}`))를 반환하며, 이는 서버의 RandomUtils.generateHash("clientSeed-nonce-cursor")와 일치한다고 설명되어 있습니다. _shared/rng.ts: u64는 다이제스트의 첫 8바이트를 hi/2^32 + lo/2^64로 읽으며, 서버의 ULong→Double 변환과 "동일한 단일 반올림 구조"입니다. 논스는 모든 게임에 걸쳐 시드 쌍당 하나의 스트림입니다: 각 베팅은 현재 논스를 소비하고 nonce + 1을 저장합니다.
따라서 라운드는 하나의 난수가 아니라 번호가 매겨진 숫자 목록이며, 게임이 필요로 하는 만큼 길고, 목록의 모든 항목은 시드 쌍과 논스가 정해지는 순간 고정됩니다. 게임은 진행하면서 뽑는 것이 아니라, 이미 존재하는 목록을 순서대로 읽습니다.
각 게임이 읽는 것
흥미로운 점은 이 목록이 게임마다 다르며, 각 파생 모듈이 헤더에 그 구조를 명시한다는 것입니다. 다음은 엔진 소스에서 가져온, 시드 쌍에서 값을 뽑는 모든 오리지널 게임의 커서 스트림입니다.
| 게임 | 읽는 커서 | 결과값 |
|---|---|---|
| 림보 | 0 | 0.99 ÷ (1 − u) 곡선을 통한 하나의 숫자 |
| 룰렛 | 0 | 포켓: floor(u × 37) |
| 후쿠부쿠로 | 0 | 가방의 가중치 테이블에 따른 아이템 |
| Sic Bo | 0, 1, 2 | 주사위 세 개 |
| 친치로 | side × 9 + attempt × 3 + die | 최대 18회: 딜러 다음 플레이어 순으로, 각각 최대 세 번씩 주사위 세 개를 던짐; 족보가 완성되는 던지기가 나오면 해당 사이드는 종료됨 |
| 고반 플립 | 플립당 1개 | 플립 i는 커서 i를 읽음; 라이드의 모든 면은 첫 코인이 구매되는 시점에 고정됨 |
| 오미쿠지 | 0, 그다음 1–3, 그다음 4부터 | 운세 등급; 당첨 시 승리하는 세 개의 칸; 나머지는 채움용 글리프 |
| 파고다 | 층당 1개 | 각 층의 함정 타일: floor(u × tiles) |
| 비디오 포커 | 5개, 교체한 카드마다 1개씩 추가 | 52장의 카드에 대한 비복원 시퀀스 1회; 전부 홀드하면 추가 커서는 읽지 않음 |
| 카지노 홀덤 | 최대 9 | 플레이어 1, 플레이어 2, 딜러 1, 딜러 2, 플랍 1–3, 턴, 리버; 턴과 리버는 CALL 시에만 읽힙니다 |
| 블랙잭 | 카드당 하나씩 | 무한 슈, 카드 = floor(u × 52), 딜링 순서대로: 플레이어, 딜러 업카드, 플레이어, 딜러 홀카드, 그 이후 모든 추가 드로우 |
| 안다르 바하르 | 0–4, 그다음 경합하는 카드마다 하나씩 | 부스트된 두 버킷, 그 크기, 조커, 그리고 등급이 반복될 때까지의 경합 |
| 하나비 | 스핀당 15개 | 셀 = 릴 × 3 + 행; 프리스핀 j는 15 + 15j부터 읽습니다 |
| 빙고 | 0–24, 그다음 25–62 | 카드, 컬럼당 다섯 번의 드로우; 그다음 38개의 공 |
키노와 크래시는 예외입니다: 이들의 데모 라운드는 아래 설명된 시드 쌍 논스 대신 티켓별 해시 체인을 사용합니다. 마인즈와 플린코는 하우스 서비스에 의해 정산되며, 마인즈 API는 전체 레이아웃이 하나의 논스에서 나온다고 명시합니다.
엔진 검증됨bingo/derive.ts의 헤더 주석(“커서 스트림: 0..24 — 카드 … 25..62 — 38개의 공”), hanabi/engine.ts(“기본 스핀 = 셀 0..14 … 프리스핀 j = 셀 15+15j .. 29+15j”), blackjack/derive.ts(“카드 i = 커서 i에서 floor(u × 52) … 딜링 순서(커서): player1, dealer-up, player2, dealer-hole”), vpoker/derive.ts(“처음 다섯 개는 딜, 교체는 같은 시퀀스를 이어갑니다 … 모두 홀드하면 더 이상 커서를 읽지 않습니다”), holdem/derive.ts(“player1, player2, dealer1, dealer2, flop1, flop2, flop3, turn, river. 턴/리버 커서는 CALL 시에만 소비됩니다”), andar/derive.ts(커서 0–4 그다음 “5.. 경합하는 카드당 커서 하나”), koban/derive.ts(“플립 i는 커서 i를 읽습니다”), sicbo/derive.ts(“커서 0/1/2는 세 개의 주사위입니다”), roulette/derive.ts(“커서 0만으로 포켓 결정: floor(u × 37)”), fuku/derive.ts(“커서 0만으로 아이템을 뽑습니다”), omikuji/derive.ts(커서 0 등급, 1..3 당첨 셀, 4.. 필러); demoLocal.ts의 rollSide(친치로 커서 = side*9 + attempt*3 + d), trapAt(파고다: 층 번호가 커서), demoLimboBet(uAt(p, 0)); mines/minesApi.ts(“전체 지뢰 레이아웃은 이 하나의 논스에서 나옵니다”).
이 표에서 세 가지가 두드러집니다. 첫째, 카드를 딜링하는 게임들은 고정된 순서로 카드당 커서 하나씩 읽으며, 이것이 블랙잭 핸드를 액션 리스트에서 재현할 수 있는 이유입니다: 히트하기로 한 결정이 카드를 만드는 것이 아니라, 딜링 이전에 이미 완성되어 있던 목록의 다음 카드를 뒤집는 것뿐입니다. 둘째, 여러 게임은 일찍 읽기를 멈춥니다. 다섯 장 모두 홀드하는 비디오 포커 핸드는 커서 5를 전혀 건드리지 않으며, 홀덤 폴드는 턴이나 리버를 읽지 않습니다. 숫자들은 거기 있었지만, 게임이 그것을 필요로 하지 않았을 뿐입니다. 셋째, 안다르 바하르의 부스트된 두 버킷과 오미쿠지의 운세 등급은 카드와 셀보다 먼저, 스트림의 맨 앞에서 뽑히므로, 라운드의 특별한 기능들은 일반적인 기능들과 동일한 커밋먼트로 고정됩니다.
비복원 추출
0과 1 사이의 균일한 숫자를 룰렛 포켓으로 바꾸는 것은 쉽습니다: 37을 곱하고 내림하면 됩니다. 덱은 더 어렵습니다, 두 번째 카드가 첫 번째 카드와 같으면 안 되기 때문입니다. 엔진은 이를 주석에서 '풀 홉'이라 부르는 방식으로 처리합니다: 아직 풀에 남아 있는 카드 목록을 유지하고, 커서의 숫자를 사용해 그 안의 위치를 선택한 다음, 선택된 카드를 목록의 끝으로 스왑하여 다음 드로우가 하나 적은 목록에서 선택되도록 합니다. 비디오 포커, 카지노 홀덤, 안다르 바하르, 그리고 빙고 카드는 모두 이 방식으로 뽑히므로, 라운드 내에서 카드나 숫자가 반복되지 않습니다.
블랙잭은 의도적인 예외입니다. 그 슈는 무한하며: 모든 카드는 자신의 커서에서 floor(u × 52)로 결정되고, 같은 카드가 한 핸드 안에서 두 번 나올 수 있습니다. 이는 블랙잭 페이아웃 아티클에서 논의된 공개된 비용이 있는 디자인 선택이며, 마케팅 페이지가 말해주지 않는 것을 커서 레이아웃이 정확히 알려주는 예입니다.
엔진 검증됨vpoker/derive.ts: “카드는 52장 덱에 대한 하나의 비복원 시퀀스에서 나옵니다(풀 홉, 드로우당 커서)”. holdem/derive.ts: “하나의 비복원 풀 홉 시퀀스(드로우당 커서)”. bingo/derive.ts: “컬럼당 다섯 번의 풀 홉 드로우”와 “38개의 공, 1..75에 대한 풀 홉”. blackjack/derive.ts: “무한 슈: 카드 i = 커서 i에서 floor(u × 52)”.
두 가지 예외
Keno와 Crash는 데모 엔진에서 시드 쌍 nonce를 전혀 읽지 않습니다. Keno는 티켓별 방식을 사용합니다: 티켓은 자체 랜덤 해시와 솔트를 받고, 열 개의 공 각각은 이전 해시를 다시 해싱하여 상위 비트를 숫자로 취하고 40개짜리 줄어드는 풀에서 선택하는 방식으로 뽑힙니다. 공정성 패널은 시드 쌍이 아니라 이 두 문자열로부터 keno 티켓을 검증합니다. Crash는 bustabit 관례를 따르며, 각 라운드의 crash point가 정확한 정수 연산으로 자신의 링크에서 계산되는 해시 체인입니다.
엔진 검증됨demoLocal.ts kenoDraws: “베팅별 randomHash 체인 + 솔트(공정성 모달을 통해 티켓별로 검증 가능) … 시드 쌍 nonce는 건드리지 않음”; 루프는 HMAC-SHA256(salt, 이전 해시)를 열 번 해싱하고, 상위 52비트를 읽어 40개 숫자 풀에서 index floor(norm × remaining)을 선택하며 끝으로 스왑합니다. rng.ts h52와 crash 주석: “100분의 1 단위 bustabit crash point: floor((n·e − h)/(e − h)), e = 2^52, n = 100 … 서버와 일치시키기 위한 정확한 BigInt 연산”.
두 예외 모두 커서 방식보다 약하지 않습니다. 오히려 더 오래되었고 더 잘 알려져 있습니다. 플레이어에게 중요한 것은 패널이 라운드가 어떤 방식을 사용했는지 보여주고, 각각이 표시된 문자열로부터 재계산 가능하다는 것뿐입니다.
테이블에서의 의미
- 라운드는 당신이 행동하기 전에 이미 존재합니다. 라운드의 모든 카드, 공, 릴 심볼은 시드 쌍, nonce, 그리고 커서 위치의 함수입니다. 히트, 콜, 홀드는 어떤 항목을 읽을지를 선택할 뿐, 그 안에 무엇이 있는지를 정하지 않습니다.
- 당신의 선택은 목록을 바꿀 수 없고, 하우스도 마찬가지입니다. 서버 시드가 커밋되고 nonce가 고정되면, 어느 쪽도 항목 7을 시드를 바꾸지 않고는 변경할 수 없으며, 그렇게 하면 당신에게 보여진 핑거프린트가 깨집니다.
- 검증자는 같은 목록을 같은 방식으로 읽어야 합니다. 빙고 라운드를 재계산한다는 것은 공개된 순서로 63개의 커서를 모두 유도하는 것을 의미하고, 홀덤 폴드를 재계산한다는 것은 7개를 유도하는 것을 의미합니다. 시드 패널은 이를 브라우저에서 수행하며, 위 표의 레이아웃이 그 기준입니다.
- 연승은 기분이 아니라 목록입니다. 두 라운드는 시드 쌍 외에는 아무것도 공유하지 않습니다. nonce가 다르므로 모든 다이제스트가 다릅니다. 이는 핫핸드 아티클이 반대 방향에서 지적하는 것과 같은 요점입니다.
세 가지 입력이 라운드를 결정합니다. 네 번째는 라운드가 얼마나 긴지를 결정합니다. 한 줄로 보는 커서
위의 커서 레이아웃은 브라우저 검증자와 데모 엔진이 공유하는 클라이언트 측 유도 모듈에서 읽은 것입니다. 하우스 서비스가 지급 권한이며, 소스의 주석이 모듈이 서버를 미러링한다고 말하는 경우, 이는 저희가 인증한 것이 아니라 작성된 그대로 인용된 것입니다.
자주 묻는 질문
공정성 입증 라운드에서 커서란 무엇인가요?
라운드 내부의 카운터입니다. 서버 시드와 함께 해싱되는 메시지는 “클라이언트시드-nonce-커서”이며, 커서 0은 게임에 필요한 첫 번째 난수를, 커서 1은 두 번째를 제공하는 식입니다. 숫자 하나만 필요한 게임은 커서 0만 읽습니다.
커서는 nonce와 어떻게 다른가요?
nonce는 라운드를 세며 모든 오리지널에서 베팅마다 1씩 증가합니다. 커서는 한 라운드 내의 추첨을 세며 매 라운드마다 0에서 다시 시작합니다. 시드 쌍과 함께, 이 둘은 사이트가 생성하는 모든 난수를 식별합니다.
빙고 라운드는 몇 개의 난수를 사용하나요?
63개입니다: 커서 0~24는 카드의 25개 칸을 배치하고, 커서 25~62는 38개의 공을 뽑으며, 각 풀 내에서 중복 없이 진행됩니다.
블랙잭에서 히트를 하면 다음 카드가 바뀌나요?
아니요. 각 카드는 딜 순서에 따라 자신의 커서에서 유도되며, 시드 쌍과 nonce가 고정되는 순간 전체 목록이 이미 존재합니다. 히트는 다음 항목을 읽고, 스탠드는 그것을 읽지 않은 채로 남깁니다.
모든 게임이 커서 방식을 사용하나요?
Keno와 Crash는 대신 해시 체인을 사용합니다: keno는 티켓별 랜덤 해시와 솔트를 10번 해싱하고, crash는 bustabit 체인을 정확한 정수 연산으로 따릅니다. 공정성 패널은 표시되는 문자열로부터 각각을 검증합니다.
출처 및 참고자료
- Betkyo 엔진 소스: _shared/demoLocal.ts (uAt, rollSide, trapAt, kenoDraws), _shared/rng.ts (u64, h52, crash point), 그리고 bingo, hanabi, blackjack, vpoker, holdem, andar, koban, sicbo, roulette, fuku, omikuji의 파생 모듈
- RFC 2104 — HMAC: 메시지 인증을 위한 키 해싱
- Bustabit provably fair 방식 (엔진이 모방하는 crash-point 구성)
이 글에서 다루는 게임들