Каковы эффективные и безопасные способы перетасовки колоды карт в контракте?

Мы хотим написать карточную игру на Ethereum. Каковы эффективные и безопасные способы перетасовки колоды карт в контракте и раздачи их игрокам? Это должно быть сделано таким образом, чтобы никто не мог определить карты друг друга и то, что представляет собой перетасованная колода, путем изучения кода контракта с открытым исходным кодом и перетасовки транзакций, которые находятся в общедоступной цепочке блоков Ethereum.

Если это невозможно в контракте, какие подходы возможны?

Пожалуйста, посмотрите этот вопрос о том, как создать случайное число ethereum.stackexchange.com/questions/191/…
Я думаю, что это достаточно существенно отличается, чтобы оправдать отдельный вопрос. Тасовать карты намного сложнее, чем простой ГСЧ. Посмотреть ментальный покер
Комментарий, указывающий на статью в Википедии о Mental Poker, действительно отвечает на ваш вопрос. Я собирался дать краткий обзор того, как мы тасуем карты, но, честно говоря, это именно то, что описано в той статье — и мое описание было написано менее четко.
@jimkberry, было бы полезно получить хороший ответ на этот вопрос, даже если это просто краткое изложение википедии. Может быть, добавить некоторые вещи, которые вы узнали из опыта, или какой-нибудь надежный код.
да, я надеюсь на то, что предлагает @TjadenHess
Допустимо ли для вашего варианта использования, если все знают, какие карты раздаются каким игрокам?
Нет, но, пожалуйста, не стесняйтесь сформулировать хороший вопрос и ответить на то, что вы обдумываете... Я также могу обновить этот заголовок, чтобы прояснить различия в наших вопросах, если вы его создадите.
В результате перетасовка/обработка карт выполняется в одноранговой сети с коммутативным шифрованием, а не с помощью контрактного кода. Я не знаю, каким образом вы могли бы иметь ненадежный единственный сервер (даже контракт Ethereum), чтобы он перетасовывал и сохранял секретность рук. Учитывая это - я не уверен, что то, что я должен сказать, на самом деле отвечает на вопрос @eth.
Это звучит как краткое изложение комментариев @jimkberry и Пайпер о том, что «это невозможно сделать в Эфириуме», что можно сделать в Эфириуме и что нужно сделать снаружи — вот ответ…
Это можно сделать на Ethereum, но большую часть работы делают сами игроки. Контракт может только подтвердить победителя в случае спора и управлять выплатами.

Ответы (3)

Перетасовка колоды


Коммутативное шифрование:

Алиса и Боб хотят перетасовать колоду карт так, чтобы ни один из них не знал, что лежит в руке другого, но эти руки не пересекаются (т. е. только одна может иметь конкретную карту).

Протокол:

Алиса и Боб выбирают протокол шифрования со следующими функциями.

  1. E K (X) — это X , зашифрованный ключом K
  2. D K [ E K (X) ] = X для всех K и X
  3. Е К [ Е J (X) ] = Е J [ Е К (Х) ]
    • т.е. E(X) коммутативно
  4. Для заданных X и E K (X) вычислительно невозможно вывести K
  5. Даны X и Y , J и K такие, что E J (X) = E K (Y) не могут быть найдены
    • т.е. E(x) устойчив к столкновениям

Теперь, когда у Алисы и Боба есть схема шифрования:

  1. Боб берет пятьдесят две карты ( «2C», «3C»,..., «AS» ) и шифрует каждую с помощью ключа B.
  2. Боб перемешивает зашифрованные карты в случайном порядке и отправляет колоду Алисе.
    • Алиса не видит карты, так как не знает B
  3. Алиса выбирает пять карт и отправляет их обратно Бобу.
    • Это рука Боба. Боб может видеть эти карты, так как он знает B
  4. Алиса отправляет Бобу еще пять карточек, но сначала шифрует их с помощью ключа А.
    • Каждая карта теперь зашифрована как EA [ EB ( X ) ] , что эквивалентно EB [ EA ( X ) ] по свойству № 4.
  5. Боб расшифровывает эти карты и отправляет их обратно
    • Боб не может видеть карты, так как он не знает А. Алиса, однако, может их видеть, и это ее рука.

Аналогичным образом можно взять больше карт. После игры Алиса и Боб раскрывают A и B , чтобы убедиться, что ни один из игроков не обманул.

Схемы шифрования с перечисленными свойствами действительно существуют, включая метод, аналогичный RSA. Смотрите мой источник для более подробной информации.


Использование в контрактах:

Контракты можно использовать для управления обменом сообщениями между игроками и для распределения вознаграждений. Контракт не должен выполнять какую-либо логику, если только игроки не согласны с результатами. В случае разногласий претендент должен поставить достаточно эфира, чтобы покрыть расходы на газ для проверки игры. Затем другой игрок должен выполнить вызов, чтобы проверить результат игры. Бензин возвращается правдивому игроку, а мошенник отменяет раунд или лишает его права.

Это называется ментальный покер.

Поскольку исходный код контрактов Ethereum может быть проверен, это должно быть так же просто, как использование ГСЧ для случайного выбора карт из колоды. Любой, кто хочет убедиться, что колода правильно перемешана, может проверить исходный код контракта.

Поскольку генерация хорошей случайности рассматривается в другом месте , этот ответ предполагает, что контракт перетасовки карт имеет доступ к хорошим случайным числам.

Использование случайности по запросу

Создать хорошо перетасованную колоду можно путем случайного выбора карт из неперетасованной колоды и создания новой колоды до тех пор, пока не будет исчерпана неперетасованная колода.

contract Deck {
   uint8[52] deck;

   function getRandomNumber() returns (uint) {
       ...;
   }

   function shuffle() {
      uint8[52] memory unshuffled;

      for (uint8 i=0; i < 52; i++) {
          unshuffled[i] = i;
      }

      uint cardIndex;

      for (i=0; i < 52; i++) {
          cardIndex = getRandomNumber() % (52 - i);
          deck[i] = unshuffled[cardIndex];
          unshuffled[cardIndex] = unshuffled[52 - i - 1]
      }
   }
}

Этот контракт представляет каждую из 52 карт как целое число от 0 до 51 включительно. Вызов shuffleфункции заполнит deckпеременную хранилища колодой из 52 карт в случайном порядке.

Реагирование на случайный ввод.

Это предполагает, что случайность может быть безопасно запрошена, что не всегда возможно. Если это не так, и вместо этого в контракт нужно отправить случайность, то следующая общая концепция должна работать одинаково хорошо.

contract Deck {
   uint8[52] deck;

   function shuffle(bytes randomBytes) {
      if (randomBytes.length < 52) throw;

      uint8[52] memory unshuffled;

      for (uint8 i=0; i < 52; i++) {
          unshuffled[i] = i;
      }

      uint8 cardIndex;

      for (i=0; i < 52; i++) {
          cardIndex = uint8(randomBytes[i]) % (52 - i);
          deck[i] = unshuffled[cardIndex];
          unshuffled[cardIndex] = unshuffled[52 - i - 1];
      }
   }
}

Эта версия контракта требует включения не менее 52 байт случайности в вызов shuffle.

Это отвечает на заданный вопрос, но я думаю, что подразумевалось, что карты будут тайно перетасовываться и раздаваться, так что игроки не могут видеть руки других игроков. Я думаю, что вопрос плохо сформулирован.
Спасибо за отзыв @TjadenHess. Надеюсь, я лучше разъяснил вопрос курсивом.
@eth: я не думаю, что ваш нынешний курсив что-то проясняет, неясно, должны ли люди иметь возможность тайно вытягивать карты без ведома других игроков.
«должно быть сделано таким образом, чтобы никто не мог определить [карты друг друга и] что такое перетасованная колода» хорошо, я добавил часть в [] (это проясняет, но не уверен, что это действительно необходимо)
В свете новых требований я склонен изменить свой ответ на «Это невозможно сделать на Ethereum, поскольку контракты не могут хранить секреты». Такое приложение достаточно сложное, поэтому я сомневаюсь, что это полезный вопрос, поскольку решение, вероятно, потребует координации через другие сети, такие как шепот. Кто-нибудь еще хочет высказаться по этому поводу?
В случаях, когда все знают, как тасуется колода, не возникает проблем, и в случаях, когда игра не требует полной колоды, я думаю, что очень расточительно тасовать 52 карты заранее (именно так происходит типичный правильный способ тасования). ). Карты следует тасовать по требованию, выполняя как можно меньше операций, чтобы получить достаточное количество перетасованных карт.
Похоже, вы правы @PiperMerriam: «решение, вероятно, потребует координации через другие сети». Я голосую за ваш ответ.
Для максимальной эффективности в игру, вероятно, следует играть на побочном канале, как в Whisper, но определенно можно играть в нее по цепочке. Контракт не видит карт до самого конца, он просто верит, что игроки честны. Карты раскрываются только в случае спора между игроками, а контракт подтверждает историю ходов.

Если вы хотите, чтобы карты не повторялись, то это довольно сложная задача. Более простая проблема заключается в том, что нам все равно, если одна и та же карта сдается несколько раз (например, у вас туз пик, а у меня туз пик).

Рассмотрим простую игру с двумя игроками. Каждому игроку раздается по 1 карте, и это нормально, что оба игрока могут получить одну и ту же карту. Это можно представить, как две карты раздаются из двух разных колод. Чтобы реализовать это, мы можем думать о каждой карте как об индексе от 0 до 51 (включительно). Вот как правильно сгенерировать карту игрока 1, чтобы только он знал, что это такое.

  1. Игрок 2 генерирует случайное число в диапазоне 0..51, называемое p2_offset. Затем он генерирует случайную строку с именем p2_secret (например, «pcjshjfgn»). Затем он отправляет sha3(p2_offset, p2_secret) в блокчейн.
  2. Игрок 1 генерирует случайное число в диапазоне 0..51, называемое p1_offset, и случайную строку p1_secret и отправляет sha3(p1_offset, p1_secret) в блокчейн.
  3. Игрок 2 раскрывает p2_offset и p2_secret контракту Ethereum, который проверяет, что sha3 этих значений соответствует тому, что он отправил ранее.
  4. Игрок 1 теперь может выяснить, какая у него карта, выполнив (p2_offset + p1_offset) мод 52. Обратите внимание, что игрок 2 не знает, что это за карты, потому что p1_offset ему неизвестен.
  5. Игрок 1 может раскрыть свою карту, указав в контракте p1_offset и p1_secret. Опять же, контракт проверит, что sha3 этих значений соответствует тому, что игрок 1 отправил ранее.

Тот же подход можно использовать для создания карты игрока 2.

Я думаю, что убедиться, что каждая карта появляется только один раз, довольно сложно. Мы могли бы сказать, что однажды