Как и обещал, напишу как работает генератор случайных чисел основанный на блокчейне и используемый в моем @lotobot.
Все началось со статьи на habrahabr, которую я увидел благодаря @habreplicator. :) Вот тут этот перепост habreplicator.
Я уже видел на steemit лотерейного бота, работающего по примерно той же схеме. Боту перечисляется один STEEM, а через 8 часов производится розыгрыш. Только непонятно было, как там генерируется случайное число. Да и я честно говоря не вдавался в подробности.
А тут почитал статью на хабре и меня подкупил принцип своей простотой, очевидностью и проверяемостью. Можно использовать сам блокчейн для генерации случайного числа. Каждый новый блок в цепочке блокчейна подписывается свидетелем иеговы, то есть witness. Вот так к примеру выглядит подпись блока c вот этим анонсом очередного раунда лотереи
"result": {
"previous": "004e86d48d7821f3eae7202828b51a2efc809b16",
"timestamp": "2017-04-15T09:58:24",
"witness": "creator",
"transaction_merkle_root": "defcd7c3832fed805d20afa670b427c148df4dc2",
"extensions": [],
"witness_signature": "201dfcc2a9ed3afb215883583590dfc1d1091e8f1a86ebd60af65ba6f31a8004af482efcfe7e440772a7dc2755ac0fc1d4d0b292a7f2cbc998c033af1020238372",
"transactions": [{
Как видно witness_signature содержит подпись делегата @creator. Это хеш сгенерированный из комбинации {содержимое блока + ключ делегата}. Так как каждый блок включает в себя сигнатуру предыдущего блока, то содержимое подписи делегата зависит так же и практически всей цепочки блоков.
Теперь если взять другой блок (на момент окончания раунда лотереи), то у него будет своя подпись, которая зависит от деятельности пользователей блокчейна. Любые посты, переводы монет, апвоты сделанные в течении раунда влияют на то, как будет выглядеть подпись делегата.
В своей лотереи пост с анонсом очередного раунда содержит permlink собранный особым образом.К примеру lottery-round-finalizer-149220678600. Где число это серверное время на момент создания очередного раунда. Таким образом по окончанию лотереи, когда 12-и часовой раунд заканчивается я без труда нахожу блок с анонсом и беру от туда подпись. А так же беру текущий блок и тоже из него беру подпись.
Обе подписи складываются и для полученного текста вычисляется keccak-256 хэш. Потом шестнадцатеричное представление хэша преобразуется в большое целое число которое в итоге делится по модулю, на число участников. Остаток от деления и есть счастливое число.
Вот так выглядит примерно вычисление в коде.
getLuckyNumber(participantsCount) {
let d = new hasher("keccak256");
d.update(this.startSig + this.endSig);
this.sha3 = d.digest().toString("hex");
return BigInt(this.sha3, 16).mod(participantsCount).value;
}
Для вычисления хэша и взятия по модулю я использовал npm библиотеки keccak и big-integer
Аналог я использовал для вебстраницы. Код страницы можно посмотреть на github-е здесь.
Недостатки
Пока что этот метод не идеален в том смысле, что я второй блок я выбираю случайно. На самом деле я не выбираю. А каждую минуту вызывается get_dynamic_global_parameters. Я беру от туда текущее серверное время и номер последнего блока. Если время прошедшее с начала раунда больше 12 часов, то я использую прочитанный блок в вычислениях. То-есть обладай я злым умыслом, я мог бы перебирать блоки в поисках нужного с подписью, с помощью которой я мог бы сгенерировать номер моего подставного лица.
Поэтому я думаю сейчас переделать на другой вариант. По окончании раунда будет создан пост извещающий об окончании приема ставок. Позже будут использованы подписи блока анонса раунда и блока с постом об окончании раунда. В этом случае я не смогу повлиять на результат лотереи. В этом случае только майнер сможет подобрать подпись. Только это вряд ли, так как майнеров несколько, неизвестно кому выпадет счастье подписывать именно тот блок, с постом @lotobot. Да и подобрать надо хеш за очень малое время.