Небезопасная случайность и Chainlink VRF
В детерминированной машине нет «настоящей» случайности — а псевдослучайность здесь предсказуема.
Ончейн-«рандом» на основе данных блока (
block.timestamp,blockhash,block.prevrandao) детерминирован и/или поддаётся влиянию, поэтому небезопасен для розыгрышей и лотерей с деньгами.
Почему блокчейн не умеет в случайность сам
EVM детерминирована: все валидаторы должны получить одинаковый результат, иначе консенсус невозможен. Значит, любая «случайная» величина обязана быть вычислимой из данных блока — а раз так, её может вычислить и кто угодно ещё. Использовать block.timestamp или blockhash как источник случайности для распределения призов — значит дать предсказуемый результат тому, кто эти данные тоже видит.
// УЯЗВИМО: предсказуемая «случайность»
function pickWinner() external view returns (uint) {
// значения известны до/во время формирования блока
return uint(keccak256(abi.encodePacked(block.timestamp, blockhash(block.number - 1)))) % players;
}Две беды: предсказуемость и влияние
Во-первых, предсказуемость: участник может в той же транзакции вычислить «выигрышный» исход и участвовать только тогда, когда выигрывает (через контракт, который откатывает невыгодные попытки). Во-вторых, влияние: тот, кто формирует блок, в некоторых схемах способен слегка подстроить входные данные (например, выбрать, включать ли транзакцию), смещая исход в свою пользу. Оба эффекта — не теоретические: на «самодельном» рандоме регулярно теряют средства лотереи и NFT-минты.
Правильное решение: проверяемая случайность (VRF)
Chainlink VRF (Verifiable Random Function) даёт случайное число вместе с криптографическим доказательством, что оно сгенерировано честно и не подстроено. Схема двухшаговая: контракт запрашивает случайность, а оракул в следующей транзакции возвращает число с доказательством, которое контракт проверяет. Поскольку результат приходит позже и сопровождается доказательством, его нельзя предугадать в момент участия и нельзя подделать.
// БЕЗОПАСНЕЕ: запрос/ответ с доказательством
// 1) requestRandomWords() -- просим случайность
// 2) fulfillRandomWords(requestId, words) -- оракул отдаёт число + доказательство
// контракт проверяет доказательство и только тогда выбирает победителяКак работает под капотом
VRF использует пару ключей оракула: по приватному ключу и входу-сидy вычисляется число и доказательство; контракт публичным ключом проверяет, что число соответствует входу и подписи. Никто (включая сам оракул) не может выбрать «удобное» число, не нарушив доказательство. Ключевой принцип безопасности любой случайности: исход не должен быть известен или управляем в момент, когда делаются ставки.
Частые ошибки
block.timestamp/blockhashкак рандом для денег. Предсказуемо и/или поддаётся влиянию.- Решать исход в той же транзакции, где участник может «отменить» проигрыш.
- Доверять одному непроверяемому источнику случайности.
Итоги
- EVM детерминирована: ончейн-данные блока непригодны как честный рандом.
- Опасности — предсказуемость для участников и влияние формирующего блок.
- Решение — проверяемая случайность (Chainlink VRF) с доказательством честности.
- Принцип: исход не должен быть известен/управляем в момент ставок.