Розробка блокчейн-казино з provably fair
Provably fair — це математична гарантія чесності кожного результату, яку гравець може верифікувати самостійно. Не «довіряйте нам», а «перевірте математику». Ключове конкурентне преимущество blockchain казино над традиційним онлайн-гублингом, де гравець вынужден вірити RNG сертифікатам.
Як працює provably fair
Два основних підходи: VRF on-chain і commit-reveal off-chain.
Chainlink VRF — on-chain verifiable randomness
VRF генерує випадкове число разом з cryptographic proof того, що це число дійсно випадкове і не могло бути передбачено або змінено оператором.
Процес:
- Контракт запрашує randomness у Chainlink VRF
- Chainlink oracle генерує число й proof
- On-chain: proof верифікується, число приймається
- Будь-хто може перевірити: даний proof = дане число, немає іншого числа яке відповідало б цьому proof
Гравець бачить: requestId → blockHash → randomness. Публічно й верифіковано.
Commit-Reveal для off-chain казино
Класичний provably fair для off-chain систем:
1. Казино генерує server_seed (випадкова рядок)
2. Казино публікує hash(server_seed) перед кожною грою
3. Гравець надає client_seed
4. Після гри казино розкриває server_seed
5. Результат = HMAC-SHA256(server_seed, client_seed + nonce)
6. Будь-хто може відтворити: знаючи server_seed + client_seed + nonce → той же результат
Казино не може змінити server_seed після його публікації. Гравець не може змінити client_seed: він зафіксований у ставці. Результат детермінований, але був непередбачуваний до завершення раунду.
On-chain реалізація з Chainlink VRF
// Основний контракт
contract ProvablyFairCasino is VRFConsumerBaseV2Plus {
struct BetRecord {
address player;
uint256 amount;
uint8 gameId;
uint256 randomResult;
bool settled;
uint256 payout;
}
mapping(uint256 => BetRecord) public betRecords;
event BetSettled(
uint256 indexed requestId,
uint256 randomness, // випадкове число публічно
uint256 payout,
bool isWin
);
function placeBet(uint8 gameId, bytes calldata betParams)
external payable returns (uint256 requestId)
{
require(msg.value >= MIN_BET[gameId], "Below minimum");
requestId = s_vrfCoordinator.requestRandomWords(
VRFV2PlusClient.RandomWordsRequest({
keyHash: KEY_HASH,
subId: SUBSCRIPTION_ID,
requestConfirmations: 3,
callbackGasLimit: 200_000,
numWords: 1,
extraArgs: VRFV2PlusClient._argsToBytes(
VRFV2PlusClient.ExtraArgsV1({nativePayment: false})
)
})
);
betRecords[requestId] = BetRecord({
player: msg.sender,
amount: msg.value,
gameId: gameId,
randomResult: 0,
settled: false,
payout: 0,
});
}
}
Off-chain Commit-Reveal реалізація
class ProvablyFairEngine {
async createSession(userId: string) {
const serverSeed = crypto.randomBytes(32).toString("hex");
const serverSeedHash = crypto.createHash("sha256").update(serverSeed).digest("hex");
return await db.createSession({
userId,
serverSeed: this.encrypt(serverSeed),
serverSeedHash,
nonce: 0,
});
}
async resolve(sessionId: string, clientSeed: string, gameType: string) {
const session = await db.getSession(sessionId);
const serverSeed = this.decrypt(session.serverSeed);
const nonce = ++session.nonce;
const hashHex = crypto
.createHmac("sha256", serverSeed)
.update(`${clientSeed}-${nonce}`)
.digest("hex");
const rawResult = parseInt(hashHex.slice(0, 8), 16);
return this.applyGameLogic(gameType, rawResult);
}
async rotateSession(sessionId: string) {
const session = await db.getSession(sessionId);
const serverSeed = this.decrypt(session.serverSeed);
// Розкриваємо публічно—тепер всі ігри верифіковані
await db.revealServerSeed(sessionId, serverSeed);
await this.createSession(session.userId);
}
}
Верифікація на стороні гравця
Гравець повинен мати UI для самостійної верифікації:
function verifyGameResult(
serverSeed: string,
clientSeed: string,
nonce: number,
gameType: string,
claimedResult: string
): boolean {
const hmac = createHmac("sha256", serverSeed);
hmac.update(`${clientSeed}-${nonce}`);
const hash = hmac.digest("hex");
const rawValue = parseInt(hash.slice(0, 8), 16);
const calculatedResult = applyGameLogic(gameType, rawValue);
return calculatedResult.outcome === claimedResult;
}
Bankroll управління для чесного казино
Provably fair не тільки про randomness — але й про прозорість bankroll. Публічні метрики:
interface PublicCasinoStats {
totalBetsCount: number;
totalWagered: bigint;
totalPaidOut: bigint;
currentBankroll: bigint;
theoreticalRTP: number;
actualRTP: number;
}
Публічність даних будує довіру. Гравець бачить що заявлений 97% RTP дійсно відповідає реальному.
Стек
| Компонент | Технологія |
|---|---|
| Smart contract | Solidity + Chainlink VRF |
| Off-chain engine | Node.js + TypeScript |
| DB | PostgreSQL |
| Frontend verifier | React |
Сроки
- Базові ігри з VRF (Dice, Coinflip, Crash): 4-6 тижнів
- Commit-reveal off-chain: 3-4 тижні
- Verification UI: 2-3 тижні
- 5-8 ігр: +4-8 тижнів
- Security audit: обов'язковий, 4-6 тижнів
- Итого: 3-5 місяців







