Pull to refresh

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

Reading time3 min
Views6K

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

О опыте

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

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

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

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

  • 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).

Tags:
Hubs:
Total votes 7: ↑7 and ↓0+7
Comments9

Articles