Blockchain aleatorio RSA

Hay un problema: es difícil generar un número aleatorio en una red descentralizada. Casi todas las cadenas de bloques ya se han encontrado con esto. De hecho, en redes donde no hay confianza entre los usuarios, la creación de un número aleatorio innegable resuelve muchos problemas.

El artículo explica cómo resolver el problema usando los juegos como ejemplo. El primero de ellos fue el Waves Xmas Tree . Para el desarrollo, necesitábamos un generador de números aleatorios.



Inicialmente, planeamos generar un número basado en la información de la cadena de bloques. Sin embargo, quedó claro: el número podría ser manipulado, lo que significa que la solución no encaja.

Se nos ocurrió una solución alternativa: utilice el esquema de confirmación de divulgación. El servidor "adivinó" un número del 1 al 5, le agregó "sal" y luego modificó el resultado utilizando la función Keccak . El servidor implementó previamente un contrato inteligente con un número ya guardado. Resulta que el juego se redujo al hecho de que el usuario adivinó el número oculto por el hash.

El jugador hizo una apuesta y el servidor envió el número solicitado y la "sal" al contrato inteligente. En lenguaje sencillo, reveló las cartas. Después de eso, el servidor verificó los números y decidió si el usuario ganó o perdió.

Si el servidor no envió un número o "sal" para la verificación, el usuario ganó. En este caso, para cada juego era necesario implementar un contrato inteligente por adelantado y depositar ganancias potenciales en él. Resultó ser inconveniente, largo y costoso. En ese momento, no había otra solución segura.

Recientemente, el equipo de Tradisys ha propuesto agregar la función rsaVerify () al protocolo Waves. Verifica la validez de la firma RSA en función de la clave pública y privada. Como resultado, se agregó la función.

Desarrollamos tres juegos: Dice Roller , Coin Flip y Ride On Waves . Cada uno implementó tecnología de números aleatorios. Veamos como funciona.



Considere la generación de números aleatorios usando Ride on Waves como ejemplo. Un contrato inteligente se puede encontrar aquí .

Vaya a la pestaña Script y seleccione Descompilado . Vea el código del contrato inteligente (también conocido como script).



El código de contrato inteligente contiene un conjunto de funciones. Los marcados como @Callable se pueden activar mediante transacciones de invocación . Estamos interesados ​​en dos funciones: apostar y retirar :

  • apuesta de func (playerChoice)
  • Func retirada (gameId, rsaSign)

1. El usuario selecciona la longitud del segmento y el tamaño de la apuesta.



2. El cliente forma una función de apuesta. Para la imagen de arriba, se apostará ("50") .

3. El cliente envía la transacción de Invocación a la dirección del contrato inteligente (difusión InvocationTx). Una transacción como parámetro de llamada contiene la función de apuesta. Esto significa que la transacción de Invocación inicia la ejecución de la función de apuesta (opción: Cadena) en el contrato inteligente.



4. Considere la función de apuesta:

@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 función escribe un nuevo juego en el estado del contrato inteligente. A saber:

  • Identificador único para un nuevo juego (identificación del juego)
  • Estado del juego = ENVIADO
  • Selección de jugador (longitud de línea 50)
  • Clave pública
  • Ganancias potenciales (depende de la apuesta del jugador)



Así es como se ve el registro de datos en la cadena de bloques (valor-clave):

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

"Clave" (clave): identificación del juego del nuevo juego. Los datos restantes están contenidos en la línea del campo "valor" (valor). Estas entradas se almacenan en la pestaña Datos del contrato inteligente:





5. El servidor "mira" el contrato inteligente y encuentra la transacción enviada (nuevo juego) utilizando la API de blockchain. El ID del juego del nuevo juego ya está registrado en la cadena de bloques, lo que significa que ya no es posible cambiarlo ni influir en él.

6. El servidor forma una función de retirada (gameId, rsaSign). Por ejemplo, esto:

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

7. El servidor envía la transacción de Invocación al contrato inteligente (difusión InvocationTx). La transacción contiene una llamada a la función de retiro generada (gameId, rsaSign):



La función contiene la identificación del juego del nuevo juego y el resultado de la firma RSA del identificador único con una clave privada. El resultado de la firma no se modifica.

¿Qué significa esto?

Tomamos el mismo valor (identificación del juego) y le aplicamos el método de firma RSA. Siempre obtendremos el mismo resultado. Así es como funciona el algoritmo RSA. No puedes manipular el número final, ya que se desconocen la identificación del juego y el resultado del uso de RSA. Igualar un número tampoco tiene sentido.

8. La cadena de bloques acepta la transacción. Ejecuta la función de retirada (gameId, rsaSign)

9. Dentro de la función de retiro, se exporta la función GenerateRandInt (gameId, rsaSign). Este es un generador de números aleatorios


 # @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 - y hay un número aleatorio.

Primero, se toma una cadena, que es el resultado de la firma RSA de la identificación del juego con la clave privada ( rsaSign ). Luego se procesa utilizando SHA-256 ( sha256 (rsaSign) ).

No podemos predecir el resultado de la firma y el hashing posterior. Por lo tanto, es imposible influir en la generación de un número aleatorio. Para obtener un número en un cierto rango (por ejemplo, de 1 a 100), se utiliza la función de conversión toInt y% 100 (analógica a mod ).

Al comienzo del artículo, mencionamos la función rsaVerify () , que le permite verificar la validez de las firmas RSA con una clave privada utilizando una clave pública. Aquí está la parte GenerateRandInt (gameId, rsaSign):

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

La clave pública RSAPUBLIC y la cadena rsaSign se pasan a la entrada. Se verifica la validez de la firma. Se genera un número si la verificación es exitosa. De lo contrario, el sistema considera que la firma no es válida (firma RSA no válida).

El servidor debe firmar la identificación del juego con la clave privada y enviar una firma Rsa válida dentro de 2880 bloques. El parámetro se configura al implementar un contrato inteligente. Si no sucede nada durante el tiempo asignado, el usuario gana. En este caso, el premio debe enviarse a su dirección usted mismo. Resulta que el servidor "no es rentable para hacer trampa", porque esto lleva a una pérdida. A continuación se muestra un ejemplo.



El usuario juega a Dice Roller . Elegí 2 de las 6 caras del cubo, la apuesta es 14 ONDAS. Si el servidor no envía una firma RSA válida en el contrato inteligente dentro del tiempo establecido (2880 bloques), el usuario tomará 34.44 ONDAS.

Para generar números en los juegos, usamos el oráculo, un sistema externo que no bloquea. El servidor implementa la firma RSA de la identificación del juego. El contrato inteligente verifica la validez de la firma y determina el ganador. Si el servidor no envió nada, el usuario gana automáticamente.

El método descrito garantiza que la manipulación sea técnicamente imposible. Todos los juegos de Tradisys usan un algoritmo, por lo que son honestos y transparentes. Todo se presta al escrutinio público. Esto asegura la honestidad.

Source: https://habr.com/ru/post/464357/


All Articles