Как мы можем генерировать несколько случайных чисел в эфириуме?

Я хочу, чтобы мой умный контракт возвращал 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. Имейте достаточно оракулов / узлов, и даже если один из них выйдет из строя / поврежден, ваш смарт-контракт сохранится.

На этот раз в примере ниже показано, как решить №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 "интервальными" блоками.

Другие вопросы по тегам