RSA random on the blockchain

There is a problem - it is difficult to generate a random number in a decentralized network. Almost all blockchains have already faced this. Indeed, in networks where there is no trust between users, the creation of an undeniable random number solves many problems.

In the article we tell how we managed to solve the problem using the example of games. The first of these was Waves Xmas Tree. For development, we needed a random number generator.

RSA random on the blockchain

Initially, we planned to generate a number based on information from the blockchain. However, then it became clear: the number can be manipulated, which means that the solution is not suitable.

We came up with a workaround: use the "commit-discovery" scheme. The server “guessed” a number from 1 to 5, added a “salt” to it, and then hashed the result with Keccak functions. The server deployed the smart contract in advance with the already saved number. It turns out that the game boils down to the fact that the user guessed the number hidden by the hash.

The player made a bet, and the server sent the hidden number and “salt” to the smart contract. In simple terms, he opened the cards. After that, the server checked the numbers and decided whether the user won or lost.

If the server did not send a number or "salt" for verification, the user won. In this case, for each game, it was necessary to deploy a smart contract in advance and put a potential win into it. It turned out to be inconvenient, long and expensive. At that time, there was no other safe solution.

Recently, the Tradisys team proposed adding a feature to the Waves protocol rsaVerify(). It checks the validity of an RSA signature based on the public and private keys. As a result, the function was added.

We have developed three games: Says Roller, Coin flip и Ride On Waves. Each implements a random number technology. Let's see how it works.

RSA random on the blockchain

Consider the generation of a random number on the example of Ride on Waves. Smart contract can be found here.

Go to the tab Script and then decompiled. See the smart contract code (aka script).

RSA random on the blockchain

The smart contract code contains a set of functions. Those marked with @Callable can be launched with Invocation transactions. We are interested in two functions: bet и withdraw:

  • func bet (playerChoice)
  • func withdraw(gameId,rsaSign)

1. The user chooses the length of the segment and the size of the bet.

RSA random on the blockchain

2. The client generates a bet function. For the image above it would be bet("50").

3. The client sends an Invocation transaction to the address of the smart contract (broadcast InvocationTx). The transaction contains the bet function as a call parameter. This means that the Invocation transaction starts the execution of the bet function (choice: String) on ​​the smart contract.

RSA random on the blockchain

4. Consider the bet function:

@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)))
                    }
    }

The function writes a new game to the smart contract state. Namely:

  • Unique identifier for the new game (game id)
  • Game state = SUBMITTED
  • Player's choice (line length 50)
  • public key
  • Potential winnings (depends on the player's bet)

RSA random on the blockchain

This is how the data entry in the blockchain (key-value) looks like:

{
    "type": "string",
    "value": "03WON_0283_448t8Jn9P3717UnXFEVD5VWjfeGE5gBNeWg58H2aJeQEgJ_06574069_09116020000_0229",
    "key": "2GKTX6NLTgUrE4iy9HtpSSHpZ3G8W4cMfdjyvvnc21dx"
  }

"Key" (key) - game id new game. The rest of the data is contained in the value field line. These records are stored in the tab Data smart contract:

RSA random on the blockchain

RSA random on the blockchain

5. The server “looks” at the smart contract and finds the sent transaction (new game) using the Api blockchain. The game id of the new game is already recorded in the blockchain, which means that it can no longer be changed or influenced

6. The server generates the withdraw function (gameId, rsaSign). For example, this one:

withdraw ("FwsuaaShC6DMWdSWQ5osGWtYkVbTEZrsnxqDbVx5oUpq", "base64:Gy69dKdmXUEsAmUrpoWxDLTQOGj5/qO8COA+QjyPVYTAjxXYvEESJbSiCSBRRCOAliqCWwaS161nWqoTL/TltiIvw3nKyd4RJIBNSIgEWGM1tEtNwwnRwSVHs7ToNfZ2Dvk/GgPUqLFDSjnRQpTHdHUPj9mQ8erWw0r6cJXrzfcagKg3yY/0wJ6AyIrflR35mUCK4cO7KumdvC9Mx0hr/ojlHhN732nuG8ps4CUlRw3CkNjNIajBUlyKQwpBKmmiy3yJa/QM5PLxqdppmfFS9y0sxgSlfLOgZ51xRDYuS8NViOA7c1JssH48ZtDbBT5yqzRJXs3RnmZcMDr/q0x6Bg==")

7. The server sends an Invocation transaction to the smart contract (broadcast InvocationTx). The transaction contains a call to the generated withdraw function (gameId, rsaSign):

RSA random on the blockchain

Function contains game id new game and the result of the RSA signature of the unique identifier with the private key. The result of the signature is unchanged.

What does this mean?

We take the same value (game id) and apply the RSA signature method to it. We will always get the same result. This is how the RSA algorithm works. The final number cannot be manipulated because the game id and the result of applying RSA is not known. Selecting a number is also pointless.

8. The blockchain accepts the transaction. It runs the withdraw function (gameId, rsaSign)

9. Withdrawal occurs inside the withdraw function GenerateRandInt Functions (gameId, rsaSign). This is the random number generator

# @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")
    }

all - is a random number.

First, the string is taken, which is the result of the RSA signature game id private key (rsaSign). Then hashed with SHA-256 (sha256(rsaSign)).

We cannot predict the outcome of the signature and subsequent hashing. Therefore, it is impossible to influence the generation of a random number. To get a number in a certain range (for example, from 1 to 100), the toInt conversion function and %100 are used (similar to against).

At the beginning of the article, we mentioned the function rsaVerify(), which allows you to check the validity of an RSA signature with a private key against a public one. Here is the GenerateRandInt(gameId,rsaSign) part:

rsaVerify (SHA256, toBytes(gameId), rsaSign, RSAPUBLIC)

The public key RSAPUBLIC and the string rsaSign are passed to the input. The signature is checked for validity. The number is generated in case of successful validation. Otherwise, the system considers that the signature is not valid (Invalid RSA signature).

The server must sign the game id of the game with a private key and send a valid Rsa signature within 2880 blocks. The parameter is configured when deploying a smart contract. If nothing happens within the allotted time, the user wins. In this case, the prize must be sent to your address yourself. It turns out that it is “not profitable for the server to cheat”, because this leads to a loss. Below is an example.

RSA random on the blockchain

The user is playing Says Roller. I chose 2 of the 6 faces of the die, the bet is 14 WAVES. If the server does not send a valid RSA signature to the smart contract within the set time (2880 blocks), the user will take 34.44 WAVES.

To generate numbers in games, we use an oracle - an external, non-blockchain system. The server performs the RSA signature of the game id. The smart contract checks the validity of the signature and determines the winner. If the server did not send anything, then the user automatically wins.

This is an honest generation method, because manipulation is technically impossible. All Tradisys games work on the basis of the described algorithm. This is how blockchain games work. Everything is transparent and verifiable. There are no analogues of such a system in any other blockchain. This is an honest random.

Source: habr.com

Add a comment