Es gibt ein Problem – es ist schwierig, eine Zufallszahl in einem dezentralisierten Netzwerk zu generieren. Fast alle Blockchains haben damit zu kämpfen. In Netzwerken, in denen kein Vertrauen zwischen den Nutzern besteht, löst die Schaffung einer unbestreitbaren Zufallszahl viele Herausforderungen.
In diesem Artikel erklären wir, wie wir das Problem anhand von Spielen gelöst haben. Das erste davon war . Für die Entwicklung benötigten wir einen Zufallszahlengenerator.

Ursprünglich hatten wir geplant, die Zahl anhand von Informationen aus der Blockchain zu generieren. Doch bald wurde klar, dass die Zahl manipuliert werden könnte, was bedeutet, dass diese Lösung nicht geeignet war.
Wir haben einen Workaround entwickelt: Wir verwendeten das „Commit-Öffnen“-Schema. Der Server „rätselte“ eine Zahl von 1 bis 5, fügte „Salz“ hinzu und hashierte dann das Ergebnis mit Hilfe der . Der Server deployte im Voraus einen Smart Contract mit der bereits gespeicherten Zahl. Das Spiel bestand also darin, dass der Benutzer die Zahl erriet, die durch den Hash verborgen war.
Der Spieler platzierte seine Wette, während der Server die gezogene Zahl sowie das „Salt“ an den Smart Contract sendete. Einfach ausgedrückt: Die Karten wurden aufgedeckt. Danach verglich der Server die Zahlen und entschied, ob der Benutzer gewonnen oder verloren hatte.
Wenn der Server keine Zahl oder kein „Salt“ zur Überprüfung sendete, gewann der Benutzer. In diesem Fall musste für jedes Spiel der Smart Contract im Voraus bereitgestellt und das potenzielle Gewinnbetrag hinterlegt werden. Dies stellte sich als unpraktisch, zeitaufwendig und kostspielig heraus. Zu dieser Zeit gab es keine andere sichere Lösung.
Kürzlich schlug das Team von Tradisys vor, die Funktion rsaVerify()zum Protokoll Waves hinzuzufügen. Sie überprüft die Gültigkeit der RSA-Signatur basierend auf dem öffentlichen und privaten Schlüssel. Letztendlich wurde die Funktion hinzugefügt.
Wir haben drei Spiele entwickelt: , und . Jedes nutzt die Technologie der Zufallszahl. Lassen Sie uns sehen, wie das funktioniert.

Betrachten wir die Generierung einer Zufallszahl am Beispiel von Ride on Waves. Den Smart Contract finden Sie unter .
Gehen Sie zur Registerkarte Script und wählen Sie Decompiled. Sie sehen den Code des Smart Contracts (dies ist auch das Skript).

Der Code des Smart Contracts enthält eine Reihe von Funktionen. Die mit @Callable gekennzeichneten können durch Invocation-Transaktionen gestartet werden.. Wir interessieren uns für zwei Funktionen: wette und abheben:
- func wette (spielerWahl)
- func abheben (spielId, rsaSign)
1. Der Benutzer wählt die Länge des Abschnitts und die Höhe des Einsatzes.

2. Der Kunde erstellt die wette-Funktion. Für das obige Bild wäre das wette («50 »).
3. Der Kunde sendet eine Invocation-Transaktion an die Adresse des Smart Contracts (broadcast InvocationTx). Die Transaktion enthält als Call-Parameter die Funktion wette. Das bedeutet, dass die Invocation-Transaktion die Ausführung der wette-Funktion (wahl: String) im Smart Contract auslöst.

4. Betrachtung der wette-Funktion:
@Callable(i)
func wette (spielerWahl) = {
let neueSpielNum = IncrementGameNum()
let spielId = toBase58String(i.transactionId)
let pmt = extract(i.payment)
let einsatzNichtInWellen = isDefined(pmt.assetId)
let gebührNichtInWellen = isDefined(pmt.assetId)
let gewinnBetrag = ValidateBetAndDefineWinAmt(pmt.amount, spielerWahl)
let txIdVerwendet = isDefined(getString(this, spielId))
if (einsatzNichtInWellen)
then throw ("Der Einsatz muss in Waves sein")
else if (gebührNichtInWellen)
then throw ("Die Gebühr der Transaktion muss in Waves sein")
else if (txIdVerwendet)
then throw ("Die übergebene txId wurde bereits verwendet. Spiel abgebrochen.")
else {
let spielerPubKey58 = toBase58String(i.callerPublicKey)
let spielDatenStr = FormatGameDataStr(STATESUBMITTED, spielerWahl, spielerPubKey58, height, gewinnBetrag, "")
ScriptResult(WriteSet(cons(DataEntry(RESERVATIONKEY, ValidateAndIncreaseReservedAmt(gewinnBetrag)), cons(DataEntry(GAMESCOUNTERKEY, neueSpielNum), cons(DataEntry(spielId, spielDatenStr), nil)))), TransferSet(cons(ScriptTransfer(SERVER, COMMISSION, unit), nil)))
}
}Die Funktion speichert ein neues Spiel im Status des Smart Contracts. Genauer gesagt:
- Eindeutige Kennung des neuen Spiels (Spiel-ID)
- Spielstatus = EINGEREICHT
- Spielerwahl (Segmentlänge 50)
- Öffentlicher Schlüssel
- Potenzielle Gewinne (abhängig vom Einsatz des Spielers)

So sieht der Datensatz in der Blockchain aus (Schlüssel-Wert):
{
"type": "string",
"value": "03WON_0283_448t8Jn9P3717UnXFEVD5VWjfeGE5gBNeWg58H2aJeQEgJ_06574069_09116020000_0229",
"key": "2GKTX6NLTgUrE4iy9HtpSSHpZ3G8W4cMfdjyvvnc21dx"
}„Schlüssel“ (key) – Spiel-ID des neuen Spiels. Die anderen Daten finden sich im Feld „Wert“ (value). Diese Einträge werden im Tab Data des Smart Contracts gespeichert:


5. Der Server „überwacht“ den Smart Contract und findet die gesendete Transaktion (neues Spiel) über die Blockchain-API. Die Spiel-ID des neuen Spiels ist bereits in der Blockchain gespeichert, was bedeutet, dass sie nicht mehr verändert oder beeinflusst werden kann.
6. Der Server erstellt die Abhebefunktion (gameId, rsaSign). Zum Beispiel so:
withdraw ("FwsuaaShC6DMWdSWQ5osGWtYkVbTEZrsnxqDbVx5oUpq", "base64:Gy69dKdmXUEsAmUrpoWxDLTQOGj5/qO8COA+QjyPVYTAjxXYvEESJbSiCSBRRCOAliqCWwaS161nWqoTL/TltiIvw3nKyd4RJIBNSIgEWGM1tEtNwwnRwSVHs7ToNfZ2Dvk/GgPUqLFDSjnRQpTHdHUPj9mQ8erWw0r6cJXrzfcagKg3yY/0wJ6AyIrflR35mUCK4cO7KumdvC9Mx0hr/ojlHhN732nuG8ps4CUlRw3CkNjNIajBUlyKQwpBKmmiy3yJa/QM5PLxqdppmfFS9y0sxgSlfLOgZ51xRDYuS8NViOA7c1JssH48ZtDbBT5yqzRJXs3RnmZcMDr/q0x6Bg==")7. Der Server sendet eine Invocation-Transaktion an den Smart Contract (broadcast InvocationTx). Die Transaktion enthält den Aufruf der erstellten Withdraw-Funktion (gameId, rsaSign):

Die Funktion enthält Spiel-ID das neue Spiel und das Ergebnis der RSA-Signatur des eindeutigen Identifikators mit dem privaten Schlüssel. Das Signaturergebnis bleibt unverändert.
Was bedeutet das?
Wir nehmen denselben Wert (gameId) und wenden die RSA-Signaturmethode darauf an. Wir werden immer dasselbe Ergebnis erhalten. So funktioniert der RSA-Algorithmus. Es ist nicht möglich, die finale Zahl zu manipulieren, da gameId und das Ergebnis der RSA-Anwendung unbekannt sind. Es ist auch sinnlos, eine Zahl zu erraten.
8. Die Blockchain akzeptiert die Transaktion. Diese startet die Withdraw-Funktion (gameId, rsaSign).
9. Innerhalb der Withdraw-Funktion erfolgt der Export der GenerateRandInt-Funktion (gameId, rsaSign). Das ist der Zufallszahlengenerator.
# @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")
}rand ist die Zufallszahl.
Zuerst wird die Zeichenkette genommen, die das Ergebnis der RSA-Signatur Spiel-ID mit dem privaten Schlüssel (rsaSign). Dann wird sie mit SHA-256 gehasht (sha256(rsaSign)).
Wir können das Ergebnis der Signatur und der anschließenden Hash-Berechnung nicht vorhersagen. Daher ist es unmöglich, die Generierung der Zufallszahl zu beeinflussen. Um eine Zahl in einem bestimmten Bereich zu erhalten (zum Beispiel von 1 bis 100), wird die Funktion toInt und 0 angewendet (analog ).
Am Anfang des Artikels haben wir die Funktion erwähnt rsaVerify(), die es ermöglicht, die Gültigkeit einer RSA-Signatur mit dem privaten Schlüssel anhand des öffentlichen Schlüssels zu überprüfen. Hier ist ein Teil von GenerateRandInt (gameId, rsaSign):
rsaVerify (SHA256, toBytes(gameId), rsaSign, RSAPUBLIC)Es werden der öffentliche Schlüssel RSAPUBLIC und der String rsaSign übergeben. Die Signatur wird auf ihre Gültigkeit überprüft. Eine Zahl wird nur bei erfolgreicher Überprüfung generiert. Andernfalls betrachtet das System die Signatur als ungültig (Ungültige RSA-Signatur).
Der Server muss die gameId des Spiels mit dem privaten Schlüssel signieren und innerhalb von 2880 Blöcken eine gültige RSA-Signatur senden. Dieser Parameter wird beim Deployment des Smart Contracts festgelegt. Wenn innerhalb der festgelegten Zeit nichts passiert, gewinnt der Benutzer. In diesem Fall muss der Preis selbst an die eigene Adresse gesendet werden. Das bedeutet, der Server hat keinen Anreiz zu betrügen, da dies zu einem Verlust führen würde. Im Folgenden ein Beispiel.

Der Benutzer spielt . Ich habe 2 von 6 Seiten des Würfels ausgewählt, der Einsatz beträgt 14 WAVES. Wenn der Server innerhalb der festgelegten Zeit (2880 Blöcke) keine gültige RSA-Unterschrift für den Smart Contract sendet, erhält der Benutzer 34,44 WAVES zurück.
Zur Generierung von Zahlen in den Spielen nutzen wir ein Oracle – ein externes, nicht blockchainbasiertes System. Der Server erstellt die RSA-Unterschrift für die Spiel-ID. Der Smart Contract überprüft die Gültigkeit der Unterschrift und bestimmt den Gewinner. Wenn der Server nichts gesendet hat, gewinnt der Benutzer automatisch.
Dies ist eine faire Methode zur Generierung, da Manipulation technisch unmöglich ist. Alle Spiele von Tradisys basieren auf dem beschriebenen Algorithmus. So funktionieren Spiele auf der Blockchain. Alles ist transparent und überprüfbar. Ein solches System gibt es in keiner anderen Blockchain. Das ist echter Zufall.
Quelle: habr.com
