Разработка блокчейн-казино с provably fair

Проектируем и разрабатываем блокчейн-решения полного цикла: от архитектуры смарт-контрактов до запуска DeFi-протоколов, NFT-маркетплейсов и криптобирж. Аудит безопасности, токеномика, интеграция с существующей инфраструктурой.
Показано 1 из 1 услугВсе 1306 услуг
Разработка блокчейн-казино с provably fair
Сложная
от 2 недель до 3 месяцев
Часто задаваемые вопросы
Направления блокчейн-разработки
Этапы блокчейн-разработки
Последние работы
  • image_website-b2b-advance_0.png
    Разработка сайта компании B2B ADVANCE
    1258
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1170
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    873
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1092
  • image_logo-advance_0.png
    Разработка логотипа компании B2B Advance
    563
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    830

Разработка блокчейн-казино с provably fair

Provably fair — это математическая гарантия честности каждого результата, которую игрок может верифицировать самостоятельно. Не «доверяйте нам», а «проверьте математику». Это ключевое конкурентное преимущество blockchain казино над традиционным онлайн-гэмблингом, где игрок вынужден верить RNG сертификатам.

Как работает provably fair

Существует два основных подхода: VRF on-chain и commit-reveal off-chain.

Chainlink VRF — on-chain verifiable randomness

VRF (Verifiable Random Function) генерирует случайное число вместе с cryptographic proof того, что это число действительно случайное и не могло быть предсказано или изменено оператором.

Процесс:

  1. Контракт запрашивает randomness у Chainlink VRF
  2. Chainlink oracle генерирует число и proof
  3. On-chain: proof верифицируется, число принимается
  4. Любой может проверить: данный 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 после его раскрытия: hash уже опубликован. Игрок не может изменить client_seed: он зафиксирован в ставке. Результат детерминирован, но был непредсказуем до завершения раунда.

On-chain реализация с Chainlink VRF

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import {VRFConsumerBaseV2Plus} from "@chainlink/contracts/src/v0.8/vrf/dev/VRFConsumerBaseV2Plus.sol";
import {VRFV2PlusClient} from "@chainlink/contracts/src/v0.8/vrf/dev/libraries/VRFV2PlusClient.sol";

contract ProvablyFairCasino is VRFConsumerBaseV2Plus {
    // Каждый requestId → детали ставки
    struct BetRecord {
        address player;
        uint256 amount;
        uint8 gameId;
        bytes betParams;
        uint256 vrfRequestId;
        uint256 randomResult;
        bool settled;
        uint256 payout;
    }
    
    mapping(uint256 => BetRecord) public betRecords;
    
    // Публично доступная история: любой может верифицировать результаты
    event BetPlaced(
        uint256 indexed requestId,
        address indexed player,
        uint8 gameId,
        uint256 amount,
        bytes betParams
    );
    
    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");
        require(msg.value <= MAX_BET[gameId], "Above maximum");
        require(msg.value <= getMaxBet(), "Exceeds bankroll limit");
        
        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,
            betParams: betParams,
            vrfRequestId: requestId,
            randomResult: 0,
            settled: false,
            payout: 0,
        });
        
        emit BetPlaced(requestId, msg.sender, gameId, msg.value, betParams);
    }
    
    function fulfillRandomWords(
        uint256 requestId,
        uint256[] calldata randomWords
    ) internal override {
        BetRecord storage bet = betRecords[requestId];
        require(!bet.settled, "Already settled");
        
        bet.randomResult = randomWords[0];
        bet.settled = true;
        
        // Диспетчеризация по игровому типу
        uint256 payout = _resolveGame(bet.gameId, bet.amount, bet.betParams, randomWords[0]);
        bet.payout = payout;
        
        bool isWin = payout > 0;
        
        if (isWin) {
            payable(bet.player).transfer(payout);
        }
        
        // randomWords[0] публично в event — любой может верифицировать
        emit BetSettled(requestId, randomWords[0], payout, isWin);
    }
}

Off-chain commit-reveal реализация

class ProvablyFairEngine {
  // Генерация server seed для новой сессии
  async createSession(userId: string): Promise<Session> {
    const serverSeed = crypto.randomBytes(32).toString("hex");
    const serverSeedHash = crypto
      .createHash("sha256")
      .update(serverSeed)
      .digest("hex");
    
    const session = await db.createSession({
      userId,
      serverSeed: this.encrypt(serverSeed), // шифруем до окончания игры
      serverSeedHash,     // публикуем hash сразу
      nonce: 0,
      createdAt: new Date(),
    });
    
    // Возвращаем пользователю hash — он может его сохранить
    return { sessionId: session.id, serverSeedHash };
  }
  
  async resolve(
    sessionId: string,
    clientSeed: string,
    gameType: string,
    betAmount: number
  ): Promise<GameResult> {
    const session = await db.getSession(sessionId);
    const serverSeed = this.decrypt(session.serverSeed);
    
    // Генерируем результат
    const nonce = ++session.nonce;
    const combinedSeed = `${serverSeed}:${clientSeed}:${nonce}`;
    
    const hashHex = crypto
      .createHmac("sha256", serverSeed)
      .update(`${clientSeed}-${nonce}`)
      .digest("hex");
    
    // Конвертируем первые 8 символов hash в число
    const rawResult = parseInt(hashHex.slice(0, 8), 16);
    const gameResult = this.applyGameLogic(gameType, rawResult);
    
    await db.updateSession(sessionId, { nonce });
    
    // Сохраняем запись для будущей верификации
    await db.saveGameRecord({
      sessionId,
      nonce,
      clientSeed,
      serverSeedHash: session.serverSeedHash, // подтверждение
      gameType,
      result: gameResult.outcome,
      betAmount,
      payout: gameResult.payout,
    });
    
    return gameResult;
  }
  
  // Раскрытие server seed при смене сессии
  async rotateSession(sessionId: string): Promise<void> {
    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;
}

// Публичный verifier — любой может проверить
// 1. serverSeedHash === sha256(serverSeed)  ✓ (нельзя изменить retroactively)
// 2. result === applyLogic(hmac(serverSeed, clientSeed-nonce)) ✓

Верификационный UI

function VerificationPanel({ gameRecord }: { gameRecord: GameRecord }) {
  const [serverSeed, setServerSeed] = useState("");
  const [isVerified, setIsVerified] = useState<boolean | null>(null);
  
  const verify = () => {
    // Проверяем что server seed соответствует hash
    const hash = sha256(serverSeed);
    if (hash !== gameRecord.serverSeedHash) {
      setIsVerified(false);
      return;
    }
    
    // Воспроизводим результат
    const result = verifyGameResult(
      serverSeed,
      gameRecord.clientSeed,
      gameRecord.nonce,
      gameRecord.gameType,
      gameRecord.outcome
    );
    
    setIsVerified(result);
  };
  
  return (
    <div className="verification-panel">
      <p>Server Seed Hash: {gameRecord.serverSeedHash}</p>
      <p>Client Seed: {gameRecord.clientSeed}</p>
      <p>Nonce: {gameRecord.nonce}</p>
      
      {gameRecord.serverSeed ? (
        <>
          <p>Server Seed (revealed): {gameRecord.serverSeed}</p>
          <button onClick={verify}>Verify Result</button>
          {isVerified !== null && (
            <p>{isVerified ? "✓ Verified - Result is correct" : "✗ Verification failed"}</p>
          )}
        </>
      ) : (
        <p>Server seed revealed after session ends</p>
      )}
    </div>
  );
}

Bankroll management для честного казино

Provably fair не только про randomness — но и про прозрачность bankroll. Публично публикуемые метрики:

interface PublicCasinoStats {
  totalBetsCount: number;
  totalWagered: bigint;
  totalPaidOut: bigint;
  currentBankroll: bigint;
  theoreticalRTP: number;       // заявленный RTP
  actualRTP: number;            // реальный за все время
  biggestWin: { amount: bigint; txHash: string; date: Date };
  recentResults: Array<{
    gameType: string;
    result: string;
    serverSeedHash: string;
  }>;
}

Публичность этих данных — дополнительное доверие. Игрок видит что заявленный 97% RTP действительно соответствует реальному.

Стек

Компонент Технология
Smart contract Solidity + Chainlink VRF v2.5
Off-chain engine Node.js + TypeScript
DB PostgreSQL (game records)
Шифрование AES-256-GCM (server seeds)
Frontend verifier React
Monitoring Grafana (RTP, bankroll)

Сроки

  • Базовые игры с VRF (Dice, Coinflip, Crash): 4-6 недель
  • Commit-reveal off-chain engine: 3-4 недели
  • Верификационный UI: 2-3 недели
  • 5-8 игр: добавить 4-8 недель
  • Security audit: обязателен, 4-6 недель
  • Итого: 3-5 месяцев