Доказуемая честность и PoL в казино (и не только)

от автора

Приветствую читатели! В данном посте я поверхностно расскажу о доказуемой честности в казино, а так-же о Proof Of Liabilities.

О опыте

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

Модель работы казино

Во всех казино есть понятие — RTP(Return To Player), процент ставки, возвращаемый игроку на долгой дистанции. Данное значение обычно достигает 94-99%. То есть на каждый поставленный игроком доллар он будет получать 94-99 центов. Конечно же это все происходит на долгой дистанции, на короткой же игрок может быть в огромном плюсе или наоборот в огромном минусе. Но есть множество недобросовестных казино, которые не выплачивают средства или нагло подкручивают свои игры.

Доказуемая честность

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

Данная схема содержит в себе четыре части:

  • Server Seed

  • Client Seed

  • Nonce

  • Cursor

Давайте подробнее о каждой части

Server Seed — случайное число, которое сгенерировало казино. Игрок перед совершением ставки видит лишь хэш данного числа.

Client Seed — случайное число, которое генерирует юзер. Он может его менять в любой момент до совершения ставки.

Nonce — число, которое увеличивается с каждой ставкой. При сбросе server seed`a как правило становится 0.

Cursor — число, нужное если исход игры требует больше чисел, например для слотов. Если игра требует 1 число, то мы используем значение 0 в качестве курсора, если исход игры требует больше 8 чисел, то мы можем увеличить курсор на единицу.

Собрав все нужные нам данные мы можем получить 256 битное число, для этого используют HMAC_SHA256(server_seed + client_seed + nonce + cursor)

Получив 32 байта, мы можем их конвертировать в 8 чисел с плавающей запятой.

Имплементация данного алгоритма:

func GenerateFloats(bytes []byte) []float64 { var result []float64 for i := 0; i < len(bytes) / 4; i++ { var res float64 for j := 0; j < 4; j++ { divider := math.Pow(256, float64(j+1)) partialResult := float64(bytes[(i*4)+j]) / divider res += partialResult } result = append(result, res) } return result }

Таким образом мы можем далее это конвертировать в результат для какой-либо игры, допустим вытащить карту:

// Index of 0 to 51 : ♦2 to ♣A const CARDS = [    ♦2, ♥2, ♠2, ♣2, ♦3, ♥3, ♠3, ♣3, ♦4, ♥4,     ♠4, ♣4, ♦5, ♥5, ♠5, ♣5, ♦6, ♥6, ♠6, ♣6,    ♦7, ♥7, ♠7, ♣7, ♦8, ♥8, ♠8, ♣8, ♦9, ♥9,    ♠9, ♣9, ♦10, ♥10, ♠10, ♣10, ♦J, ♥J, ♠J,    ♣J, ♦Q, ♥Q, ♠Q, ♣Q, ♦K, ♥K, ♠K, ♣K, ♦A,    ♥A, ♠A, ♣A  ];   // Game event translation const card = CARDS[Math.floor(float * 52)];

Proof Of Liabilities

Данный алгоритм может применяться не только в гэмблинге, а так-же в централизованных криптобиржах(CEX), и других сервисах, принимающих депозиты.

За пример я взял данную имплементацию на JS, и расскажу о ней поверхностно.

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

На помощь нам приходит алгоритм PoL. Данный алгоритм строится с помощью Merkle tree и казино или биржа может раз в 24 часа генерировать данное дерево заново и показывать root узел всем пользователям.

{"root":{"sum":"87.19198928","hash":"2f88e5473199aaacb6f2f60b8d93a11862426902aeb31c0198c8f6c4156329dd"},"currency":"BTC","timestamp":1670972401975}

О реализации

В данном примере я расскажу о имплементации с репозитория выше, но данных можно вносить больше.

Листья в данном дереве будут юзеры, а внутренние узлы — промежуточный баланс юзеров ниже.

Информация о листе(юзере)

  • Name — имя юзера, его id

  • Nonce — рандомное число, сгенерированное оператором, доступно только юзеру. Нужно чтоб его сосед не смог узнать name.

  • Sum — баланс юзера

  • Hash — SHA256(name + ‘|’ + sum + ‘|’ + nonce)

Промежуточный узел

  • Sum — баланс промежуточного узла (сумма левого и правого потомка)

  • Hash — SHA256(sum + ‘|’ + left_hash + ‘|’ + right_hash)

function NodeCombiner (left_child, right_child) {   var n = {};   n.sum = left_child.sum + right_child.sum;   n.hash = sha256(string(n.sum) + '|' + left_child.hash + '|' + right_child.hash);   return n; }

Итоговый узел(root node)

Содержит как и внутренний узел Sum и Hash

Поиграться с данным алгоритмом можно тут

Плюс данного алгоритма в том, что не нужно кол-во юзеров кратным 2^n.

Визуализация дерева
Визуализация дерева

Юзер может проверить находится ли его средства в общей массе. Для этого ему достаточно предоставить свои данные и запросить у оператора промежуточные узлы дерева.

В завершение

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


ссылка на оригинал статьи https://habr.com/ru/post/706734/


Комментарии

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

Ваш адрес email не будет опубликован. Обязательные поля помечены *