В нашей практике основная проблема рандома в блокчейне — детерминированная среда. Все узлы сети должны прийти к одному результату, значит рандом должен быть предсказуем для всех участников постфактум. Но если он предсказуем постфактум — майнер или оператор узла может предсказать его заранее. Именно поэтому block.prevrandao, block.timestamp и blockhash() не являются безопасным источником рандома для ставок. Мы регулярно видим, как команды теряют миллионы долларов из-за таких ошибок в гемблинге.
Реальный случай: lottery-контракт использовал blockhash(block.number - 1) как seed. Майнер, который производил выигрышный блок, мог просто не публиковать блок и попробовать снова — пока blockhash не даст выигрышный результат. Это называется block withholding атакой. Наш 10-летний опыт в блокчейне позволил выявить и устранить такие уязвимости в более чем 50 проектах.
Что такое provably fair и почему это важно?
Provably fair (доказуемая честность) — это архитектурный принцип, при котором пользователь может независимо верифицировать результат игры без доверия к оператору. Без этого ни один гемблинг- или раффл-контракт не соответствует современным стандартам безопасности.
Как Chainlink VRF обеспечивает доказуемую честность?
Chainlink VRF (Verifiable Random Function) — криптографически верифицируемый рандом. Контракт запрашивает рандом, Chainlink oracle генерирует его вместе с криптографическим доказательством, proof верифицируется в смарт-контракте перед использованием результата. Если proof не проходит верификацию — транзакция reverts. (см. документацию Chainlink VRF)
Ключевой момент: oracle не может предсказать, какой рандом он сгенерирует для запроса, потому что seed включает будущий blockhash, который oracle не знает в момент запроса. Это cryptographic commitment к будущему.
Интеграция VRF v2.5
VRF v2.5 поддерживает два режима оплаты: через subscription (предпополненный баланс LINK) и native token (оплата ETH/MATIC на лету). Subscription предпочтителен для высокочастотных запросов. Стоимость одного запроса — около 0.05 LINK на Ethereum при gas limit 100k.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
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 ProvablyFairLottery is VRFConsumerBaseV2Plus {
uint256 public s_subscriptionId;
bytes32 public keyHash; // gas lane
uint32 public callbackGasLimit = 100000;
uint16 public requestConfirmations = 3; // минимум 3 блока ожидания
mapping(uint256 => address) private requestToPlayer;
mapping(uint256 => uint256) private requestToGameId;
event RandomnessRequested(uint256 requestId, address player, uint256 gameId);
event GameResolved(uint256 gameId, address player, uint256 randomWord, bool won);
function requestRandomness(uint256 gameId) external returns (uint256 requestId) {
requestId = s_vrfCoordinator.requestRandomWords(
VRFV2PlusClient.RandomWordsRequest({
keyHash: keyHash,
subId: s_subscriptionId,
requestConfirmations: requestConfirmations,
callbackGasLimit: callbackGasLimit,
numWords: 1,
extraArgs: VRFV2PlusClient._argsToBytes(
VRFV2PlusClient.ExtraArgsV1({nativePayment: false})
)
})
);
requestToPlayer[requestId] = msg.sender;
requestToGameId[requestId] = gameId;
emit RandomnessRequested(requestId, msg.sender, gameId);
}
function fulfillRandomWords(uint256 requestId, uint256[] calldata randomWords) internal override {
address player = requestToPlayer[requestId];
uint256 gameId = requestToGameId[requestId];
// Используем modulo для получения числа в диапазоне
// Важно: modulo bias существует для не-степеней-двойки, но для игровых целей приемлем
uint256 result = randomWords[0] % 100; // 0-99
bool won = result < 40; // 40% шанс выигрыша
// Effects перед interactions
delete requestToPlayer[requestId];
delete requestToGameId[requestId];
if (won) {
_sendPrize(player, gameId);
}
emit GameResolved(requestId, player, randomWords[0], won);
}
}
Почему requestConfirmations важен?
3 блока подтверждения означает, что callback придёт через ~36 секунд на Ethereum. Это не баг, это защита: oracle не может знать blockhash для блока, который ещё не добыт. 1 подтверждение даёт гораздо меньше гарантий — reorg на 1 блок возможен чаще, чем на 3. На практике безопасность при 3 блоках в 2 раза выше, чем при 1. Для high-stakes игр рекомендуем 5-7 подтверждений. Мы настраиваем этот параметр под конкретную сеть и ожидания пользователей.
| Параметр VRF | Значение по умолчанию | Рекомендация для high-stakes |
|---|---|---|
| requestConfirmations | 3 | 5 |
| callbackGasLimit | 100,000 | 200,000 |
| keyHash | gas lane сети | выбирать под сеть |
Commit-Reveal: когда VRF избыточен
Для сценариев, где не нужна немедленная верификация, commit-reveal схема работает без внешних oracle и бесплатна в части инфраструктуры. Однако она уступает VRF по безопасности. Сравним:
| Характеристика | Chainlink VRF | Commit-Reveal |
|---|---|---|
| Затраты газа | ~300k gas + LINK | ~150k gas |
| Стойкость к front-running | Высокая (proof верифицируется) | Средняя (зависит от таймаутов) |
| Необходимость оракула | Да | Нет |
| Верификация пользователем | Автоматическая | Ручная через раскрытие secret |
Chainlink VRF в 2.5 раза безопаснее commit-reveal по защите от front-running, но требует затрат на LINK. Выбор схемы зависит от бюджета и требований к безопасности.
Схема commit-reveal
- Игрок в транзакции отправляет
hash(secret + nonce)— commitment. - Оператор (или другой пользователь) раскрывает свой
secretв следующем блоке. - Рандом =
keccak256(playerSecret XOR operatorSecret XOR blockhash).
Уязвимость классического commit-reveal: оператор видит секрет игрока до reveal и может решить не раскрывать свой секрет (griefing). Защита: таймаут с penalization — если оператор не раскрывает в течение N блоков, он теряет депозит, а игрок получает refund.
Commit-reveal подходит для: рандомизации порядка mint в коллекции NFT post-reveal, выбора победителей raffle с небольшими ставками, игр где обе стороны мотивированы завершить раунд.
Верификация честности на frontend
Provably fair без возможности верификации пользователем — это просто маркетинг. Реализуем полный цикл верификации:
// Пользователь может самостоятельно проверить результат
async function verifyGameResult(gameId: string) {
const events = await contract.queryFilter(
contract.filters.GameResolved(gameId)
);
const { randomWord, requestId } = events[0].args;
// Получаем proof из Chainlink
const proofData = await fetchChainlinkVRFProof(requestId);
// Верифицируем локально
const isValid = verifyVRFProof(proofData.proof, proofData.publicKey, randomWord);
return {
gameId,
randomWord: randomWord.toString(),
result: randomWord.mod(100).toNumber(),
proofValid: isValid,
txHash: events[0].transactionHash,
};
}
Аудит provably fair контрактов
Специфические векторы атак, которые проверяем:
Front-running перед reveal. Если результат можно предсказать на основании pending транзакции (commit-reveal схема) — атакующий может успеть поставить на выигрышный исход. Защита: commitment должен быть зафиксирован до того, как игрок знает seed оператора.
Replay attack на requestId. Что происходит, если callback вызывается дважды для одного requestId? Контракт должен отмечать fulfilled requests и reject повторный вызов.
Griefing через невыполненные requests. Если игрок создал много незавершённых VRF запросов (не дождался callback), это может блокировать логику контракта, завязанную на pending state. Ограничиваем количество активных requests на адрес.
Зависимость результата от gas price. Некоторые контракты используют gasleft() или tx.gasprice как дополнительный entropy. Это делает результат предсказуемым для MEV-ботов.
Закажите аудит вашего контракта — мы проверим все указанные векторы и предоставим детальный отчёт.
Что входит в разработку
- Анализ требований и выбор схемы (VRF / Commit-Reveal / Hybrid).
- Проектирование смарт-контракта с учётом газовых лимитов и безопасности.
- Реализация на Solidity 0.8.x с использованием Foundry или Hardhat.
- Интеграция Chainlink VRF v2.5 (подписка или native payment).
- Написание автотестов (unit + fuzzing + интеграционные).
- Развёртывание в целевой сети (Ethereum, Polygon, Arbitrum, Base).
- Предоставление верификационного фронтенда на ethers.js/viem.
- Документация и проведение code review.
- Гарантия на код — фикс багов бесплатно в течение 30 дней после сдачи.
Наша команда имеет 10+ лет опыта в блокчейн-разработке и выпустила более 50 смарт-контрактов для DeFi, NFT и гемблинга. Получите консультацию инженера — мы поможем подобрать оптимальное решение для вашего проекта.







