Postoji problem - teško je generirati slučajni broj u decentraliziranoj mreži. Gotovo svi blockchaini već su se susreli s tim. Doista, u mrežama u kojima nema povjerenja među korisnicima, stvaranje nepobitnog slučajnog broja rješava mnoge probleme.
U ovom članku ćemo vam reći kako smo uspjeli riješiti problem koristeći igre kao primjer. Prvi od njih bio je
U početku smo planirali generirati broj na temelju informacija iz blockchaina. No, tada je postalo jasno: brojkom se može manipulirati, što znači da rješenje nije prikladno.
Smislili smo zaobilazno rješenje: upotrijebite shemu commit-expand. Poslužitelj je pogodio broj od 1 do 5, dodao mu sol, a zatim raspršio rezultat pomoću
Igrač je stavio okladu, a server je poslao skriveni broj i "sol" pametnom ugovoru. Jednostavnim riječima, otkrio je karte. Nakon toga poslužitelj je provjerio brojeve i odlučio je li korisnik pobijedio ili izgubio.
Ako poslužitelj nije poslao broj ili "sol" na provjeru, korisnik je pobijedio. U ovom slučaju, za svaku je igru bilo potrebno unaprijed implementirati pametni ugovor i u njega uključiti potencijalne dobitke. Pokazalo se da je to nezgodno, dugotrajno i skupo. U to vrijeme nije bilo drugog sigurnog rješenja.
Nedavno je Tradisys tim predložio dodavanje funkcije Waves protokolu rsaVerify(). Provjerava valjanost RSA potpisa na temelju javnog i privatnog ključa. Kao rezultat toga, značajka je dodana.
Razvili smo tri igre:
Pogledajmo generiranje nasumičnog broja koristeći Ride on Waves kao primjer. Pametni ugovor se može pronaći
Idite na karticu Script i odaberite Dekompilirano. Vidjet ćete kod pametnog ugovora (aka skripta).
Kod pametnog ugovora sadrži skup funkcija. One označene kao @Callable mogu se pokrenuti pomoću Transakcije pozivanja. Zanimaju nas dvije funkcije: ulog и povući:
- func ulog (playerChoice)
- funkcija povlačenja(gameId,rsaSign)
1. Korisnik odabire duljinu segmenta i veličinu uloga.
2. Klijent kreira funkciju oklade. Za gornju sliku bilo bi ulog("50").
3. Klijent šalje Invocation transakciju na adresu pametnog ugovora (broadcast InvocationTx). Transakcija sadrži funkciju klađenja kao parametar poziva. To znači da transakcija Invocation pokreće izvršenje funkcije klađenja (izbor: niz) na pametnom ugovoru.
4. Razmotrite funkciju oklade:
@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)))
}
}
Funkcija upisuje novu igru u stanje pametnog ugovora. Naime:
- Jedinstveni identifikator za novu igru (ID igre)
- Stanje igre = POSLANO
- Izbor igrača (duljina segmenta 50)
- Javni ključ
- Potencijalni dobici (ovisno o ulogu igrača)
Ovako izgleda zapis podataka u blockchainu (ključ-vrijednost):
{
"type": "string",
"value": "03WON_0283_448t8Jn9P3717UnXFEVD5VWjfeGE5gBNeWg58H2aJeQEgJ_06574069_09116020000_0229",
"key": "2GKTX6NLTgUrE4iy9HtpSSHpZ3G8W4cMfdjyvvnc21dx"
}
"Ključ" (ključ) – id igre Nova igra. Preostali podaci nalaze se u retku polja "vrijednost". Ti su unosi pohranjeni u kartici Datum pametni ugovor:
5. Poslužitelj “gleda” pametni ugovor i pronalazi poslanu transakciju (nova igra) koristeći blockchain Api. Game ID nove igre već je zabilježen u blockchainu, što znači da se više ne može mijenjati ili utjecati na njega
6. Poslužitelj generira funkciju povlačenja (gameId, rsaSign). Na primjer, ovako:
withdraw ("FwsuaaShC6DMWdSWQ5osGWtYkVbTEZrsnxqDbVx5oUpq", "base64:Gy69dKdmXUEsAmUrpoWxDLTQOGj5/qO8COA+QjyPVYTAjxXYvEESJbSiCSBRRCOAliqCWwaS161nWqoTL/TltiIvw3nKyd4RJIBNSIgEWGM1tEtNwwnRwSVHs7ToNfZ2Dvk/GgPUqLFDSjnRQpTHdHUPj9mQ8erWw0r6cJXrzfcagKg3yY/0wJ6AyIrflR35mUCK4cO7KumdvC9Mx0hr/ojlHhN732nuG8ps4CUlRw3CkNjNIajBUlyKQwpBKmmiy3yJa/QM5PLxqdppmfFS9y0sxgSlfLOgZ51xRDYuS8NViOA7c1JssH48ZtDbBT5yqzRJXs3RnmZcMDr/q0x6Bg==")
7. Poslužitelj šalje transakciju Invocation pametnom ugovoru (broadcast InvocationTx). Transakcija sadrži poziv formiranoj funkciji povlačenja (gameId, rsaSign):
Funkcija sadrži id igre nova igra i rezultat RSA potpisivanja jedinstvenog identifikatora s privatnim ključem. Rezultat potpisa je nepromijenjen.
Što to znači?
Uzimamo istu vrijednost (id igre) i na nju primjenjujemo metodu RSA potpisa. Uvijek ćemo dobiti isti rezultat. Ovako radi RSA algoritam. Konačnim brojem se ne može manipulirati jer ID igre i rezultat primjene RSA nisu poznati. Biranje broja također je besmisleno.
8. Blockchain prihvaća transakciju. Pokreće funkciju povlačenja (gameId, rsaSign)
9. Unutar funkcije povlačenja dolazi do povlačenja GenerateRandInt funkcije (Id igre, rsaSign). Ovo je generator slučajnih brojeva
# @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")
}
red - i tu je nasumični broj.
Prvo se uzima niz, koji je rezultat RSA potpisa id igre privatni ključ (rsaSign). Zatim se rasprši s SHA-256 (sha256(rsaSign)).
Ne možemo predvidjeti ishod potpisa i naknadnog raspršivanja. Stoga je nemoguće utjecati na generiranje slučajnog broja. Da biste dobili broj u određenom rasponu (na primjer, od 1 do 100), upotrijebite funkciju pretvorbe u Int i %100 (slično
Na početku članka spomenuli smo funkciju rsaVerify(), koji vam omogućuje da provjerite valjanost RSA potpisa korištenjem privatnog ključa u odnosu na javni. Evo dijela GenerateRandInt(gameId,rsaSign):
rsaVerify (SHA256, toBytes(gameId), rsaSign, RSAPUBLIC)
Javni ključ RSAPUBLIC i niz rsaSign prosljeđuju se na ulaz. Provjerava se valjanost potpisa. Broj se generira ako je provjera uspješna. U suprotnom, sustav smatra da potpis nije valjan (Invalid RSA signature).
Poslužitelj mora potpisati ID igre privatnim ključem i poslati važeći RSA potpis unutar 2880 blokova. Parametar se konfigurira prilikom implementacije pametnog ugovora. Ako se ništa ne dogodi unutar dodijeljenog vremena, korisnik pobjeđuje. U tom slučaju nagradu morate sami poslati na svoju adresu. Ispada da "nije isplativo za poslužitelja varati", jer to dovodi do gubitka. Ispod je primjer.
Korisnik igra
Za generiranje brojeva u igrama koristimo oracle - vanjski sustav koji nije povezan s blokovima. Poslužitelj izvodi RSA potpis ID-a igre. Pametni ugovor provjerava valjanost potpisa i određuje pobjednika. Ako poslužitelj ne pošalje ništa, korisnik automatski pobjeđuje.
Ovo je metoda poštene generacije, jer je manipulacija tehnički nemoguća. Sve Tradisys igre rade na temelju opisanog algoritma. Ovako funkcioniraju blockchain igre. Sve je transparentno i provjerljivo. Nema analoga takvog sustava ni u jednom drugom blockchainu. Ovo je prilično nasumično.
Izvor: www.habr.com