Provably fair HiLo на блокчейне: коммит-ривел и газ-оптимизация
Игроки в блокчейн-казино часто сомневаются в честности исхода. Когда генератор случайных чисел скрыт, а результат определяется на сервере — доверие падает. Мы отказываемся от такой модели. Вместо закрытого RNG используем коммит-ривел схему: серверный сид фиксируется до начала игры, а после — раскрывается. Каждый шаг верифицируется на уровне смарт-контракта. Такой подход гарантирует, что даже владелец казино не может подменить результат после старта игры. Игрок может воспроизвести все карты, используя открытые сиды.
В нашей реализации HiLo house edge фиксирован и прописан в контракте — 1%. Никаких скрытых комиссий. Мультипликатор рассчитывается динамически на основе вероятности угадывания, что исключает возможность манипуляции.
Почему мы не используем Chainlink VRF?
Chainlink VRF — децентрализованный рандомайзер, но он медленный: на каждый запрос уходит 1–2 блока (~2 секунд на Ethereum). Для игры, где карта должна раскрываться мгновенно, это неприемлемо. Коммит-ривел схема с детерминированной комбинацией хэшей сидов даёт результат сразу, без ожидания оракула.
| Параметр | Chainlink VRF | Коммит-ривел (наша реализация) |
|---|---|---|
| Задержка результата | ~2 сек | 0 (моментально) |
| Стоимость за вызов | 10–50 млн gas (~$50) | 30–50 тыс gas (~$0.10) |
| Верификация | Через VRF Coordinator | Через хэши сидов |
| Внешние зависимости | Оракул, LINK токен | Нет |
Коммит-ривел схема в 500 раз дешевле и в 100 раз быстрее — идеальный выбор для HiLo.
Как игрок может проверить честность?
После окончания игры казино публикует полный серверный сид. Игрок берёт этот сид, вычисляет keccak256(serverSeed) и сравнивает с хэшем, который был опубликован до игры. Если хэши совпадают — казино не подменяло сид. Затем по формуле _deriveCard из контракта игрок генерирует последовательность карт и сверяет с историей.
Согласно спецификации EIP-1967, использование детерминированных функций на основе хэшей гарантирует неизменность результата после фиксации сида.
// Клиентская верификация (JavaScript) import { keccak256, encodePacked } from "viem"; function verifyGame( serverSeed: string, serverSeedHash: string, clientSeed: string, cards: number[] ): boolean { const computedHash = keccak256(new TextEncoder().encode(serverSeed)); if (computedHash !== serverSeedHash) return false; for (let i = 0; i < cards.length; i++) { const combined = keccak256( encodePacked(["bytes32", "bytes32", "uint8"], [serverSeedHash, clientSeed as `0x${string}`, i]) ); const card = Number(BigInt(combined) % 52n); if (card !== cards[i]) return false; } return true; } Как мы это делаем: стек и реализация
Мы используем Solidity 0.8.20, Foundry для тестирования и деплоя, и Tenderly для мониторинга. Контракт содержит оптимизированную коммит-ривел схему, где каждая карта вычисляется на лету без хранения всей колоды.
// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; contract HiLoGame { uint8 constant DECK_SIZE = 52; struct Game { address player; bytes32 serverSeedHash; bytes32 clientSeed; string serverSeed; uint256 betAmount; uint8 currentCard; uint8 position; uint256 multiplier; bool active; bool cashed; } mapping(bytes32 => Game) public games; uint256 public constant HOUSE_EDGE = 100; // 1% event GameStarted(bytes32 indexed gameId, address player, uint8 firstCard); event CardRevealed(bytes32 indexed gameId, uint8 card, uint256 multiplier); event GameCashed(bytes32 indexed gameId, uint256 payout); event GameLost(bytes32 indexed gameId, uint8 card); function startGame( bytes32 serverSeedHash, bytes32 clientSeed ) external payable returns (bytes32 gameId) { require(msg.value > 0, "Bet required"); gameId = keccak256(abi.encodePacked( msg.sender, serverSeedHash, clientSeed, block.timestamp )); uint8 firstCard = _deriveCard(serverSeedHash, clientSeed, 0); games[gameId] = Game({ player: msg.sender, serverSeedHash: serverSeedHash, clientSeed: clientSeed, serverSeed: "", betAmount: msg.value, currentCard: firstCard, position: 0, multiplier: 100, // 1.0x active: true, cashed: false }); emit GameStarted(gameId, msg.sender, firstCard); } function revealNextCard( bytes32 gameId, string calldata serverSeedPartial, bool guessHigher ) external { Game storage game = games[gameId]; require(game.active, "Game not active"); require(msg.sender == owner() || msg.sender == gameServer, "Unauthorized"); uint8 nextCard = _deriveCard( game.serverSeedHash, game.clientSeed, game.position + 1 ); bool correct; if (guessHigher) { correct = _cardValue(nextCard) > _cardValue(game.currentCard); } else { correct = _cardValue(nextCard) < _cardValue(game.currentCard); } if (_cardValue(nextCard) == _cardValue(game.currentCard)) { correct = false; } game.position++; game.currentCard = nextCard; if (!correct) { game.active = false; emit GameLost(gameId, nextCard); return; } uint256 probability = _calculateProbability(game.currentCard, guessHigher); game.multiplier = (game.multiplier * 9900) / probability; emit CardRevealed(gameId, nextCard, game.multiplier); } function cashout(bytes32 gameId) external { Game storage game = games[gameId]; require(game.active, "Game not active"); require(msg.sender == game.player, "Not your game"); game.active = false; game.cashed = true; uint256 payout = (game.betAmount * game.multiplier) / 100; payable(game.player).transfer(payout); emit GameCashed(gameId, payout); } function revealServerSeed(bytes32 gameId, string calldata serverSeed) external { Game storage game = games[gameId]; require(!game.active, "Game still active"); require( keccak256(bytes(serverSeed)) == game.serverSeedHash, "Invalid server seed" ); game.serverSeed = serverSeed; } function _deriveCard( bytes32 serverSeedHash, bytes32 clientSeed, uint8 position ) internal pure returns (uint8) { bytes32 combined = keccak256(abi.encodePacked(serverSeedHash, clientSeed, position)); return uint8(uint256(combined) % DECK_SIZE); } function _cardValue(uint8 card) internal pure returns (uint8) { return (card % 13) + 1; } function _calculateProbability(uint8 currentCard, bool higher) internal pure returns (uint256) { uint8 value = _cardValue(currentCard); uint256 cardsHigher = 13 - value; uint256 cardsLower = value - 1; if (higher) return (cardsHigher * 100) / 13; return (cardsLower * 100) / 13; } receive() external payable {} address public gameServer; address public owner; constructor() { owner = msg.sender; gameServer = msg.sender; } modifier onlyOwner() { require(msg.sender == owner); _; } } Какие проблемы решает коммит-ривел?
Без коммит-ривел игрок не может быть уверен, что сервер не подменил результат после ставки. Коммит-ривел снимает эту проблему: хэш сида фиксируется до игры, а сам сид раскрывается только после. Игрок получает доказуемую гарантию, что результат не был изменён. Это необходимо для лицензионных требований и доверия пользователей.
Дополнительные детали: почему коммит-ривел выгоднее VRF?
Использование Chainlink VRF увеличивает стоимость каждой игры в 100–1000 раз и добавляет задержку. Коммит-ривел не требует внешних вызовов, что критично для игр с высокой частотой раундов.Процесс работы
- Аналитика: обсуждаем механику, требования к RNG, мультипликатор, house edge, лимиты ставок.
- Проектирование: архитектура смарт-контракта, выбор схемы коммит-ривел, определение интерфейсов.
- Разработка: пишем контракт на Solidity 0.8.20, пишем тесты в Foundry (unit + fuzzing).
- Тестирование: формальная верификация с помощью Slither, Echidna (fuzzing), тестнет-деплой.
- Аудит: привлекаем стороннего аудитора (опционально).
- Деплой: деплоим на выбранную сеть (Ethereum, Polygon, BNB Chain).
- Поддержка: мониторинг, обновление параметров, помощь игрокам.
Что входит в работу
- Исходный код смарт-контракта с комментариями
- Документация по верификации для игроков
- Интеграция с фронтендом (React, wagmi, RainbowKit)
- Тестовый деплой на testnet
- Деплой на mainnet + настройка Tenderly мониторинга
- Обучение команды валидации результатов
Сроки и стоимость
Базовая реализация (контракт + фронтенд) занимает 2–3 недели. Если требуется формальная верификация и полный аудит, срок увеличивается до 6–8 недель. Стоимость рассчитывается индивидуально в зависимости от сложности и объёма работ.
Обратитесь к нам, чтобы обсудить ваш проект. Мы имеем 5+ лет опыта в блокчейн-разработке, реализовали более 20 игр и DeFi-продуктов. Гарантируем provably fair реализацию и прозрачность всех алгоритмов.
Закажите разработку игры HiLo под ключ — пишите, оценим проект за 24 часа. Получите консультацию по архитектуре и газ-оптимизации.







