Використовувати block.timestamp, block.prevrandao або хеш попереднього блоку для генерації випадкових чисел — ризиковано: майнер або валідатор може впливати на ці значення. Кілька років тому відома лотерея втратила $4M через маніпуляцію блочним хешем. Випадкові числа на блокчейні — нетривіальна задача, що потребує індивідуального підходу. Ми розробляємо системи RNG під ключ, оцінюємо загрози та пропонуємо оптимальне рішення. Наш досвід налічує понад 50 успішних проєктів у цій сфері.
Чому on-chain рандом складний?
Блокчейн детермінований. Кожен вузол повинен прийти до одного результату, виконуючи одні й ті самі операції. Це фундаментально суперечить випадковості: якщо результат передбачуваний — він не випадковий. Будь-яке джерело, яке видно в блокчейні до фіксації результату, може бути використане атакуючим.
Validator bias — валідатор на Ethereum бачить block.prevrandao (RANDAO reveal) до публікації блоку. Якщо результат невигідний — він може пропустити свій слот (слот пропускається, результат змінюється). Вартість атаки = втрачена винагорода за слот (~0.01 ETH). Якщо ставка в лотереї > 0.01 ETH — атака раціональна.
Chainlink VRF: стандарт для більшості випадків
Chainlink VRF (Verifiable Random Function) — найбільш перевірене рішення для NFT-мінту, лотерей, ігрової механіки. Chainlink VRF працює через оракульну мережу:
- Контракт запитує випадкове число, надсилаючи LINK.
- Chainlink-нода генерує випадкове число та криптографічний доказ.
- Доказ верифікується on-chain перед використанням числа.
// VRF V2.5 (актуальна версія)
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 Lottery is VRFConsumerBaseV2Plus {
uint256 public s_subscriptionId;
bytes32 public keyHash; // gas lane
uint32 public callbackGasLimit = 200_000;
uint16 public requestConfirmations = 3;
mapping(uint256 => address) public requestToPlayer;
function requestRandomWinner() 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;
}
function fulfillRandomWords(
uint256 requestId,
uint256[] calldata randomWords
) internal override {
address player = requestToPlayer[requestId];
uint256 result = randomWords[0] % totalTickets;
_declareWinner(player, result);
}
}
requestConfirmations: 3 — чекаємо 3 блоки підтвердження перед генерацією. Це ускладнює reorg-атаки на request.
Обмеження VRF: latency 1-3 блоки (15-45 секунд на mainnet), вартість LINK на кожен запит (0.25-2 LINK залежно від мережі), необхідність subscription менеджменту. Для high-frequency gameplays (кожен хід у грі потребує рандому) — занадто дорого та повільно.
Commit-reveal: рандом без оракула
Для випадків без доступу до Chainlink або при потребі мінімізувати витрати — commit-reveal схема:
Слабке місце commit-reveal: останній, хто розкриває secret, бачить фінальний результат до публікації. Він може вибрати не розкривати (griefing) або розкрити лише якщо результат вигідний. Mitigation: штраф за не-reveal (bond при commit, який згорає при неявці).
Приклад реалізації з бондами
mapping(address => bytes32) public commits;
mapping(address => uint256) public bonds;
uint256 public bondAmount = 0.1 ether;
function commit(bytes32 commitment) external payable {
require(msg.value == bondAmount);
commits[msg.sender] = commitment;
bonds[msg.sender] = msg.value;
}
function reveal(uint256 secret) external {
require(keccak256(abi.encode(secret, msg.sender)) == commits[msg.sender]);
// process secret
payable(msg.sender).transfer(bonds[msg.sender]); // повернення застави
delete bonds[msg.sender];
}
function claimBond(address participant) external {
require(bonds[participant] > 0);
// перевірка, що учасник не розкрив вчасно
// перевести заставу штрафуючому
}
RANDAO: нативний Ethereum рандом після Merge
Після переходу на PoS Ethereum надає block.prevrandao — агрегований RANDAO reveal від валідаторів. Це краще, ніж старий block.difficulty, але має описану вище проблему validator bias.
Для некритичних застосувань (косметика в грі, порядок у черзі, невеликі лотереї) — block.prevrandao достатній та безкоштовний:
uint256 random = uint256(keccak256(abi.encode(
block.prevrandao,
block.timestamp,
msg.sender,
nonce++
)));
Додавання msg.sender та nonce збільшує entropy та ускладнює передбачення для конкретного користувача, хоча не усуває validator bias.
Як вибрати відповідний метод RNG?
| Застосування | Ставка / цінність | Рекомендація |
|---|---|---|
| NFT мінт (whitelist рандом) | Висока | Chainlink VRF |
| Лотерея з великим призом | Висока | Chainlink VRF + requestConfirmations: 5+ |
| Внутрішньоігровий рандом (предмети) | Середня | Commit-reveal або Chainlink VRF |
| Порядок у черзі | Низька | block.prevrandao |
| PvP матчмейкінг | Низька | block.prevrandao + nonce |
Порівняння методів
| Метод | Безпека | Швидкість | Вартість | Комплексність |
|---|---|---|---|---|
| Chainlink VRF | Висока | 1-3 блоки | 0.25-2 LINK на запит | Середня |
| Commit-reveal | Середня (залежить від механік) | 2+ раунди | Тільки газ | Висока |
| RANDAO | Низька (validator bias) | 0 блоків | Безкоштовно | Низька |
| Гібрид (off-chain + on-chain) | Висока | 0 блоків | Комбінація | Висока |
Гібридні рішення
Для gamefi-проєктів, де потрібен швидкий рандом з високою throughput, використовуємо off-chain VRF з on-chain commitment:
- Backend генерує seed через Chainlink VRF заздалегідь.
- Hash seed публікується on-chain (commitment).
- При кожній ігровій події — використовуємо HMAC(seed, event_id) як рандом.
- Після сесії — розкриваємо seed, користувачі можуть верифікувати всі результати.
Це дає instant response на кожну дію та повну верифікованість постфактум. Гібридне рішення економить до 90% газу порівняно з прямими VRF запитами, а при високочастотному використанні — в 5-10 разів дешевше.
Що ви отримуєте
- Аналіз загроз та вибір оптимальної схеми.
- Інтегрований смарт-контракт з покриттям юніт-тестами (понад 200 тестів).
- Документацію з розгортання та використання.
- Підтримку при деплої та моніторингу.
- Навчання команди (опціонально).
Гарантуємо прозорість — всі рішення верифіковані на блокчейні. Зв'яжіться з нами для оцінки вашого проєкту.
Процес роботи
Аналітика. Визначаємо threat model: хто може атакувати? Яка максимальна вигода від маніпуляції? Яка latency допустима? Чи є доступ до Chainlink на цільовому чейні.
Розробка та тестування. VRFConsumer тестуємо через VRFCoordinatorV2_5Mock з пакету Chainlink — дозволяє симулювати fulfillment в unit-тестах без реальної оракульної мережі. Commit-reveal тестуємо на сценарії griefing та last-revealer атак.
Деплой. Для Chainlink VRF — створюємо subscription, фондуємо LINK, додаємо consumer. Налаштовуємо моніторинг балансу subscription.
Орієнтири за термінами
Інтеграція Chainlink VRF в існуючий контракт: 1-2 дні. Система RNG з commit-reveal та anti-griefing: 1-2 дні. Гібридний off-chain VRF з on-chain commitment та верифікацією: 3-5 днів.
Отримайте консультацію — оцінимо ваш проєкт та запропонуємо оптимальне рішення під ключ.







