¿Cómo puedo generar de forma segura un número aleatorio en mi contrato inteligente?

Si estoy escribiendo un contrato inteligente para algún tipo de juego (de apuestas), ¿cómo puedo generar un número aleatorio de forma segura? ¿Cuáles son las mejores prácticas y compensaciones de seguridad a tener en cuenta?

Esta es una pregunta muy interesante que puede tener muchas aplicaciones además de un juego de apuestas. La mejor aplicación que se me ocurre es un esquema de minería de criptomonedas basado en números verdaderamente aleatorios: simplemente dé la recompensa a un minero/usuario aleatorio. Una red con este esquema atraería a nuevos mineros, mientras que necesitaría muy poca electricidad para funcionar en comparación con la prueba de trabajo de Bitcoin, que no es más que un desperdicio de energía.
@Kozuch: aunque los números aleatorios confiables serían un gran paso, aún habría una serie de problemas con ese enfoque, por ejemplo, ataques de Sybil, etc.

Respuestas (20)

Hay algunas compensaciones y puntos clave a tener en cuenta en esta área.

  1. Cualquier decisión que tome un usuario que afecte el resultado le da a ese usuario una ventaja injusta. Ejemplos incluyen:

    • Usando un blockhash, marca de tiempo u otro valor definido por el minero. Tenga en cuenta que el minero tiene la opción de publicar un bloque o no, por lo que posiblemente podría tener una oportunidad de obtener el premio por bloque que extrae.
    • Cualquier número aleatorio enviado por el usuario. Incluso si el usuario se compromete previamente con un número, tiene la opción de revelarlo o no.
  2. Todo lo que ve el contrato, lo ve el público.

    • Esto significa que el número no debe generarse hasta después de que se haya cerrado la entrada a la lotería.
  3. El EVM no superará a una computadora física.

    • Cualquier número que genere el contrato podrá conocerse antes de que finalice ese bloque. Dejar tiempo entre la generación del número y su uso.

Ahora la técnica:

Compromiso no maleable al estilo de la lotería perfectamente descentralizado:

  1. El Casino reserva una recompensa por un número aleatorio
  2. Cada usuario genera su propio número aleatorio secretoN
  3. Los usuarios crean su compromiso haciendo hash Ncon su dirección: bytes32 hash = sha3(N,msg.sender)1
    • nota: los pasos 2 y 3 deben realizarse localmente, en secreto
  4. Los usuarios envían su hash al contrato, junto con ether mayor o igual en valor al valor del número aleatorio. 2
  5. Las presentaciones continúan durante una cierta cantidad de bloques, o hasta que se hayan unido suficientes participantes.

Una vez que finaliza la ronda de sumisión, comienza la ronda de revelación.

  1. Cada usuario envía su número aleatorio Nal contrato
  2. El contrato verifica que sha3(N,msg.sender)coincida con la presentación original.

Si el usuario no presenta un válido Na tiempo, su depósito se perderá.

  1. Los Ns están todos XORjuntos para generar el número aleatorio resultante.
  2. Este número se utiliza para determinar cuál de los participantes recibe la recompensa. ( N % numUsers) 3

Notas y alternativas:

1. Los usuarios deben concatenar su dirección a su Nantes del hashing. De lo contrario, otro usuario podría simplemente enviar un hash idéntico, luego esperar a Nque se revele y luego enviarlo. N XOR Nes igual a 0, por lo que esto podría usarse para cancelar todos los envíos excepto los del atacante.

2. Aquí es donde entran las compensaciones. La última persona en revelar su Ndecisión tiene la opción de revelar o no revelar. Esto esencialmente les da una doble oportunidad de ganar. Ingrese suficientes veces y obtendrá una nueva opción para cada entrada. Sugerencia: los mineros eligieron el orden de las transacciones en un bloque. Para desalentar esto, los usuarios deben realizar un gran depósito de seguridad, igual al valor que ganarían manipulando el número aleatorio. Esto podría ser un problema para muchos usuarios, especialmente para grandes premios mayores, incluso con optimizaciones de teoría de juegos.

  • Una alternativa comúnmente utilizada es un sistema semi-centralizado. Esto requiere que la "casa" envíe el primer hash y la última revelación. Si la casa no cumple con su deber, se devuelve el éter de todos. Esto tiene problemas, como que la casa decida quedarse sin dinero si el pago del premio mayor es inminente. La idea es que la reputación de la casa está en juego.
  • Tenga en cuenta que esto esencialmente centraliza todo el sistema. Uno simplemente necesita derribar la casa para que toda la operación se cierre. Este riesgo se puede reducir mediante la contratación de varios no jugadores de confianza para que sean los primeros/últimos en comprometerse.
  • Otro truco es utilizar terceros profesionales contratados para "minar" la aleatoriedad por usted, de modo que los jugadores no tengan que preocuparse por el proceso.

3. Es necesaria una recompensa para fomentar la competencia entre los participantes. Provoca una situación clásica de tragedia de los comunes/dilema del prisionero. La colusión entre los participantes les permitiría ganar un bote grande y dividirlo entre ellos, pero si todos saben lo que los demás enviarán, sabrán lo que deben enviar ellos mismos para ganar la recompensa. Por lo tanto, si la recompensa es mayor que el valor del número aleatorio dividido por la cantidad de jugadores, se incentiva a todos a mantener su propio número en secreto para tener una mejor oportunidad de obtener la recompensa. Tenga en cuenta que solo uno de los participantes debe enviar un buen número aleatorio y el resultado será impredecible.

Ejemplos: Chainlink VRF , RANDAO , El hilo donde pensé por primera vez en esto

Pensando en voz alta aquí: ¿qué tal usar un oráculo (o varios) para obtener el SHA-256 de un número de bloque de Bitcoin en particular (aún no encontrado) como semilla? ¿Funcionaría esto para los juegos de Ethereum donde el pago es inferior a la recompensa de minar un bloque BTC? Debido a que el costo de encontrar un bloque BTC es muy alto, decidir "no publicar" el bloque y perder la recompensa de 12,5 BTC más las tarifas de tx no tendría sentido si el pago del juego Ethereum es inferior a, digamos, 12,5 BTC.
Esta es realmente una idea muy interesante. Tendría que esperar algunas confirmaciones, pero definitivamente aumentaría el costo de manipular los resultados. Creo que haré un contrato para hacer esto...
@TjadenHess ¿Podemos simplemente construir el número aleatorio secuencialmente a partir de futuros hashes de bloque ETH bit a bit? El primer bit del número aleatorio es el bit más a la derecha del futuro hash del bloque #1, el segundo bit es el bit más a la derecha del futuro hash del bloque #2, etc. hasta algún futuro bloque #L. Si al final obtenemos más 1 que 0, entonces es "cara", de lo contrario, es "cruz". Ningún bloque individual puede influir significativamente en el resultado (ni siquiera el último). Al aumentar la longitud L, puede hacer que sea progresivamente más costoso para cualquier minero hacer trampa continuamente.
@akhmed Esa es una idea interesante, pero a medida que crece L, la influencia de cualquier minero individual crecerá , no disminuirá, ya que en promedio todos los mineros honestos están sacando 1 y 0 con la misma probabilidad, por lo que cada bloque que hace el minero malicioso cambia el total 1s vs 0s más desiguales
El potencial de manipulación de esta técnica es peor de lo que describe aquí. Imagine un minero que ha ingresado 100 direcciones. Tendrá que pagar 100 depósitos, pero por cada una de sus entradas puede elegir revelarlas para poder verificar cada combinación, dándole 2^100 oportunidades de ganar... o al menos, tantas oportunidades como su hardware pueda generar. en unos segundos
Eso es exactamente lo que describí... "Ingrese suficientes veces y obtendrá una nueva opción para cada entrada". Es decir, obtiene 2 opciones por entrada, por lo que n entradas permite 2 ^ n opciones
Creo que puede manipular el resultado de manera más eficiente que eso: cree mentradas N0, ..., Nmdonde mestá el número de bits en cada entrada y Nison m-(i+1)ceros seguidos de un 1 seguido de imás ceros, por ejemplo, N3= 0...01000. En la etapa de revelación, ahora puede voltear el jbit menos significativo en la combinación de entradas XOR al revelar su entrada correspondiente Nj. Por lo tanto, puede cambiar la combinación XOR'd a lo que quiera y, en particular, asegurarse de que N % numPlayerssea igual a una de sus entradas reveladas.
¿Podría dar un ejemplo para el paso 4? ¿Qué quiere decir con: "junto con éter mayor o igual en valor al valor del número aleatorio". ? Si el número aleatorio es 10, ¿no es costoso enviar 10 ether al contrato? y ¿Cuál es el valor más pequeño y más grande para N? @Tjaden Hess♦
Esta solución es ineficiente en términos de memoria. Si cada usuario compra 100 boletos, debe generar 100 hashes diferentes (necesita realizar 100 transacciones para enviar su hash al contacto), donde los cientos de hashes también deben almacenarse en el almacenamiento del contrato. @Tjaden Hess♦
En el paso 8: Ns y BLOCKHASHtodos juntos también podrían ser XOR'd para generar otro número aleatorio? @Tjaden Hess♦
@TjadenHess Puede que me esté perdiendo algo aquí. ¿Por qué no esperar al siguiente bloque y usar su hash como número aleatorio?
Supongo que me perdí algo, pero en el paso 8, ¿por qué XORing, sabiendo que el último Npuede poner en peligro la aleatoriedad de la salida? (como sugiere en su primera nota al pie, diciendo " N XOR N") Estaba pensando en concatenar todo N, luego hacer un hash del resultado. De esa manera, nadie puede "cancelar" la entropía, solo dejar de contribuir a ella (por ejemplo, con un valor notable como 0). ¡Ah, y gracias por sus contribuciones a esta pregunta!
La función exacta utilizada para calcular el resultado final no importa mucho, también podría hacer un hash por pares. El punto es que las N están comprometidas de antemano, por lo que no hay forma de saber cuál será la contribución. Es algo similar al resonar detrás de una libreta de una sola vez; cada bit tiene una probabilidad del 50/50 de ser 1, por lo que no hay forma de elegir su N para manipularlo.

Tenga en cuenta que con la sugerencia de linagee, no solo está confiando en random.org, también está confiando en Oraclize. Oraclize publica una prueba notarial de TLS para demostrar que los datos que le brindan realmente provienen de random.org, pero eso no es suficiente para este caso: necesitamos saber que estos fueron los únicos datos que obtuvieron de random.org. De lo contrario, podrían seguir intentándolo y descartando números aleatorios de random.org hasta que consiguieran uno que ganara su apuesta, y no tendrías forma de detectarlo.

Sin mencionar que la prueba no está verificada por el contrato, se verifica fuera de la cadena. Así, podrían quedarse con el dinero cuando quisieran.
Derecha. Estos juegos de dados son en realidad el único caso en el que puedo pensar en el que estas pruebas podrían ser realmente útiles, porque generalmente se entregan muy rápido, por lo que si puede detectar el fraude de manera automática y hacer sonar el silbato, es posible que pueda mantener el daño bastante pequeño. Sin las pruebas, aún descubriría el fraude (las personas que fueron jodidas gritarían asesinato azul y la gente cotejaría las fuentes de datos originales), pero podría ser un poco más lento. Pero es solo mitigación, no prevención, y como digo, necesita una fuente de datos determinista, no un número aleatorio.

Puede usar https://api.random.org/json-rpc/1/ que le brinda una fuente aleatoria de datos a través de JSON y Oraclize, lo que le permite utilizar el feed dentro de un contrato de Ethereum y, opcionalmente, autenticarlo fuertemente. como si hubiera venido de random.org. (Junto con los métodos existentes para usar el hash del bloque, la marca de tiempo y demás).

Estaría "confiando" en random.org para proporcionarle datos aleatorios. Puede mitigar el riesgo utilizando múltiples fuentes de aleatoriedad.

Advertencias adicionales sobre este enfoque aquí ethereum.stackexchange.com/a/1655/42 (en caso de que las respuestas se separen)

Dado que los diferentes contratos aseguran diferentes cantidades de valor , en el extremo opuesto del espectro de RANDAO se encuentra el simple BLOCKHASH .

Si BLOCKHASH puede satisfacer su propósito, se recomienda encarecidamente que revise la pregunta (a continuación se muestra solo un fragmento):

¿Cuándo se puede usar BLOCKHASH de manera segura para un número aleatorio? ¿Cuándo sería inseguro?

Como regla general, BLOCKHASH solo se puede usar de manera segura para un número aleatorio si la cantidad total de valor que se basa en la calidad de esa aleatoriedad es menor que lo que gana un minero extrayendo un solo bloque.

Creo que es una mala idea tener algún valor descansando en un blockhash. Tenga en cuenta que si el valor total , entre todos los contratos en la red, que descansan en ese blockhash es más de 5 Ether, existe un incentivo para que los mineros hagan trampa.
Votado a favor. Para aclarar a los jugadores de loterías pequeñas que usan blockhash, pueden ser "atacados" aunque una lotería tenga un límite de, digamos, 1 Finney. Si hay más de 5000 jugadores que apuestan 1 Finney, eso excede la recompensa de minería de 5 Ether y hay un incentivo para que un minero haga trampa.
Y tenga en cuenta que esto también se aplica a la marca de tiempo y a cualquier combinación de las dos. Aunque nunca deberías usar la marca de tiempo.
Pregunta relevante sobre la marca de tiempo y cómo es más vulnerable a los ataques que blockhash: ethereum.stackexchange.com/q/413/42
Bajo prueba de participación, ¿significa esto que BLOCKHASH casi nunca es seguro? (a menos que las tarifas sean muy altas, ya que un participante solo pierde una fracción del interés compuesto al no empujar un bloque)
Ha pasado un tiempo, pero me gustaría abordar el comentario anterior. Tiene razón, la seguridad del blockhash varía inversamente con el valor de cada bloque. Pero lo que es más importante, en PoS es muy fácil crear múltiples versiones de un bloque, mientras que con PoW lleva mucho tiempo. Por lo tanto, ni siquiera necesita renunciar a un bloque para cambiar el blockhash. El sistema PoW ideal tendría tiempos de bloque muy largos en relación con el valor en juego y bloques de valor muy alto
@eth el minero aún puede tener incentivos para hacer trampa en la lotería de 1 finney, INCLUSO si hay menos de 5000 jugadores, porque la creación de tarifas de transacción puede compensar la diferencia.

Para hacerlo en la práctica, use un Chainlink VRF .

Conceptualmente, use esta respuesta .

Para hacer el número aleatorio, necesitamos algunas variables:

keyhash: El hash de clave pública del oráculo(s). Esto es para ASEGURARSE de que están haciendo un número aleatorio basado en la semilla que le damos). Vamos a utilizar el oráculo VRF de Chainlink de ropsten.

userProvidedSeed : Una semilla de nuestra elección, esta es otra pieza que vamos a usar para demostrar que nos están haciendo un número aleatorio. Cada vez que llamamos a rollDice, debemos usar una semilla diferente. Consulte la selección de una semilla para obtener más información. Estos son los pasos n.º 2 "Cada usuario genera su propio número aleatorio secreto N" y n.º 3 "Los usuarios crean su compromiso codificando su N con su dirección: bytes32 hash = sha3(N,msg.sender)1" en esta respuesta .

_vrfcoordinatory _link: Estas son la dirección del contrato del vrfcoordinator y el token Chainlink. Están codificados para Ropsten en este contrato.

A continuación se muestra un ejemplo completo.

Para implementar el contrato, use las direcciones _vrfCoordinator y _link proporcionadas en los comentarios. Luego use su número hash como el userProvidedSeed.

pragma solidity 0.6.2;

import "https://raw.githubusercontent.com/smartcontractkit/chainlink/develop/contracts/src/v0.6/VRFConsumerBase.sol";

contract Verifiable6SidedDiceRoll is VRFConsumerBase {
    using SafeMath for uint;
    
    bytes32 internal keyHash;
    uint256 internal fee;
    
    event RequestRandomness(
        bytes32 indexed requestId,
        bytes32 keyHash,
        uint256 seed
    );
    
    event RequestRandomnessFulfilled(
        bytes32 indexed requestId,
        uint256 randomness
    );
    
    /**
     * @notice Constructor inherits VRFConsumerBase
     * @dev Ropsten deployment params:
     * @dev   _vrfCoordinator: 0xf720CF1B963e0e7bE9F58fd471EFa67e7bF00cfb
     * @dev   _link:           0x20fE562d797A42Dcb3399062AE9546cd06f63280
     */
    constructor(address _vrfCoordinator, address _link)
        VRFConsumerBase(_vrfCoordinator, _link) public
    {
        vrfCoordinator = _vrfCoordinator;
        LINK = LinkTokenInterface(_link);
        keyHash = 0xced103054e349b8dfb51352f0f8fa9b5d20dde3d06f9f43cb2b85bc64b238205; // hard-coded for Ropsten
        fee = 10 ** 18; // 1 LINK hard-coded for Ropsten
    }
    
    /** 
     * @notice Requests randomness from a user-provided seed
     * @dev The user-provided seed is hashed with the current blockhash as an additional precaution.
     * @dev   1. In case of block re-orgs, the revealed answers will not be re-used again.
     * @dev   2. In case of predictable user-provided seeds, the seed is mixed with the less predictable blockhash.
     * @dev This is only an example implementation and not necessarily suitable for mainnet.
     * @dev You must review your implementation details with extreme care.
     */
    function rollDice(uint256 userProvidedSeed) public returns (bytes32 requestId) {
        require(LINK.balanceOf(address(this)) > fee, "Not enough LINK - fill contract with faucet");
        uint256 seed = uint256(keccak256(abi.encode(userProvidedSeed, blockhash(block.number)))); // Hash user seed and blockhash
        bytes32 _requestId = requestRandomness(keyHash, fee, seed);
        emit RequestRandomness(_requestId, keyHash, seed);
        return _requestId;
    }
    
    function fulfillRandomness(bytes32 requestId, uint256 randomness) external override {
        uint256 d6Result = randomness.mod(6).add(1);
        emit RequestRandomnessFulfilled(requestId, randomness);
    }
    
}

Nota: En este momento, Chainlink VRF no tiene la XORfunción de envío descentralizado, esto aún está en desarrollo. Consulte el blog de Chainlink para obtener más detalles.

La respuesta corta es que no puedes. RANDAO funciona, pero es lento, y si su juego es popular, habrá un fuerte incentivo para que la gente juegue aplicando el último número.

Sugiero usar un oráculo para proporcionar aleatoriedad. (como se menciona en Notas y alternativas 2c de Tjaden Hess). Los dos principales beneficios son que puede afirmar firmemente que su número es independiente de cualquier otra apuesta, lo cual es vital para valorar el riesgo de usar ese número. En segundo lugar, efectivamente no hay límite en el rendimiento de entropía. Si hay un mercado de oráculos, puede aprovechar el historial irrevocable de la cadena de bloques para modelar la probabilidad de que un oráculo determinado esté en connivencia con los jugadores o no. Por supuesto, los jugadores, la casa y el oráculo proporcionan su propia entropía. Se pueden agregar otras características, como listas blancas, listas negras, bonos que afirman la no colusión, etc., según se desee.

Me parece que las diversas sugerencias presentadas aquí tienen en común un modelo en el que un contrato inteligente actúa como servidor en lo que es esencialmente una arquitectura normal de un solo servidor/cliente múltiple, y gran parte de la preocupación es que un Miner jugará con el sistema, que es el problema que siempre ha existido cuando hay un solo servidor confiable.

¿Por qué no poner las cosas de lado y simplemente no involucrar el contrato en absoluto en el proceso de generación de números aleatorios?

Un juego similar a la lotería podría funcionar de esta manera:

  • Cada jugador envía una transacción al contrato de lotería que contiene la tarifa, un número "seleccionado" deseado y un valor semilla aleatorio encriptado. Si el mismo jugador quiere elegir otro número, solo se requiere la tarifa y la selección: la semilla es por jugador, no por apuesta.

  • Después de que se cierran las apuestas, el jugador envía otra transacción, esta vez que no contiene nada más que la clave para descifrar su semilla.

  • Después de que todos hayan proporcionado las claves de descifrado (o haya transcurrido una cantidad de tiempo suficiente), cada jugador obtiene todas las claves y las semillas cifradas y calcula el número aleatorio "real", probablemente solo una suma modular de las semillas descifradas. Si es una de las selecciones del jugador, se envía una transacción al contrato que hace que el contrato verifique que el jugador proporcionó una semilla, verifique que el número ganador proporcionado en la transacción sea correcto y que haya sido seleccionado por el jugador, y luego otorgar las ganancias.

Aquí hay muchos detalles que agitan las manos y se omiten, pero creo que la idea básica es sólida.

¿Objeciones de seguridad a esto, alguien? Esto me parece sensato y parece eliminar los problemas de confianza/oráculo señalados en otras respuestas. Las claves secretas reveladas verifican absolutamente que los valores iniciales cifrados se crearon previamente y cuáles eran. @jimkberry, ¿qué quieres decir con "Si el jugador quiere elegir otro número...?" Otro numero cuando? ¿En otra ronda de apuestas (cuando todos los demás usuarios también eligen un número; cuando se emplea el contrato inteligente para crear un nuevo pago)? ¿Después de que ya hayan elegido un número (anulando su elección anterior)?
@r_alex_hall: Wow, ni siquiera recuerdo haber respondido esta pregunta, ha pasado tanto tiempo... Dado que estaba saludando con la mano en términos de lotería, estoy bastante seguro de que "elegir otro número" me refería al equivalente de "comprar otro billete" en una lotería tradicional.
@jimkberry Creo que el crítico de esta solución fue el último revelador que tuvo la oportunidad de no revelar. por lo que tendría doble oportunidad. (puede calcular si gana la lotería al no revelar) y tenga en cuenta que realmente no puede saber si la persona que no revela es la persona ganadora, porque es fácil tener múltiples identidades. Entonces, la única solución es un depósito para cada persona que es más costoso que tener una "segunda oportunidad", ¿lo cual no es tan bueno para la experiencia del usuario?
@jimkberry imagina que 1,2,3,4 y 5 están jugando el juego 3 y 5 son la misma "persona", 1 2 3 4 revela sus claves de descifrado y luego 5 puede optar por no revelar si 3 ya está ganando.

¿Por qué no utilizar simplemente las cifras oficiales de la lotería de la televisión? Por ejemplo, el Lotto Zahlen alemán. Luego, crea un mecanismo de votación en el que los jugadores pueden votar los números ganadores después de que los números de la lotería hayan sido sorteados y publicados en la televisión. Para incentivar también a los perdedores a votar, podría ofrecer a los jugadores que reembolsen, digamos, el 5 % de su apuesta si deciden votar y su voto resulta ser consensuado entre todos los jugadores.

Y si los perdedores superan en número a los ganadores, ¿por qué los perdedores no votarán en contra de la verdad oficial?
Necesita un Oracle para esta solución. Aunque es seguro, cuesta.

He publicado eth-random , que es una guía simplificada sobre cómo implementamos RNG en CryptoKitties.

Hay algunas advertencias al respecto, pero hasta este momento no he visto un solo intento de pirateo fundado en él.

Actualización 1: para ver un ejemplo práctico de la implementación, consulte el código fuente del contrato CK - funciones _breedWith(actuar como confirmación) y giveBirth+ geneScience.mixGenes(actuar como revelación).

¡Agradable! Sin embargo, ahorre en detalles ... no parece haber una descripción básica de lo que realmente es el RNG.
Oye, @Chan-HoSuh, es más una descripción de una técnica que una implementación en particular, porque actualmente es imposible crear solo un equivalente de Math.random() en Ethereum.
Fabiano, ¡gracias por publicar la actualización!

Pensemos que está utilizando un hash de bloque para decidir un ganador de lotería y el minero A participa en esa lotería y compra un boleto. Luego extrae muchos bloques válidos y los tira, si no se ajustan a su boleto.

En la práctica, cada bloque válido que tira un minero le cuesta 5 éteres. Si las probabilidades de su lotería son tales, ese minero A tiene que, por ejemplo, crear 1 000 000 de bloques válidos hasta que encuentre un bloque adecuado para ganar, tirará 5 000 000 éteres.

Si la ganancia principal de su lotería es 1 000 000 éteres, no tiene sentido que un minero tire 5 000 000 eth para ganar 1 000 000.

El problema con este análisis es que asumes que el minero solo está comprando un boleto. ¿Qué sucede si está comprando la mitad de los boletos o, en el peor de los casos, todos menos uno? Entonces las probabilidades son mucho mayores.

Tengo una idea que se basa en el protocolo de Tjaden que describo aquí:

¿Es este enfoque de una lotería ethereum sonido y/o novedoso?

Resuelve el problema de tener que poner un gran depósito de seguridad en las loterías. Agradecería algunos comentarios.

No conozco el mejor método seguro, pero vea a continuación una lista de implementaciones vulnerables (así que finalmente no lo use ):

  • PRNG que utilizan variables de bloque como fuente de entropía
  • PRNG basados ​​en un blockhash de algún bloque anterior
  • PRNG basados ​​en un blockhash de un bloque anterior combinado con una semilla considerada privada
  • PRNG propensos a la delantera

Puede echar un vistazo a la predicción de números aleatorios en los contratos inteligentes de Ethereum , que explica todos los problemas de seguridad relacionados con los diferentes tipos de generadores de números pseudoaleatorios (PRNG) implementados en (muchos de los existentes) contratos inteligentes.

Solo una actualización de esta pregunta, pero esta vez usa una función de retraso verificable de llamada técnica (vdf). Fue construido en base al rompecabezas de bloqueo de tiempo inventado por Ronald L Rivest et al . La idea central detrás es que si no puede calcular el resultado en un período de tiempo determinado, entonces el resultado es pseudo aleatorio. Entonces, con eso en mente, vdf es una función que toma una entrada y puede tardar 1 hora en calcularse (puede cambiar el tiempo en la fase de configuración de esta función), luego muestra el resultado y una evidencia de que este resultado es de hecho, calcule usando esa entrada en poco tiempo.

Entonces, la idea es como el Compromiso no maleable de estilo de lotería perfectamente descentralizado que se propuso en la respuesta de esta pregunta, pero solo necesitamos agregar vdf en un paso antes

  1. Todas las N se combinan con XOR para generar el número aleatorio resultante.

Entonces nuestro paso es:

  1. Con ese resultado, dejamos que se ejecute a través de nuestra función vdf (el tiempo para calcularlo debe ser más largo que el tiempo de envío para garantizar que el último envío no pueda calcular el resultado final).

  2. Luego, publica el resultado de la función vdf para todos, de modo que todos puedan verificar que el resultado se calculó usando la entrada

La recompensa es la misma que la propuesta de respuesta.

Pero solo simplifico la idea central detrás de esto, la belleza de esto está en las matemáticas que lo implementan.

Documento de función de retardo verificable: https://eprint.iacr.org/2018/601.pdf

También puede consultar este documento que habla de 2 vdf: https://crypto.stanford.edu/~dabo/pubs/papers/VDFsurvey.pdf

La generación segura de un número aleatorio es difícil debido a la naturaleza determinista de la EVM. Hay muchos enfoques. Cuál funciona mejor depende de sus requisitos de seguridad y contexto. Aquí hay algunos:

Hash de bloque anterior

Si su número aleatorio no es crítico para la seguridad, simplemente puede usar el hash de bloque anterior. Por ejemplo, un número aleatorio de 0 a 99:uint256(block.blockhash(block.number-1)) % 100

oráculo de confianza

Utilice un oráculo en el que confíe para obtener un número y realizar una devolución de llamada a su contrato inteligente.

Comprometerse y revelar

  1. A genera un número r
  2. A escribe keccak256(r) en el contrato inteligente
  3. B se compromete con cualquier evento que deba ser aleatorio
  4. A revela r

A no debe poder predecir con qué evento se comprometerá B.

A tiene pruebas de que B no podría haber sabido el número cuando B se comprometió con el evento

B tiene pruebas de que el número se generó antes de comprometerse con el evento aleatorio

¿Qué tal este flujo:

1) Cada usuario genera una cadena "Soy parte de la lotería y mi número es 8272143" (en secreto) donde 8272143 es el número elegido por él mismo.

2) Cada usuario encripta su propia cadena con una contraseña elegida por él mismo (secreta)

3) Cada usuario publica la cadena cifrada, por lo que ahora todos pueden ver todas las cadenas cifradas

4) Cuando se decide no permitir más participantes, todos los usuarios publican sus contraseñas elegidas por ellos mismos y todas las cadenas se descifran. Los usuarios que no publiquen sus contraseñas quedan excluidos de ganar.

5) Todos pueden confirmar que cada jugador dice la verdad sobre su contraseña, ya que la primera parte de la cadena debe ser "Soy parte de la lotería y mi número es ", por lo que no hay forma de falsificar otro número en este punto. .

6) Todas las cadenas ahora están concatenadas y se genera un hash. ¡Este hash es el número aleatorio final! En el caso de una lotería, puede elegir el número más cercano al hash, pero esto es solo un detalle específico del caso.

De esta manera, no habrá que esperar a que se completen los bloques. Solo el descifrado por fuerza bruta de las cadenas permitirá obtener una ventaja. Esto se evitará mediante el uso de un cifrado lo suficientemente fuerte.

(Soy un novato total en este campo, así que no me digas si esto es una tontería).

ACTUALIZAR:

Me doy cuenta de que si 2 clientes trabajan juntos, el último cliente puede elegir esperar a publicar su contraseña y ser excluido en caso de que su amigo gane de esta manera. Una solución puede ser que todos los usuarios tengan una cantidad de ETH en la línea que tendrán que pagar si no publican su contraseña. Con todo, esta solución no es muy sexy, lo admito.

¿Me parece que esto y la respuesta de @jimkberry están duplicados? el de jimkberry fue el primero ;)

Otra forma de generar números aleatorios podría ser distribuir varias fuentes de aleatoriedad en la plataforma para generar el boleto ganador. Por ejemplo, podríamos usar los centavos de los precios de cierre de todas las acciones negociadas en las principales bolsas de valores del mundo. Sería casi imposible hacer que todas las bolsas de valores trabajen juntas para manipular la lotería.

Los precios de cierre son mostrados por varios servicios, incluidos Yahoo, Google, Bloomberg, etc., por lo que pueden funcionar como una especie de libro de contabilidad público para verificar que los precios sean correctos.

Además, dado que tantos comerciantes directos, algoritmos, comerciantes de empresas, etc. están involucrados en cambiar esos precios, sería casi imposible predecir cuál será el último precio negociado o esperar hasta el último segundo para ser la última persona en negociar un Valores.

Pruebe el siguiente algoritmo para evitar trampas.

La idea es usar el hash de un bloque, pero los mineros/participantes y organizadores no conocen el número de bloque para evitar trampas.

  1. El organizador de inicio define un uint64 aleatorio: número clave que se utilizará en el cálculo del número aleatorio ganador. El número de unidad es lo suficientemente grande y el organizador lo conoce solo al inicio.
  2. El organizador corrige el número clave al proporcionar el hash de la suma: el número más el hash del bloque actual (como una sal). El hash clave se almacena en el contrato de Lotería y está disponible para todos los participantes. Luego, se usa el hash clave para evitar que los organizadores hagan trampa y corrige el número clave.
  3. Los participantes compran boletos y el contrato de Lotería almacena todas las direcciones de los participantes. Las direcciones también se utilizan en el cálculo del ganador.
  4. Cuando se cumplen las condiciones (p. ej., se venden todas las entradas o se genera la cantidad necesaria de bloques o justo después de un tiempo), el organizador inicia los cálculos del ganador.
  5. Organizador proporciona el número de clave almacenado y confirma que el número es exactamente el mismo calculando el hash de clave (ingresado en init).
  6. El número clave se agrega a la suma de direcciones y obtuvimos el mod 255 de la suma para detectar el número de bloque cuyo hash se usa para detectar al ganador. Número de clave + dirección1 + dirección2 ... + direcciónN % 255 = número de bloque ganador (el hash de bloque se usa para obtener el número ganador)

Para la persona que quiere adivinar el número ganador, no es posible obtener el número; en el contrato, solo tenemos un hash del número. El número es bastante grande uint64 (o podría ser incluso mayor) para evitar "deshacer" el número. Para los organizadores, el número es fijo y es inútil porque se deben conocer todas las direcciones de los propietarios de boletos para calcular el ganador.

¿Opiniones? ¿Algún agujero en la lógica?

Creé una pregunta separada, pero también podría responderse aquí.

Necesita usar un tercero para generar aleatoriedad.

Las cuentas de contrato solo realizan una operación cuando una cuenta de propiedad externa se lo indica. Por lo tanto, no es posible que una cuenta de contrato realice operaciones nativas, como la generación de números aleatorios o llamadas API; solo puede hacer estas cosas si se lo solicita una cuenta de propiedad externa. Esto se debe a que Ethereum requiere que los nodos puedan ponerse de acuerdo sobre el resultado del cálculo, lo que requiere una garantía de ejecución estrictamente determinista.

Lea más: http://ethdocs.org/en/latest/introduction/what-is-ethereum.html#how-does-ethereum-work

Hay oráculos de aleatoriedad como Chainlink VRF disponibles hoy en día.

Marco de Oracle aquí. Podría utilizar la fuente de datos aleatoria de Oraclize, que aprovecha un entorno de ejecución de confianza de libro mayor .

Puedes leer una introducción aquí y algunos ejemplos aquí .

Random Data Source es significativamente más seguro que usar Random.org con TLSNotary y usar blockhash para determinar un número aleatorio, y es menos difícil y costoso que otros esquemas de confirmación y revelación.

Los enlaces están rotos.