Общая картина была понятна, а вот способ конкретной реализации — нет. Какие-то коды, подписи, что, куда, зачем?
Несколько консультаций у автора оракула, в результате получилось объединить логику розыгрыша (реализована на PHP) с алгоритмом получения случайного числа.
- В момент старта турнира/раунда мы запрашиваем у оракула первую часть кода (R-code).
В этот момент нет информации ни о количестве игроков, ни о количестве призовых мест, ни о размере призовых выплат и вообще о существовании лотереи. Оракул посредством транзакции выдает персональный случайный код, который в дальнейшем может быть использован только один раз и только тем, кто его запросил. Кстати, R-code можно «закупить» (имеется в виду стоимость транзакции запроса + компенсация оракулу за ответную транзакцию, это сумма порядка $0.015 по текущему курсу, сам код выдается бесплатно) заранее сразу несколько, чтобы потом не ждать получения ответной транзакции. Я сделал небольшой регулярно пополняемый буфер в БД. - Турнир длится стандартно 60 блоков блокчейна Waves platform, на данный момент это примерно 1 час. Турнир считается состоявшимся и закрытым, если после 60 блоков в нем будет не менее двух билетов, иначе время активности турнира продлевается на следующие 60 блоков.
- Сразу после закрытия турнира мы формируем и отправляем дата транзакцию (за нее также платим комиссию примерно $0.005), при необходимости — несколько, в которой зафиксированы все условия розыгрыша и упорядоченный список игроков (билетов) из которого нам необходимо выбрать победителей.
- На данном этапе у нас уже есть первая часть кода (R-code) плюс ID дата транзакции (TXID). Мы отправляем их на подпись оракулу в виде конкатенации (R-code + TXID), опять платим комиссию+компенсацию. Оракул проверяет полученные данные на предмет уникальности и принадлежности, а в ответ посылает нам вторую часть кода (S-code) в формате sha256, которая и является отправной точкой для генератора случайных чисел.
- Чтобы получить случайное число, которое будет указывать на порядковый номер победившего билета, мы преобразовываем S-code из бинарных данных sha256 в шестнадцатиричное (HEX) представление. Затем из получившейся HEX строки, получаем число. Получаем остаток от деления получившегося числа на количество билетов (all_tickets) и прибавляем к результату 1 (чтобы получить цифру 1 до all_tickets). В итоге мы получаем порядковый номер победителя.
- Если по условиям розыгрыша победителей несколько, то повторяем предыдущие операции в количестве равном количеству призовых мест. При этом каждый раз удаляем из списка билет который уже выиграл и уменьшаем 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.
Комментарии (23)
mayorovp
30.05.2019 16:58Не очень понимаю смысл перевода байт в hex- или base64-представление, кодирования в ascii и последующее хеширования.
Не проще ли напрямую хешировать массив байт?
Earthsound Автор
30.05.2019 17:18Буду благодарен, если подскажете как в PHP из daf5802953dcb27f89972e38e8900b898733f6a613e6e1c6c5491362c1832596 получить число 1 без промежуточных преобразований
mayorovp
30.05.2019 17:21Не вижу связи между daf5802953dcb27f89972e38e8900b898733f6a613e6e1c6c5491362c1832596 и числом 1.
А если вы хотели загрузить набор байт в gmp — то вам нужна функция gmp_import
Earthsound Автор
30.05.2019 17:24В таком виде мы получаем данные от оракула и нам надо превратить их в порядковый номер очереди
mayorovp
30.05.2019 17:28Не важно в каком виде вы получаете данные от оракула — их всегда можно перевести к последовательности байт (в PHP, кажется, это называется "бинарная строка").
Дальше с этой строкой можно делать всё что вам нужно по алгоритму — импортировать в gmp и делить на число участников.
Earthsound Автор
30.05.2019 17:29Вы перечислили именно то, что делается и описано в статье.
mayorovp
30.05.2019 17:34Нет. Вы вместо бинарной строки используете её hex-представление. Которое мало того что в два раза больше — так ещё и неоднозначно.
Если кто-то решит повторить ваш алгоритм на другом языке программирования — ему придется бороться со следующими проблемами:
- hex-представление может использовать прописные буквы вместо строчных, что изменит его хеш;
- hex-представление может содержать пробелы, что изменит его хеш;
- hex-представление может начинаться с префикса "0x" или заканчиваться суффиксом "h", что изменит его хеш;
- сам факт, что нужно считать хеш не от набора байт, а от его hex-представления, неочевиден.
Earthsound Автор
30.05.2019 17:44Я поэтому и спросил, знаете ли вы возможность более простого преобразования bin to int посредством PHP? Минуя hex.
Возможно это упростило бы код и позволило бы избежать лишних операций.
В статье приведен пример конкретной реализации на PHP, на других языках никакой борьбы не будет, получаете bin и доступными средствами преобразовываете его в int, если есть возможность, то без hex
А в данном примере hex представление является однозначным, т.к. мы получаем его самостоятельно, а не из сторонних источников и формат нам известен.
Другими словами: было удобно сделать именно так. Если можно проще — с удовольствием исправлю.mayorovp
30.05.2019 18:01Я поэтому и спросил, знаете ли вы возможность более простого преобразования bin to int посредством PHP? Минуя hex.
Так я и ответил же: gmp_import, а дальше всё по вашему алгоритму.
В статье приведен пример конкретной реализации на PHP, на других языках никакой борьбы не будет, получаете bin и доступными средствами преобразовываете его в int, если есть возможность, то без hex
Увы, в таком случае получится другой результат. То есть провалидировать результаты вашей лотереи не получится.
Earthsound Автор
30.05.2019 18:47Я понял о чем вы!
Спасибо, подумаю над решением этого вопроса.
Хотя нет…
По большому счету суть не поменяется, результат останется прежним.mayorovp
30.05.2019 19:31Как он останется прежним, когда
sha256("1234")
не равноsha256("\x12\x34")
?Earthsound Автор
30.05.2019 19:42Пробуем валидацию на сторонних ресурсах?
На сайте gchq.github.io/CyberChef/#recipe=SHA2('256') в INPUT даем наш S-code, в приведенном примере это:
Ri89jHB4UXZDXY6gT1m4LBDXGMTaYzHozMk4nxiuqVXdC
На выходе получаем:
daf5802953dcb27f89972e38e8900b898733f6a613e6e1c6c5491362c1832596
Дополняем 0x и отправляем на сайт defuse.ca/big-number-calculator.htm в виде:
(0xdaf5802953dcb27f89972e38e8900b898733f6a613e6e1c6c5491362c1832596 % 7 ) + 1
Получаем: 4
Как и в приведенном примере.mayorovp
30.05.2019 20:10Это вы хеш hex-представления посчитали. А теперь добавляем перед sha256 шаг fromhex (он нужен потому что сайт не позволяет удобно ввести бинарную строку) — и получаем вместо вашего daf5802953dcb27f89972e38e8900b898733f6a613e6e1c6c5491362c1832596 какой-то 98f358536272a4550fdea5dbb1020a305b8ac5e38b1ec5af920f250d8a38f064.
Earthsound Автор
30.05.2019 21:28Приведите пожалуйста последовательность действий и результаты каждого действия начиная с:
Ri89jHB4UXZDXY6gT1m4LBDXGMTaYzHozMk4nxiuqVXdCmayorovp
30.05.2019 21:48fromhex, sha256
Earthsound Автор
30.05.2019 22:42-1всё равно не могу понять, зачем fromhex?
какую строку сайт не дает удобно ввести?
У нас есть Ri89jHB4UXZDXY6gT1m4LBDXGMTaYzHozMk4nxiuqVXdC — ее мы и вставляем на сайте.
vassabi
Earthsound Автор
нечетные номера в какой момент? очередь уменьшается и индексируется заново при каждой итерации. Т.е. если изначально билет 145 был в очереди четным, то после определения первого победителя, он станет нечетным (не по номеру билета, а по порядку в очереди).
А почему на начало будет большее предпочтение?
mayorovp
sha256 — криптостойкая функция, что означает, в том числе, равномерное распределение результата для любого практически достижимого способа получения исходных данных
vassabi
т.е. не предыдущее число, а sha256 от предыдущего числа?
Earthsound Автор
да, если быть точным, то sha256 от предыдущего числа