Il y a un problĂšme - il est difficile de gĂ©nĂ©rer un nombre alĂ©atoire dans un rĂ©seau dĂ©centralisĂ©. Presque toutes les blockchains l'ont dĂ©jĂ rencontrĂ©. En effet, dans les rĂ©seaux oĂč il n'y a pas de confiance entre les utilisateurs, la crĂ©ation d'un nombre alĂ©atoire indĂ©niable rĂ©sout de nombreux problĂšmes.
L'article explique comment résoudre le problÚme en utilisant des jeux comme exemple. Le premier d'entre eux était l'
arbre de Noël Waves . Pour le développement, nous avions besoin d'un générateur de nombres aléatoires.

Au dĂ©part, nous avions prĂ©vu de gĂ©nĂ©rer un nombre basĂ© sur les informations de la blockchain. Cependant, il est devenu clair: le nombre pourrait ĂȘtre truquĂ©, ce qui signifie que la solution ne convient pas.
Nous avons trouvĂ© une solution de contournement: utilisez le schĂ©ma de validation et de divulgation. Le serveur a «deviné» un nombre de 1 Ă 5, y a ajoutĂ© du «sel», puis hachĂ© le rĂ©sultat Ă
l' aide de
la fonction Keccak . Le serveur a pré-déployé un contrat intelligent avec un numéro déjà enregistré. Il s'avÚre que le jeu a été réduit au fait que l'utilisateur a deviné le nombre caché par le hachage.
Le joueur a fait un pari et le serveur a envoyé le numéro demandé et le «sel» au contrat intelligent. En langage clair, il a révélé les cartes. AprÚs cela, le serveur a vérifié les numéros et a décidé si l'utilisateur avait gagné ou perdu.
Si le serveur n'a pas envoyĂ© de numĂ©ro ou de «sel» pour vĂ©rification, l'utilisateur a gagnĂ©. Dans ce cas, pour chaque jeu, il Ă©tait nĂ©cessaire de dĂ©ployer un contrat intelligent Ă l'avance et d'y mettre des gains potentiels. Cela s'est avĂ©rĂ© gĂȘnant, long et coĂ»teux. A cette Ă©poque, il n'y avait pas d'autre solution sĂ»re.
Récemment, l'équipe Tradisys a proposé d'ajouter la fonction
rsaVerify () au protocole Waves. Il vérifie la validité de la signature RSA en fonction de la clé publique et privée. En conséquence, la fonction a été ajoutée.
Nous avons développé trois jeux:
Dice Roller ,
Coin Flip et
Ride On Waves . Chaque technologie implémentée de nombres aléatoires. Voyons comment cela fonctionne.

ConsidĂ©rez la gĂ©nĂ©ration de nombres alĂ©atoires en utilisant Ride on Waves comme exemple. Un contrat intelligent peut ĂȘtre trouvĂ©
ici .
Accédez à l'onglet
Script et sélectionnez
Décompilé . Voir le code du contrat intelligent (aka script).

Le code de contrat intelligent contient un ensemble de fonctions. Ceux marquĂ©s comme @Callable peuvent ĂȘtre dĂ©clenchĂ©s Ă l'aide de
transactions d'invocation . Nous sommes intéressés par deux fonctions:
parier et
retirer :
- func bet (playerChoice)
- func retrait (gameId, rsaSign)
1. L'utilisateur sélectionne la longueur du segment et la taille du pari.

2. Le client forme une fonction de pari. Pour l'image ci-dessus, ce sera
parié ("50") .
3. Le client envoie la transaction Invocation à l'adresse du contrat intelligent (diffusion InvocationTx). Une transaction en tant que paramÚtre Call contient la fonction de mise. Cela signifie que la transaction d'invocation démarre l'exécution de la fonction de pari (choix: chaßne) sur le contrat intelligent.

4. Considérez la fonction de pari:
@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))) } }
La fonction écrit un nouveau jeu dans l'état du contrat intelligent. à savoir:
- Identifiant unique pour un nouveau jeu (identifiant de jeu)
- Ătat du jeu = SOUMIS
- Sélection des joueurs (longueur de ligne 50)
- Clé publique
- Gains potentiels (dépend de la mise du joueur)

Voici à quoi ressemble l'enregistrement de données dans la blockchain (valeur-clé):
{ "type": "string", "value": "03WON_0283_448t8Jn9P3717UnXFEVD5VWjfeGE5gBNeWg58H2aJeQEgJ_06574069_09116020000_0229", "key": "2GKTX6NLTgUrE4iy9HtpSSHpZ3G8W4cMfdjyvvnc21dx" }
"Clé" (clé) -
identifiant de jeu du nouveau jeu. Les données restantes sont contenues dans la ligne du champ «valeur» (valeur). Ces entrées sont stockées dans l'onglet
Données du contrat intelligent:


5. Le serveur «regarde» le contrat intelligent et trouve la transaction envoyée (nouveau jeu) en utilisant la blockchain Api. L'identifiant de jeu du nouveau jeu est déjà enregistré sur la blockchain, ce qui signifie qu'il n'est plus possible de le modifier ou de l'influencer
6. Le serveur forme une fonction de retrait (gameId, rsaSign). Par exemple, ceci:
withdraw ("FwsuaaShC6DMWdSWQ5osGWtYkVbTEZrsnxqDbVx5oUpq", "base64:Gy69dKdmXUEsAmUrpoWxDLTQOGj5/qO8COA+QjyPVYTAjxXYvEESJbSiCSBRRCOAliqCWwaS161nWqoTL/TltiIvw3nKyd4RJIBNSIgEWGM1tEtNwwnRwSVHs7ToNfZ2Dvk/GgPUqLFDSjnRQpTHdHUPj9mQ8erWw0r6cJXrzfcagKg3yY/0wJ6AyIrflR35mUCK4cO7KumdvC9Mx0hr/ojlHhN732nuG8ps4CUlRw3CkNjNIajBUlyKQwpBKmmiy3yJa/QM5PLxqdppmfFS9y0sxgSlfLOgZ51xRDYuS8NViOA7c1JssH48ZtDbBT5yqzRJXs3RnmZcMDr/q0x6Bg==")
7. Le serveur envoie la transaction d'invocation au contrat intelligent (diffusion InvocationTx). La transaction contient un appel à la fonction de retrait générée (gameId, rsaSign):

La fonction contient l'
identifiant de jeu du nouveau jeu et le résultat de la signature RSA de l'identifiant unique avec une clé privée. Le résultat de la signature est inchangé.
Qu'est-ce que cela signifie?Nous prenons la mĂȘme valeur (identifiant de jeu) et lui appliquons la mĂ©thode de signature RSA. Nous obtiendrons toujours le mĂȘme rĂ©sultat. C'est ainsi que fonctionne l'algorithme RSA. Vous ne pouvez pas manipuler le nombre final, car l'ID du jeu et le rĂ©sultat de l'utilisation de RSA ne sont pas connus. Faire correspondre un nombre est Ă©galement inutile.
8. La blockchain accepte la transaction. Il exécute la fonction de retrait (gameId, rsaSign)
9. Dans la fonction de retrait, la
fonction GenerateRandInt (gameId, rsaSign) est exportée. Ceci est un générateur de nombres aléatoires
# @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 - et il y a un nombre aléatoire.
Tout d'abord, une chaßne est prise, qui est le résultat de la signature RSA de l'
ID de
jeu avec la clé privée (
rsaSign ). Il est ensuite haché à l'aide de SHA-256 (
sha256 (rsaSign) ).
Nous ne pouvons pas prédire le résultat de la signature et du hachage ultérieur. Par conséquent, il est impossible d'influencer la génération d'un nombre aléatoire. Pour obtenir un nombre dans une certaine plage (par exemple, de 1 à 100), la fonction de conversion toInt et% 100 est utilisée (analogique vers
mod ).
Au début de l'article, nous avons mentionné la fonction
rsaVerify () , qui vous permet de vérifier la validité des signatures RSA avec une clé privée par clé publique. Voici la partie GenerateRandInt (gameId, rsaSign):
rsaVerify (SHA256, toBytes(gameId), rsaSign, RSAPUBLIC)
La clé publique RSAPUBLIC et la chaßne rsaSign sont transmises à l'entrée. La validité de la signature est vérifiée. Un numéro est généré si la vérification réussit. Sinon, le systÚme considÚre que la signature n'est pas valide (signature RSA non valide).
Le serveur doit signer l'id du jeu avec la clĂ© privĂ©e et envoyer une signature Rsa valide dans 2880 blocs. Le paramĂštre est configurĂ© lors du dĂ©ploiement d'un contrat intelligent. Si rien ne se passe pendant le temps imparti, l'utilisateur gagne. Dans ce cas, le prix doit ĂȘtre envoyĂ© Ă votre adresse vous-mĂȘme. Il s'avĂšre que le serveur n'est "pas rentable de tricher", car cela entraĂźne une perte. Voici un exemple.

L'utilisateur joue au
rouleau de dés . J'ai choisi 2 faces sur 6 du cube, le pari est de 14 ONDES. Si le serveur n'envoie pas de signature RSA valide sur le contrat intelligent dans le délai défini (2880 blocs), l'utilisateur prendra 34,44 ONDES.
Pour générer des nombres dans les jeux, nous utilisons l'oracle - un systÚme externe non bloquant. Le serveur implémente la signature RSA de l'ID de jeu. Le contrat intelligent vérifie la validité de la signature et détermine le gagnant. Si le serveur n'a rien envoyé, l'utilisateur gagne automatiquement.
La mĂ©thode dĂ©crite garantit que la manipulation est techniquement impossible. Tous les jeux Tradisys utilisent un algorithme, ils sont donc honnĂȘtes et transparents. Tout se prĂȘte Ă l'examen du public. Cela garantit l'honnĂȘtetĂ©.