Има проблем - трудно е да се генерира произволно число в децентрализирана мрежа. Почти всички блокчейни вече са се сблъскали с това. Наистина, в мрежи, където няма доверие между потребителите, създаването на неоспоримо произволно число решава много проблеми.
В тази статия ви разказваме как успяхме да разрешим проблема, използвайки игри като пример. Първият от тях беше
Първоначално планирахме да генерираме число въз основа на информация от блокчейна. Тогава обаче стана ясно: числото може да бъде манипулирано, което означава, че решението не е подходящо.
Измислихме заобиколно решение: използвайте схемата commit-expand. Сървърът позна число от 1 до 5, добави сол към него и след това хешира резултата с помощта на
Играчът направи залог и сървърът изпрати скритото число и „солта“ към интелигентния договор. С прости думи той разкри картите. След това сървърът провери числата и реши дали потребителят спечели или загуби.
Ако сървърът не изпрати номер или „сол“ за проверка, потребителят печели. В този случай за всяка игра беше необходимо предварително да се разгърне интелигентен договор и да се включат потенциални печалби в него. Оказа се неудобно, времеемко и скъпо. По това време нямаше друго безопасно решение.
Наскоро екипът на Tradisys предложи добавяне на функция към протокола Waves rsaVerify(). Той проверява валидността на RSA подписа въз основа на публичния и частния ключ. В резултат на това функцията беше добавена.
Разработихме три игри:
Нека да разгледаме генерирането на произволно число, използвайки Ride on Waves като пример. Интелигентният договор може да бъде намерен
Отидете в раздела Сценарий и изберете Декомпилиран. Ще видите кода на интелигентния договор (известен още като скрипт).
Кодът на интелигентния договор съдържа набор от функции. Тези, маркирани като @Callable, могат да бъдат стартирани с помощта на Извикващи транзакции. Ние се интересуваме от две функции: заложат и оттегли:
- func залог (playerChoice)
- func тегли (gameId,rsaSign)
1. Потребителят избира дължината на сегмента и размера на залога.
2. Клиентът създава функция за залагане. За изображението по-горе би било залог ("50").
3. Клиентът изпраща транзакция Invocation до адреса на интелигентния договор (broadcast InvocationTx). Транзакцията съдържа функцията за залагане като параметър за повикване. Това означава, че транзакцията Invocation задейства изпълнението на функцията за залагане (избор: String) на интелигентния договор.
4. Помислете за функцията за залагане:
@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)))
}
}
Функцията записва нова игра в състоянието на интелигентния договор. а именно:
- Уникален идентификатор за нова игра (ID на играта)
- Състояние на играта = ИЗПРАТЕНО
- Избор на играча (дължина на сегмента 50)
- Публичен ключ
- Потенциални печалби (в зависимост от залога на играча)
Ето как изглежда запис на данни в блокчейна (ключ-стойност):
{
"type": "string",
"value": "03WON_0283_448t8Jn9P3717UnXFEVD5VWjfeGE5gBNeWg58H2aJeQEgJ_06574069_09116020000_0229",
"key": "2GKTX6NLTgUrE4iy9HtpSSHpZ3G8W4cMfdjyvvnc21dx"
}
"Ключ" (ключ) – идентификатор на играта нова игра. Останалите данни се съдържат в реда на полето „стойност“. Тези записи се съхраняват в раздела Дата интелигентен договор:
5. Сървърът „преглежда“ интелигентния договор и намира изпратената транзакция (нова игра) с помощта на блокчейн Api. Идентификационният номер на играта на новата игра вече е записан в блокчейна, което означава, че вече не може да бъде променян или повлиян
6. Сървърът генерира функция за теглене (gameId, rsaSign). Например така:
withdraw ("FwsuaaShC6DMWdSWQ5osGWtYkVbTEZrsnxqDbVx5oUpq", "base64:Gy69dKdmXUEsAmUrpoWxDLTQOGj5/qO8COA+QjyPVYTAjxXYvEESJbSiCSBRRCOAliqCWwaS161nWqoTL/TltiIvw3nKyd4RJIBNSIgEWGM1tEtNwwnRwSVHs7ToNfZ2Dvk/GgPUqLFDSjnRQpTHdHUPj9mQ8erWw0r6cJXrzfcagKg3yY/0wJ6AyIrflR35mUCK4cO7KumdvC9Mx0hr/ojlHhN732nuG8ps4CUlRw3CkNjNIajBUlyKQwpBKmmiy3yJa/QM5PLxqdppmfFS9y0sxgSlfLOgZ51xRDYuS8NViOA7c1JssH48ZtDbBT5yqzRJXs3RnmZcMDr/q0x6Bg==")
7. Сървърът изпраща транзакция Invocation към интелигентния договор (излъчване InvocationTx). Транзакцията съдържа извикване на формирана функция за теглене (gameId, rsaSign):
Функцията съдържа идентификатор на играта нова игра и резултат от RSA подписване на уникален идентификатор с частен ключ. Резултатът от подписа е непроменен.
Какво означава това?
Взимаме същата стойност (идентификатор на играта) и прилагаме към нея метода за подпис RSA. Винаги ще получаваме един и същ резултат. Ето как работи алгоритъмът RSA. Крайното число не може да бъде манипулирано, тъй като идентификаторът на играта и резултатът от прилагането на RSA не са известни. Избирането на номер също е безсмислено.
8. Blockchain приема транзакцията. Той изпълнява функцията за теглене (gameId, rsaSign)
9. Вътре във функцията за оттегляне се извършва оттегляне Функции 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 подписа идентификатор на играта частен ключ (rsaSign). След това се хешира с SHA-256 (sha256(rsaSign)).
Не можем да предвидим резултата от подписа и последващото хеширане. Следователно е невъзможно да се повлияе на генерирането на произволно число. За да получите число в определен диапазон (например от 1 до 100), използвайте функцията за преобразуване toInt и %100 (подобно на
В началото на статията споменахме функцията rsaVerify(), което ви позволява да проверите валидността на RSA подпис с помощта на частен ключ спрямо публичен. Ето частта GenerateRandInt(gameId,rsaSign):
rsaVerify (SHA256, toBytes(gameId), rsaSign, RSAPUBLIC)
Публичният ключ RSAPUBLIC и низът rsaSign се предават на входа. Подписът се проверява за валидност. Номерът се генерира, ако проверката е успешна. В противен случай системата счита, че подписът не е валиден (Невалиден RSA подпис).
Сървърът трябва да подпише идентификатора на играта с частен ключ и да изпрати валиден Rsa подпис в рамките на 2880 блока. Параметърът се конфигурира при внедряването на интелигентния договор. Ако нищо не се случи в рамките на определеното време, потребителят печели. В този случай наградата трябва да бъде изпратена на вашия адрес сами. Оказва се, че „не е изгодно за сървъра да мами“, защото това води до загуба. По-долу е даден пример.
Потребителят играе
За генериране на числа в игрите ние използваме оракул – външна, неблокчейн система. Сървърът извършва RSA подпис на идентификатора на играта. Интелигентният договор проверява валидността на подписа и определя победителя. Ако сървърът не изпрати нищо, тогава потребителят автоматично печели.
Това е честен метод за генериране, тъй като манипулацията е технически невъзможна. Всички игри на Tradisys работят на базата на описания алгоритъм. Ето как работят блокчейн игрите. Всичко е прозрачно и проверимо. Няма аналози на такава система в нито една друга блокчейн. Това е доста случайно.
Източник: www.habr.com