RSA кокустук блокчейнде

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

В статье рассказываем, как удалось решить проблему на примере игр. Первой из них стала Waves Xmas Tree. Для разработки нам понадобился генератор случайных чисел.

RSA кокустук блокчейнде

Изначально мы планировали генерировать число на основании информации из блокчейна. Однако потом стало ясно: число могут подтасовать, а значит решение не подходит.

Мы придумали обходной путь: использовать схему «коммит-раскрытие». Сервер «загадывал» число от 1 до 5, добавлял к нему «соль», а затем хэшировал результат при помощи функции Keccak. Cервер заранее деплоил смарт-контракт с уже сохраненным числом. Получается, игра сводидась к тому, что пользователь угадывал число, скрытое хэшем.

Игрок делал ставку, а сервер отправлял загаданное число и «соль» на смарт-контракт. Простым языком, раскрывал карты. После этого сервер сверял цифры и решал, победил пользователь или проиграл.

Если сервер не присылал число или «соль» для проверки, пользователь побеждал. В этом случае для каждой игры было необходимо заранее деплоить смарт-контракт и закладывать в него потенциальный выигрыш. Оказалось, это неудобно, долго и дорого. На тот момент другого безопасного решения не было.

Недавно команда Tradisys предожила добавить в протокол Waves функцию rsaVerify(). Она проверяет валидность RSA-подписи на основании публичного и приватного ключа. В итоге функция была добавлена.

Мы разработали три игры: Roller дейт, Монета Flip и Ride On Waves. В каждой реализована технология случайного числа. Разберемся, как это работает.

RSA кокустук блокчейнде

Рассмотрим генерацию случайного числа на примере Ride on Waves. Смарт-контракт можно найти бул жерде.

Өтмөккө өтүңүз кол шрифти жана тандаңыз Decompiled. Увидите код смарт-контракта (он же скрипт).

RSA кокустук блокчейнде

Код смарт-контракта содержит набор функций. Те, что помечены как @Callable, могут запускаться с помощью Invocation-транзакций. Нас интересуют две функции: мелдешүү и алып:

  • func bet (playerChoice)
  • func withdraw (gameId,rsaSign)

1. Пользователь выбирает длину отрезка и размер ставки.

RSA кокустук блокчейнде

2. Клиент формирует bet-функцию. Для изображения выше это будет bet («50»).

3. Клиент отправляет Invocation-транзакцию на адрес смарт-контракта (broadcast InvocationTx). Транзакция в качестве Сall-параметра содержит функцию bet. Это означает, что Invocation-транзакция запускает выполнение bet-функции (choice: String) на смарт-контракте.

RSA кокустук блокчейнде

4. Рассмотрим bet-функцию:

@Callable(i)
func bet (playerChoice) = {
    let newGameNum = IncrementGameNum()
    let gameId = toBase58String(i.transactionId)
    let pmt = extract(i.payment)
    let betNotInWaves = isDefined(pmt.assetId)
    let feeNotInWaves = isDefined(pmt.assetId)
    let winAmt = ValidateBetAndDefineWinAmt(pmt.amount, playerChoice)
    let txIdUsed = isDefined(getString(this, gameId))
    if (betNotInWaves)
        then throw ("Bet amount must be in Waves")
        else if (feeNotInWaves)
            then throw ("Transaction's fee must be in Waves")
            else if (txIdUsed)
                then throw ("Passed txId had been used before. Game aborted.")
                else {
                    let playerPubKey58 = toBase58String(i.callerPublicKey)
                    let gameDataStr = FormatGameDataStr(STATESUBMITTED, playerChoice, playerPubKey58, height, winAmt, "")
                    ScriptResult(WriteSet(cons(DataEntry(RESERVATIONKEY, ValidateAndIncreaseReservedAmt(winAmt)), cons(DataEntry(GAMESCOUNTERKEY, newGameNum), cons(DataEntry(gameId, gameDataStr), nil)))), TransferSet(cons(ScriptTransfer(SERVER, COMMISSION, unit), nil)))
                    }
    }

Функция записывает в стейт смарт-контракта новую игру. А именно:

  • Уникальный идентификатор новой игры (game id)
  • Game state = SUBMITTED
  • Выбор игрока (длина отрезка 50)
  • Публичный ключ
  • Потенциальный выигрыш (зависит от ставки игрока)

RSA кокустук блокчейнде

Так выглядит запись данных в блокчейне (ключ-значение):

{
    "type": "string",
    "value": "03WON_0283_448t8Jn9P3717UnXFEVD5VWjfeGE5gBNeWg58H2aJeQEgJ_06574069_09116020000_0229",
    "key": "2GKTX6NLTgUrE4iy9HtpSSHpZ3G8W4cMfdjyvvnc21dx"
  }

«Ключ» (key) – game id новой игры. Остальные данные содержатся в строке поля «значение» (value). Эти записи хранятся во вкладке маалыматтар смарт-контракта:

RSA кокустук блокчейнде

RSA кокустук блокчейнде

5. Сервер «смотрит» на смарт-контракт и находит отправленную транзакцию (новую игру) с помощью Api блокчейна. Game id новой игры уже записан в блокчейне, а значит изменить или повлиять на нее уже нельзя

6. Сервер формирует withdraw-функцию (gameId, rsaSign). Например, такую:

withdraw ("FwsuaaShC6DMWdSWQ5osGWtYkVbTEZrsnxqDbVx5oUpq", "base64:Gy69dKdmXUEsAmUrpoWxDLTQOGj5/qO8COA+QjyPVYTAjxXYvEESJbSiCSBRRCOAliqCWwaS161nWqoTL/TltiIvw3nKyd4RJIBNSIgEWGM1tEtNwwnRwSVHs7ToNfZ2Dvk/GgPUqLFDSjnRQpTHdHUPj9mQ8erWw0r6cJXrzfcagKg3yY/0wJ6AyIrflR35mUCK4cO7KumdvC9Mx0hr/ojlHhN732nuG8ps4CUlRw3CkNjNIajBUlyKQwpBKmmiy3yJa/QM5PLxqdppmfFS9y0sxgSlfLOgZ51xRDYuS8NViOA7c1JssH48ZtDbBT5yqzRJXs3RnmZcMDr/q0x6Bg==")

7. Сервер отправляет Invocation-транзакцию на смарт-контракт (broadcast InvocationTx). Транзакция содержит вызов сформированной withdraw-функции (gameId, rsaSign):

RSA кокустук блокчейнде

Функция содержит game id новой игры и результат RSA-подписи уникального идентификатора приватным ключом. Результат подписи неизменен.

Бул эмнени билдирет?

Берем одно и то же значение (game id) и применяем к нему метод RSA-подписи. Будем всегда получать один и тот же результат. Так работает RSA-алгоритм. Нельзя манипулировать финальным числом, так как game id и результат применения RSA не известен. Подбирать число также бессмысленно.

8. Блокчейн принимает транзакцию. Она запускает withdraw-функцию (gameId, rsaSign)

9. Внутри withdraw-функции происходит вывоз GenerateRandInt-функции (gameId, rsaSign). Это и есть генератор случайных чисел

# @return 1 ... 100
func GenerateRandInt (gameId,rsaSign) = {
   	# verify RSA signature to proof random
    let rsaSigValid = rsaVerify (SHA256, toBytes(gameId), rsaSign, RSAPUBLIC)
    if (rsaSigValid)
        then {
            let rand = (toInt(sha256(rsaSign)) % 100)
            if ((0 > rand))
                then ((-1 * rand) + 1)
                else (rand + 1)
            }
        else throw ("Invalid RSA signature")
    }

ранды – и есть случайное число.

Сначала берется строка, которая является результатом RSA-подписи game id приватным ключом (rsaSign). Затем хэшируется с помощью SHA-256 (sha256(rsaSign)).

Мы не можем предсказать результат подписи и последующего хэширования. Поэтому невозможно повлиять на генерацию случайного числа. Чтобы получить число в определенном диапазоне (например, от 1 до 100), применяется функция преобразования toInt и %100 (аналог MOD).

В начале статьи мы упоминали функцию rsaVerify(), которая позволяет проверить валидность RSA-подписи приватным ключом по публичному. Вот часть GenerateRandInt (gameId,rsaSign):

rsaVerify (SHA256, toBytes(gameId), rsaSign, RSAPUBLIC)

На вход передается публичный ключ RSAPUBLIC и строка rsaSign. Подпись проверяется на валидность. Число генерируется в случае успешной проверки. В обратном случае система считает, что подпись не валидна (Invalid RSA signature).

Сервер должен подписать game id игры приватным ключом и отправить валидную Rsa-подпись в течение 2880 блоков. Параметр настраивается при деплое смарт-контракта. Если за отведенное время ничего не происходит, пользователь выигрывает. В этом случае приз нужно отправить на свой адрес самостоятельно. Получается, серверу «не выгодно обманывать», ведь это ведет к проигрышу. Ниже – пример.

RSA кокустук блокчейнде

Пользователь играет в Roller дейт. Выбрал 2 из 6 граней кубика, ставка – 14 WAVES. Если сервер не пришлет валидную RSA-подпись на смарт-контракт в течение установленного времени (2880 блоков), пользователь заберет 34.44 WAVES.

Для генерации чисел в играх мы используем оракул – внешнюю, не блокчейновую систему. Сервер осуществляет RSA-подпись game id. Смарт-контракт проверяет валидность подписи и определяет победителя. Если сервер не прислал ничего, то пользователь автоматически побеждает.

Это честный метод генерации, ведь манипуляция технически невозможна. Все игры Tradisys работают на основании описанного алгоритма. Так работают игры на блокчейне. Все прозрачно и поддается проверке. Аналогов подобной системы нет ни в одном другом блокчейне. Это честный рандом.

Source: www.habr.com

Комментарий кошуу