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

Как-то утром на глаза попалась статья о проверяемом генераторе случайных чисел на блокчейне Waves platform.

Общая картина была понятна, а вот способ конкретной реализации — нет. Какие-то коды, подписи, что, куда, зачем?

Несколько консультаций у автора оракула, в результате получилось объединить логику розыгрыша (реализована на PHP) с алгоритмом получения случайного числа.

  1. В момент старта турнира/раунда мы запрашиваем у оракула первую часть кода (R-code).

    В этот момент нет информации ни о количестве игроков, ни о количестве призовых мест, ни о размере призовых выплат и вообще о существовании лотереи. Оракул посредством транзакции выдает персональный случайный код, который в дальнейшем может быть использован только один раз и только тем, кто его запросил. Кстати, R-code можно «закупить» (имеется в виду стоимость транзакции запроса + компенсация оракулу за ответную транзакцию, это сумма порядка $0.015 по текущему курсу, сам код выдается бесплатно) заранее сразу несколько, чтобы потом не ждать получения ответной транзакции. Я сделал небольшой регулярно пополняемый буфер в БД.

  2. Турнир длится стандартно 60 блоков блокчейна Waves platform, на данный момент это примерно 1 час. Турнир считается состоявшимся и закрытым, если после 60 блоков в нем будет не менее двух билетов, иначе время активности турнира продлевается на следующие 60 блоков.
  3. Сразу после закрытия турнира мы формируем и отправляем дата транзакцию (за нее также платим комиссию примерно $0.005), при необходимости — несколько, в которой зафиксированы все условия розыгрыша и упорядоченный список игроков (билетов) из которого нам необходимо выбрать победителей.
  4. На данном этапе у нас уже есть первая часть кода (R-code) плюс ID дата транзакции (TXID). Мы отправляем их на подпись оракулу в виде конкатенации (R-code + TXID), опять платим комиссию+компенсацию. Оракул проверяет полученные данные на предмет уникальности и принадлежности, а в ответ посылает нам вторую часть кода (S-code) в формате sha256, которая и является отправной точкой для генератора случайных чисел.
  5. Чтобы получить случайное число, которое будет указывать на порядковый номер победившего билета, мы преобразовываем S-code из бинарных данных sha256 в шестнадцатиричное (HEX) представление. Затем из получившейся HEX строки, получаем число. Получаем остаток от деления получившегося числа на количество билетов (all_tickets) и прибавляем к результату 1 (чтобы получить цифру 1 до all_tickets). В итоге мы получаем порядковый номер победителя.
  6. Если по условиям розыгрыша победителей несколько, то повторяем предыдущие операции в количестве равном количеству призовых мест. При этом каждый раз удаляем из списка билет который уже выиграл и уменьшаем all_tickets на 1, а вместо S-code указываем предыдущее полученное число.

Разберем конкретный реальный пример, турнир №119:

Всего 7 билетов (all_tickets)
Стоимость билета 50 монет (Bet)
Игровой сбор 10% (Fee)

По условиям лотереи 30% попадают в призовые, т.е. в данном случае 2 билета должны получить приз, размер которого считается по формуле (Bet*all_tickets-Fee)/2.

1. Получили R-code: RdbAiAhKhveAtR4eyTKq75noMxdcEoxbE6BvojJjM13VE

2. После закрытия турнира имеем список билетов в виде пар: номер + адрес (адрес кошелька с которого была оплата участия в турнире). Заметим, что адреса могут повторяться, это означает, что один участник купил несколько билетов в один турнир, это не возбраняется правилами.

Отправили дата транзакцию: 82JTMzhHM5xEA2fQ9Qscd5QAJU3DAd8nShLjdVHTer5S

3. Запросили S-code: FTF3uRyaa4F2uAyD6z5a3CNbTXbQLc7fSR6CFNVjgZYV с комментарием (R-code + TXID):
RdbAiAhKhveAtR4eyTKq75noMxdcEoxbE6BvojJjM13VE 82JTMzhHM5xEA2fQ9Qscd5QAJU3DAd8nShLjdVHTer5S

4. Получили S-code: Ri89jHB4UXZDXY6gT1m4LBDXGMTaYzHozMk4nxiuqVXdC

5. Определили победителей.

6. Отправили выплаты

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

determine the winner № 1

All_tickets:
Index: 1 Ticket:139
Index: 2 Ticket:141
Index: 3 Ticket:143
Index: 4 Ticket:145
Index: 5 Ticket:147
Index: 6 Ticket:149
Index: 7 Ticket:151

1. bin -> hex ( bin2hex(sha256(S-code)) ): Ri89jHB4UXZDXY6gT1m4LBDXGMTaYzHozMk4nxiuqVXdC -> 0xdaf5802953dcb27f89972e38e8900b898733f6a613e6e1c6c5491362c1832596

2. hex -> gmp number: 0xdaf5802953dcb27f89972e38e8900b898733f6a613e6e1c6c5491362c1832596 -> 99037963059744689166154019807924045947962565922868104113173478160267437352342

3. gmp -> modulo (mod=7): 99037963059744689166154019807924045947962565922868104113173478160267437352342 -> 4

4. modulo -> ticket: 4 -> 145

determine the winner № 2

All_tickets:

Index: 1 Ticket:139
Index: 2 Ticket:141
Index: 3 Ticket:143
Index: 4 Ticket:147
Index: 5 Ticket:149
Index: 6 Ticket:151

1. bin -> hex ( bin2hex(sha256(previous hex)) ): daf5802953dcb27f89972e38e8900b898733f6a613e6e1c6c5491362c1832596 -> 0x9560e77525e9ea2db92cdb8484dc52046ccafac7c719b8859ff55f0eb92834a0
2. hex -> gmp number: 0x9560e77525e9ea2db92cdb8484dc52046ccafac7c719b8859ff55f0eb92834a0 -> 67565829218838067182838043983962684143266386786567427968312120473742580659360
3. gmp -> modulo (mod=6): 67565829218838067182838043983962684143266386786567427968312120473742580659360 -> 1
4. modulo -> ticket: 1 -> 139

End.

Источник: habr.com

Добавить комментарий