RSA-рандом на блокчэйне

Ёсць праблема - складана згенераваць выпадковы лік у дэцэнтралізаванай сетцы. Ці не ўсе блокчейны ўжо з гэтым сутыкнуліся. Бо ў сетках, дзе няма даверу паміж карыстачамі, стварэнне бясспрэчнага выпадковага ліку вырашае мноства задач.

У артыкуле расказваем, як удалося вырашыць праблему на прыкладзе гульняў. Першай з іх стала Waves Xmas Tree. Для распрацоўкі нам спатрэбіўся генератар выпадковых лікаў.

RSA-рандом на блокчэйне

Першапачаткова мы планавалі генераваць лік на падставе інфармацыі з блокчэйна. Аднак потым стала зразумела: колькасць могуць падтасаваць, а значыць рашэнне не падыходзіць.

Мы прыдумалі абыходны шлях: выкарыстоўваць схему "комміт-раскрыццё". Сервер "загадваў" лік ад 1 да 5, дадаваў да яго "соль", а затым хэшаваў вынік пры дапамозе функцыі Keccak. Сервер загадзя дэплоіў смарт-кантракт з ужо захаваным лікам. Атрымліваецца, гульня зводзілася да таго, што карыстач адгадваў лік, утоенае хэшам.

Гулец рабіў стаўку, а сервер адпраўляў загаданы лік і "соль" на смарт-кантракт. Простым мовай, раскрываў карты. Пасля гэтага сервер звяраў лічбы і вырашаў, перамог карыстач або прайграў.

Калі сервер не дасылаў лік ці "соль" для праверкі, карыстач перамагаў. У гэтым выпадку для кожнай гульні было неабходна загадзя дэплоіць смарт-кантракт і закладваць у яго патэнцыйны выйгрыш. Аказалася, гэта няёмка, доўга і дорага. На той момант іншага бясьпечнага рашэньня не было.

Нядаўна каманда Tradisys прадказала дадаць у пратакол Waves функцыю rsaVerify(). Яна правярае валіднасць RSA-подпісы на падставе публічнага і прыватнага ключа. У выніку функцыя была дададзена.

Мы распрацавалі тры гульні: Косці Roller, Фліп манет и 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 (аналаг модуль).

У пачатку артыкула мы згадвалі функцыю 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 працуюць на падставе апісанага алгарытму. Так працуюць гульні на блокчэйне. Усё празрыста і паддаецца праверцы. Аналагаў падобнай сістэмы няма ні ў адным іншым блокчэйне. Гэта сумленны рандом.

Крыніца: habr.com

Дадаць каментар