Я хочу, чтобы мой смарт-контракт возвращал 7 или 8 УНИКАЛЬНЫХ случайных чисел в диапазоне от 1 до 100 при вызове контракта. Как лучше всего добиться такого результата?
Как мы можем сгенерировать несколько случайных чисел в эфириуме?
Ответы (4)
Вероятно, если вы пытаетесь создать рулетки, лотереи и карточные игры с использованием блокчейна Ethereum, поскольку блокчейн Ethereum является детерминированным, он создает определенные трудности для тех, кто решил написать свой собственный генератор псевдослучайных чисел (PRNG).
Некоторые используемые в настоящее время уязвимые методы
Если вы используете переменные блока, такие как block.coinbase, block.difficulty, block.timestamp и т. Д., В качестве источника энтропии, все эти переменные блока могут управляться майнерами, поэтому они не могут использоваться в качестве источника энтропии из-за поощрение майнеров. Поскольку переменные блока, очевидно, используются совместно в одном блоке, вы можете легко использовать внутренние сообщения для получения того же результата.
Другие методы похожи на использование блочного хэша текущего или некоторого прошлого блока или блочного хэша прошлого блока в сочетании с частным начальным значением. В этих случаях используется функция block.blockhash (block.number). Однако в момент выполнения транзакции в EVM хэш создаваемого блока еще не известен по очевидным причинам, и EVM всегда будет давать ноль. Если мы пробуем это с хешем предыдущего блока, злоумышленник может заключить контракт эксплойта с тем же кодом, чтобы вызвать целевой контракт через внутреннее сообщение. «Случайные» числа для двух контрактов будут одинаковыми.
Даже если мы объединим хеш-блок с частным семенем, будучи прозрачным по своей природе, цепочка блоков не должна использоваться для хранения секретов в виде открытого текста. Извлечь значение указателя частной переменной из хранилища контракта и передать его в качестве аргумента - тривиально. к эксплойту.
Некоторые области, которые стоит изучить
- Внешние оракулы
- Сигнидице
- Фиксировать – раскрыть подход
С помощью внешних оракулов, таких как Oraclize, смарт-контракты могут запрашивать данные из веб-API, такие как курсы обмена валют, прогнозы погоды и цены на акции (например, random.org). Ключевой недостаток этого подхода - централизация. Будет ли демон Oraclize вмешиваться в результаты? Можем ли мы доверять random.org?
Вместо Oraclize мы также можем использовать BTCRelay, который является мостом между блокчейнами Ethereum и Bitcoin. Используя BTCRelay, смарт-контракты в блокчейне Ethereum могут запрашивать будущие хэши биткойнов и использовать их в качестве источника энтропии.
Signidice - это алгоритм, основанный на криптографических подписях, который можно использовать для Генерация случайных чисел в смарт-контрактах с участием только двух сторон: игрока и дома. Алгоритм работает следующим образом:
- Игрок делает ставку, вызывая смарт-контракт.
- Дом видит ставку, подписывает ее своим закрытым ключом и отправляет подпись в смарт-контракт.
- Смарт-контракт проверяет подпись с помощью известного открытого ключа.
- Затем эта подпись используется для генерации случайного числа.
Подход «фиксация – раскрытие» состоит из двух этапов:
- Этап «фиксации», когда стороны передают в смарт-контракт свои криптографически защищенные секреты.
- Этап «раскрытия», когда стороны объявляют начальные числа в открытом виде, смарт-контракт проверяет их правильность, и начальные числа используются для генерации случайного числа.
Лучшая реализация подхода «фиксация-раскрытие» - Randao. Фиксация – раскрытие может быть объединена с будущими хешами блоков, чтобы сделать его более безопасным.
Это в значительной степени охватывает все методы генерации случайных чисел с использованием Ethereum.
Как сказал Рагхав, случайные числа в блокчейне сложны. Общедоступный характер сети очень затрудняет получение числа, которое невозможно рассчитать заранее.
С учетом сказанного, одним из лучших решений является использование оракула, который получает случайное число из внешнего (читай: не основанного на блокчейне) источника. Взгляните на это гид. Ethtroll Dapp является хорошим примером этого, поэтому взгляните на код здесь. Они используют Oraclize для получения случайного числа с Random.org.
Проблема с использованием оракула - фактор централизации. Если вы настроите свое Dapp так, как я описал выше, вы окажетесь во власти румян двух разных централизованных служб - Oraclize и Random.org. Хотя маловероятно, что кто-то сможет манипулировать любым из этих источников, люди будут совершать иррациональные действия ради потенциальной экономической выгоды.
Используйте Chainlink VRF.
Есть ряд проблем с использованием хэша блоков или подобного метода случайного заполнения. Если злоумышленник знает хэш блока до вашего контракта, он может использовать эту информацию для получения злонамеренного преимущества в том, что вы пытаетесь сделать. Здесь может помочь оракул, но они являются основным источником неудач и должны быть в состоянии доказать, что они случайны.
У вас должна быть сеть оракулов, которая может:
- Докажите, что генерируемые числа случайны.
- Имейте достаточно оракулов / узлов, и даже если один из них выйдет из строя / поврежден, ваш смарт-контракт сохранится.
На этот раз в приведенном ниже примере показано, как решить №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-мя интервалами.