Es gibt ein Problem, das wir bei der Entwicklung unserer Spiele angehen mussten. Es ist kompliziert, eine Zufallszahl in einem verteilten Netzwerk zu generieren. Fast alle Blockchains haben sich bereits mit diesem Problem befasst. In Netzwerken, in denen zwischen niemandem Vertrauen besteht, löst die Erstellung einer Zufallszahl eine Vielzahl von Problemen.
In diesem Artikel erklären wir, wie wir dieses Problem für unsere Spiele gelöst haben. Der erste davon war
Waves Xmas Tree .

Zunächst wollten wir eine Nummer mit Informationen aus der Blockchain generieren. Bei weiteren Untersuchungen wurde jedoch klar, dass der Prozess, mit dem auf diese Weise eine Zahl erstellt wurde, manipuliert werden konnte. Wir mussten diese Lösung verwerfen.
Wir haben eine Problemumgehung mit einem Commit-Enthüllungsschema entwickelt. Der Server schlug eine Zahl von 1 bis 5 vor, fügte 'salt' hinzu und hashte das Ergebnis mit der
Keccak-Funktion . Der Server hat einen Smart-Vertrag mit einer bereits gespeicherten Nummer vorab debuggt. Das Ergebnis war, dass das Spiel effektiv auf den Benutzer reduziert wurde, der die durch den Hash verborgene Zahl errät.
Der Spieler platzierte seine Wette und der Server schickte eine versteckte Nummer und "Salz" an einen intelligenten Vertrag. Anders ausgedrückt, die Karten wurden aufgedeckt. Anschließend überprüfte der Server die Zahlen und entschied, ob der Benutzer gewonnen oder verloren hatte.
Wenn der Server die Nummer und das "Salt" nicht zur Überprüfung gesendet hat, hat der Benutzer gewonnen. In diesem Fall war es notwendig, im Voraus einen intelligenten Vertrag abzuschließen und mögliche Gewinne für jedes Spiel zu vereinbaren. Dies war unpraktisch, teuer und zeitaufwändig. Zu diesem Zeitpunkt gab es jedoch keine andere sichere Lösung.
Kurz darauf schlug das Tradisys-Team vor, die Funktion
rsaVerify () zum Waves-Protokoll hinzuzufügen. Dies überprüft die Gültigkeit einer RSA-Signatur basierend auf öffentlichen und privaten Schlüsseln. Aufgrund unseres Vorschlags wurde die Funktion hinzugefügt.
Wir haben drei neue Spiele gebaut:
Dice Roller ,
Coin Flip und
Ride On Waves . In jedem von ihnen wurde die neue Zufallszahlentechnologie implementiert. Schauen wir uns genauer an, wie es funktioniert.

Schauen wir uns zuerst die Zufallszahlengenerierung an. Den Smart-Vertrag finden Sie
hier .
Gehen Sie zur
Registerkarte Skript und wählen Sie
Dekompiliert . Sie sehen den Code (oder das Skript) des Smart Contract.

Der Smart Contract Code besteht aus einer Liste von Funktionen. Diejenigen, die @Callable sind, können über Aufruftransaktionen ausgeführt
werden . Wir sind an zwei davon interessiert:
wetten und
abheben :
- Funkwette (playerChoice)
- func zurückziehen (gameId, rsaSign)
1. Der Benutzer wählt den Bereich und die Einsatzgröße.

2. Der Kunde arrangiert die Wettfunktion. Für das Bild oben wäre es eine
Wette ("50")3. Der Client sendet eine Invocation-Transaktion an die Smart Contract-Adresse (Broadcast InvocationTx). Eine Transaktion als Call-Parameter enthält die Wettfunktion. Dies bedeutet, dass die Invocation-Transaktion die Ausführung der Wettfunktion für den Smart-Vertrag startet (Auswahl: String).

4. Schauen wir uns die Wettfunktion an:
@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))) } }
Die Funktion zeichnet ein neues Spiel im Smart-Vertragsstatus auf:
- Einzigartige neue Spiel-ID (Spiel-ID)
- Spielstatus = ABGEGEBEN
- Spielerauswahl (die Reichweite beträgt 50)
- Öffentlicher Schlüssel
- Mögliche Belohnung (hängt von der Wette des Spielers ab)

So sieht die Schlüsselwertdatenbank in der Blockchain aus:
{ "type": "string", "value": "03WON_0283_448t8Jn9P3717UnXFEVD5VWjfeGE5gBNeWg58H2aJeQEgJ_06574069_09116020000_0229", "key": "2GKTX6NLTgUrE4iy9HtpSSHpZ3G8W4cMfdjyvvnc21dx" }
'Schlüssel' ist die
Spiel-ID für ein neues Spiel. Die restlichen Daten sind im Feld 'Wert' enthalten. Diese Einträge werden auf der Registerkarte
Daten des Smart-Vertrags gespeichert:


5. Der Server findet die gesendete Transaktion (das neue Spiel) über die Blockchain-API. Die Spiel-ID ist bereits in der Blockchain aufgezeichnet, sodass sie nicht geändert oder gelöscht werden kann.
6. Der Server bildet eine Rückzugsfunktion (gameId, rsaSign) wie:
zurückzuziehen ( «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 einen Aufruf der generierten Rückzugsfunktion (gameId, rsaSign):

Die Funktion enthält eine
Spiel-ID und eine RSA-Signatur einer eindeutigen ID. Das Signaturergebnis ist unveränderlich.
Was bedeutet das?
Wir nehmen den gleichen Wert (Spiel-ID) und wenden die RSA-Signaturmethode darauf an. So funktioniert der RSA-Algorithmus. Es ist unmöglich, die endgültige Zahl zu manipulieren, da die
Spiel-ID und das Ergebnis des RSA-Algorithmus unbekannt sind. Es ist auch sinnlos zu versuchen, eine Zahl zu erraten.
8. Die Blockchain empfängt eine Transaktion, die die Rücknahmefunktion ausführt (gameId, rsaSign).
9. Innerhalb der Rückzugsfunktion (gameId, rsaSign) wird die GenerateRandIn-Funktion aufgerufen. Dies ist ein 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 eine Zufallszahl
Zunächst wird die Zeichenfolge verwendet, die ein Ergebnis der RSA-Signatur ist. Dann wird es über SHA-256 (
sha256 (rsaSign) ) gehasht.
Wir können das Signaturergebnis und das anschließende Hashing nicht vorhersagen. Somit ist es unmöglich, seine Erzeugung zu beeinflussen. Um eine Zahl in einem bestimmten Bereich (z. B. von 1 bis 100) zu erhalten, werden die Konvertierungsfunktionen in Int und% 100 (
Mod- Analog) angewendet.
Am Anfang des Artikels haben wir die Funktion
rsaVerify () erwähnt , mit der die Gültigkeit einer RSA-Signatur durch einen privaten Schlüssel gegen einen öffentlichen Schlüssel überprüft werden kann. Hier ist ein Teil von GenerateRandInt (gameId, rsaSign):
rsaVerify (SHA256, toBytes (gameId), rsaSign, RSAPUBLIC)
Zunächst werden der öffentliche RSAPUBLIC-Schlüssel und die rsaSign-Zeichenfolge verwendet. Die Signatur wird auf Gültigkeit geprüft. Wenn die Prüfung erfolgreich ist, wird die Nummer generiert. Andernfalls ist das System der Ansicht, dass die Signatur ungültig ist (ungültige RSA-Signatur).
Der Server muss die Spiel-ID mit einem privaten Schlüssel signieren und innerhalb von 2.880 Blöcken eine gültige RSA-Signatur senden. Die Option wird verwaltet, während der Smart Contract bereitgestellt wird. Wenn in der angegebenen Zeit nichts passiert, gewinnt der Benutzer. In diesem Fall muss die Belohnung vom Benutzer unabhängig gesendet werden. Es stellt sich heraus, dass Betrug für den Server unrentabel ist, da dies zu einem Verlust führt. Es gibt unten ein Beispiel.

Der Benutzer spielt
Dice Roller . Er wählt 2 von 6 Würfelflächen mit einem Einsatz von 14 WELLEN. Wenn der Server innerhalb einer festgelegten Zeit (2.880 Blöcke) keine gültige RSA-Signatur an den Smart-Vertrag sendet, erhält der Benutzer 34,44 WAVES.
Für die Zahlengenerierung verwenden wir ein Orakel, ein externes System anstelle der Blockchain. Der Server implementiert eine RSA-Signatur für die Spiel-ID. Der Smart-Vertrag prüft die Gültigkeit der Unterschrift und ermittelt den Gewinner. Wenn der Server nichts sendet, gewinnt der Benutzer automatisch.
Diese Methode stellt sicher, dass eine Manipulation technisch unmöglich ist. Alle Tradisys-Spiele basieren auf dem oben beschriebenen Algorithmus - um sicherzustellen, dass unsere Spiele fair und transparent sind. Alles kann öffentlich geprüft werden, um Ehrlichkeit zu gewährleisten.