Как мы можем генерировать несколько случайных чисел в эфириуме?
Я хочу, чтобы мой умный контракт возвращал 7 или 8 уникальных случайных чисел в диапазоне от 1 до 100 при вызове контракта. Какой может быть лучший подход для получения такого результата?
2 ответа
Вероятно, если вы пытаетесь построить рулетки, лотереи и карточные игры с использованием блокчейна Ethereum, поскольку блокчейн Ethereum является детерминированным, это создает определенные трудности для тех, кто решил написать собственный генератор псевдослучайных чисел (PRNG).
Некоторые уязвимые методы, используемые в настоящее время
Если в качестве источника энтропии вы используете блочные переменные, такие как block.coinbase, block.difficulty, block.timestamp и т. Д., То все эти блочные переменные могут обрабатываться майнерами, поэтому они не могут использоваться в качестве источника энтропии из-за стимул шахтеров. Поскольку переменные блока, очевидно, совместно используются в одном и том же блоке, вы можете легко использовать внутренние сообщения для получения одинакового результата.
Другие методы подобны использованию блочного хэша текущего или некоторого прошлого блока или блочного хэша прошлого блока в сочетании с частным начальным числом. В этих случаях используется функция block.blockhash (block.number). Однако в момент выполнения транзакции в EVM хеш-блок создаваемого блока еще не известен по очевидным причинам, и EVM всегда будет давать ноль. Если мы пытаемся сделать это с помощью хэша предыдущего блока, злоумышленник может заключить контракт на использование с тем же кодом, чтобы вызвать целевой контракт с помощью внутреннего сообщения. "Случайные" числа для двух контрактов будут одинаковыми.
Даже если мы объединяем blockhash с частным начальным числом, являясь прозрачным по своей природе, блокчейн не должен использоваться для хранения секретов в незашифрованном виде. Это тривиально, чтобы извлечь значение указателя частной переменной из хранилища контракта и предоставить его в качестве аргумента на подвиг.
Некоторые районы, которые стоит изучить
- Внешние оракулы
- Signidice
- Фиксация - выявить подход
Благодаря внешним оракулам, таким как Oraclize, интеллектуальные контракты могут запрашивать данные из веб-API, такие как курсы валют, прогнозы погоды и цены на акции (например, random.org) . Основной недостаток этого подхода в том, что он централизован. Будет ли Oraclize вмешиваться в результаты демона? Можем ли мы доверять random.org?
Вместо Oraclize мы также можем использовать BTCRelay, который является мостом между блокчейнами Ethereum и Bitcoin. Используя BTCRelay, умные контракты в блокчейне Ethereum могут запрашивать будущие биткойны и использовать их в качестве источника энтропии.
Signidice - это алгоритм, основанный на криптографических сигнатурах, который можно использовать для генерации случайных чисел в смарт-контрактах, в которых участвуют только две стороны: игрок и дом. Алгоритм работает следующим образом:
- Игрок делает ставку, вызывая умный контракт.
- Дом видит ставку, подписывает ее своим закрытым ключом и отправляет подпись в смарт-контракт.
- Смарт-контракт проверяет подпись с использованием известного открытого ключа.
- Эта подпись затем используется для генерации случайного числа.
Подход фиксации-раскрытия состоит из двух этапов:
- Стадия "фиксации", когда стороны представляют свои секретно защищенные секреты в смарт-контракт.
- Стадия "раскрытия", когда стороны объявляют семена открытого текста, умный контракт проверяет их правильность и семена используются для генерации случайного числа.
Лучшей реализацией подхода фиксации и раскрытия является Randao. Коммит-раскрытие может быть объединен с будущими блок-хэшами, чтобы сделать его более безопасным
Это в значительной степени охватывает все методы генерации случайных чисел с использованием Ethereum.
Как сказал Рагхав, случайные числа в блокчейне сложны. Публичный характер сети делает очень трудным создание числа, которое не может быть предварительно рассчитано.
С учетом сказанного, одним из лучших решений является использование оракула, который получает случайное число из внешнего источника (читай: не на основе блокчейна). Посмотрите на это руководство. Ethtroll Dapp является хорошим примером этого, так что взгляните на код здесь. Они используют Oraclize, чтобы получить случайное число от Random.org.
Проблема с использованием оракула является фактором централизации. Если вы настроили свой Dapp так, как я описал выше, вы попадаете в зависимость от сотрудника Rouge в двух разных централизованных сервисах - Oraclize и Random.org. Хотя вряд ли кто-то будет манипулировать каким-либо из этих источников, люди будут совершать иррациональные действия для получения потенциальной экономической выгоды.
Используйте VRF Chainlink.
Есть ряд проблем с использованием хэша блоков или подобного метода случайного заполнения. Если злоумышленник знает хэш блока до вашего контракта, он может использовать эту информацию для получения злонамеренного преимущества в том, что вы пытаетесь сделать. Здесь могут помочь оракулы, но они являются основным источником неудач и должны быть в состоянии доказать, что они случайны.
У вас должна быть сеть оракулов, которая может:
- Докажите, что генерируемые числа случайны.
- Имейте достаточно оракулов / узлов, и даже если один из них выйдет из строя / поврежден, ваш смарт-контракт сохранится.
На этот раз в примере ниже показано, как решить №1. Вы можете решить №2, выбрав из достаточного количества узлов, поддерживающих Chainlink VRF.
Для точной реализации см. Ответ на аналогичный вопрос.
Вы захотите сделать запрос к узлу с функцией, которая принимает сгенерированное вами семя:
function rollDice(uint256 userProvidedSeed) public returns (bytes32 requestId) {
require(LINK.balanceOf(address(this)) > fee, "Not enough LINK - fill contract with faucet");
uint256 seed = uint256(keccak256(abi.encode(userProvidedSeed, blockhash(block.number)))); // Hash user seed and blockhash
bytes32 _requestId = requestRandomness(keyHash, fee, seed);
emit RequestRandomness(_requestId, keyHash, seed);
return _requestId;
}
И когда значение будет возвращено, вы измените его на 100 и прибавите 1. Вам нужно будет вызвать это 7 или 8 раз, если вы хотите 7 или 8 случайных чисел.
function fulfillRandomness(bytes32 requestId, uint256 randomness) external override {
uint256 d6Result = randomness.mod(100).add(1);
emit RequestRandomnessFulfilled(requestId, randomness);
}
У меня есть идея для мозгового штурма, может, кому-нибудь поможет.
Это упрощенный подход фиксации-раскрытия только с одним участником. Для каждого случайного поколения потребуется название. Этот атрибут должен быть стандартным и легко поддающимся аудиту.
Сначала я совершаю коммит ("Лотерея Алисы") в smartContract. Если заголовок повторяется (проверьте хеши), он будет отклонен. И для раскрытия потребуется дождаться подтверждения хотя бы одного дополнительного блока, эти 2 блока должны поступать от разных майнеров, чтобы гарантировать, что майнер не атакует этот смарт-контракт.
А затем вы выполняете Reveal("Лотерея Альберто"). Здесь происходит волшебство; Источниками для random будут заголовок, msg.sender, block.blockhash блока фиксации и block.blockhash(commitBlockNumber+1), потому что никто не может предсказать будущий хеш, ни какой майнер его обнаружит [вы можете добавить coinbase или отметка времени также, чтобы получить больше случайного значения]. Также вы можете проверить, слишком ли близки или слишком разделены временные метки commitBlockNumber и commitBlockNumber + 1, это может указывать на то, что какой-то майнер пытается принудительно заблокировать какой-то блок, поэтому вы можете отклонить эту лотерею.
И, конечно, если вы можете наблюдать слишком много близких транзакций с помощью коммитов вроде ("Лотерея Алисы") || ("A лотереи Алисы") вы можете исследовать, что это лотерея обманули. Также вы можете сделать это с более чем 2 "интервальными" блоками.