Números aleatorios en Solidity: mejores y peores prácticas

Federico Elgarte
4 min readMar 8, 2018

--

Si tu proyecto está relacionado con gambling o necesita de la generación de números aleatorios por algo en particular, seguramente ya te has inspirado con proyectos en GitHub y has sacado buenas ideas. Basta con buscar palabras como "solidity lottery", "solidity random" o "solidity gambling" para ver un listado de files .sol de lo más variado.

Seguramente a los 5 minutos de haber empezado el research te has percatado que Solidity no tiene un método core que genere dichos números, y de tu mente a salido una frase del estilo "a este lenguaje le falta mucho", pero no… la razón por la que no tenemos un random() es válida y te la paso a explicar en seis palabras:

La Blockchain de Ethereum es determinística.

Cada nodo en la red tiene que ser capaz de devolver el mismo resultado dado el mismo input de datos. Imaginen una red en donde cada nodo devuelve un número distinto, la transacción terminaría con resultados diferentes y no sería posible llegar a un consenso.

Habiendo entendido el por qué, avancemos con las peores prácticas que utilizan los developers para llegar a un pseudo número aleatorio.

Peores practicas

Basado en variables de bloque

La documentación de Solidity nos proporciona una nutrida lista de variables de bloque que podemos usar en nuestros contratos y se suelen usar como fuente de entropía.

  • block.coinbase representa la dirección del minero que está minando el bloque actual.
  • block.difficulty medida que representa la dificultad para encontrar el bloque.
  • block.gaslimit número que representa la cantidad máxima de gas a ser consumida dentro del bloque.
  • block.number posición del bloque actual en la red.
  • block.timestamp cuándo el bloque fue minado.

Todas estas variables pueden ser manipuladas por mineros, por lo que la fuente de aleatoriedad que pensábamos lograr es riesgosa y poca efectiva.
Así mismo, las variables de bloque, obviamente, se comparten dentro del mismo bloque, por lo que si un atacante decide hacer un llamado interno desde su contrato al nuestro dentro del mismo bloque, podría fácilmente determinar qué números se generarían y hackear/ganar la partida.

Basado en hash del bloque

Cada bloque en la blockchain tiene un hash de verificación que podemos obtener ejecutando block.blockhash(_ param)

  • block.blockhash(block.number) hash del bloque actual
  • block.blockhash(block.number-1) hash del bloque anterior al actual

En este caso tendremos el mismo problema de manipulación y podemos predecir con un poco de análisis los outputs de dichas variables.

Algunos ejemplos aplicando estas estrategias:

Blockhash futuro

Existe un work-around cuyo contrato posee dos métodos obligatorios. El primer método es el encargado de guardar el número jugado por el usuario y el block.number de dicha transacción. El segundo método, transaccionado por el mismo jugador, es encargado de anunciar el número ganador. El contrato recupera el block.number asignado al address del jugador y obtiene su blockhash, que luego se usa para generar un número aleatorio.

Este wa tiene una consideración a tener en cuenta:

Leyendo el white paper de Ethereum podemos advertir que si no se realiza una segunda llamada dentro de los 256 bloques siguientes completos, el valor retornado al ejecutar block.blockhash(_number) será cero.

Esta debilidad ha sido famosa y explotada por un individuo que supo advertir la falla conceptual de que si su llamada al segundo método sucedía luego de los 256 bloques posteriores a su apuesta, el número ganador iba a ser cero. Este error le costó 400 ETH a la gente de SmartBillions lottery.
Una posible solución podría ser corroborar la antigüedad del block.number y bloquear la transacción, o no permitir un cero como posible valor de jugada.

Mejores prácticas

Oráculos

La utilización de oráculos en Ethereum requiere un apartado especial (y un post también) sobre su arquitectura y uso. En este caso vamos a hablar de dos herramientas específicas.

Oraclize es un servicio para aplicaciones distribuidas que proporciona un puente entre la blockchain y el exterior (Internet).

La utilización de un oráculo dentro de un contrato permite realizar requests a diferentes data sources. Uno de los usos más populares es la obtención de números aleatorios haciendo peticiones a API’s externas o al generador de números aleatorios incorporado en la misma herramienta.

Los más puristas podrán decir que esta solución es centralizada y desconfiar de la posible manipulación de los números generados por servicios de terceros. Un mejor uso de Oraclize es con una fuente de datos “aleatoria” que utiliza Ledger proofs y que puede verificarse on-chain: https://blog.oraclize.it/welcoming-our-brand-new-ledger-proof-649b9f098ccc

BTCRelay es un oráculo que establece un puente de comunicación entre la blockchain de Ethereum y la de Bitcoin. Los contratos inteligentes ejecutados en la EVM pueden solicitar bloques futuros de la red Bitcoin y usarlos como fuente de entropía.
Podemos ver un contrato activo usando esta estrategia en https://etherscan.io/address/0x302fE87B56330BE266599FAB2A54747299B5aC5B#code

Algoritmos

Día a día se van desarrollando nuevas soluciones algorítmicas al problema de generación de números aleatorios.
Signidice es un algoritmo basado en firmas criptográficas que se puede usar como un pseudo generador de números random que involucra solo a dos partes: el jugador y la casa.

También podemos encontrar soluciones más caseras que usan firmas digitales como fuente de aleatoriedad: https://github.com/sbsends/erng-solidity/blob/master/contracts/erng.sol

Conclusión

No tomemos a la ligera el uso de alguna de las implementaciones vistas ya que podemos cometer un error irreversible si de contratos con transacciones altas se tratare. Al diseñar un método de aleatoriedad, debemos asegurarnos primero de entender el incentivo de cada parte y solo entonces elegir un enfoque apropiado.

--

--