Cómo crear fácilmente una red privada de Ethereum

Desde hace un tiempo se han vuelto populares las granjas de minería para monedas virtuales para ganar dinero como pago por validar transacciones de una cadena de bloques.

Esto se realiza en redes públicas de tipo Blockchain para bitcoin, Ethereum o Monero. Pero como ya contamos en este post, las posibilidades de Blockchain van mucho más allá del intercambio de tokens.

Hoy vamos a centrarnos en las redes privadas que nos van a permitir aprovechar las posibilidades de blockchain dentro de nuestra organización. En este post explicaremos cómo crear una red privada de Ethereum, en la que posteriormente podremos crear smart contracts.

Instalación

Empezaremos por instalar el software necesario:

  • Ubuntu Server (también es posible instalar en otros sistemas operativos Mac o Windows).
  • Ethereum/Geth.

Los mineros están distribuidos por servidores, de forma que cada servidor tiene un software de minería. Todos los mineros deben de trabajar con mismo formato, denominado génesis.

Una vez tengamos un servidor con el sistema operativo actualizado, instalamos el cliente Geth escrito en Go, de Ethereum, con las siguientes instrucciones:

sudo apt-get install software-properties-common
sudo add-apt-repository -y ppa:ethereum/ethereum
sudo apt-get update
sudo apt-get install ethereum

Configurando la cadena

La configuración de las propiedades de nuestra red (o cadena) se hace a través de un fichero de propiedades que debe copiarse en todos los servidores donde vaya a arrancarse un nodo de la red.

La génesis es el primer bloque de la cadena, y en él se define cómo será nuestra cadena de bloques. Mediante este fichero de configuración se establece cómo van a ser nuestras recompensas, cómo de difícil será el minado, cómo se llama nuestra red, el mecanismo de consenso que utilizaremos en la red, etc.

En definitiva, es un conjunto de propiedades definidas en un fichero json que será el origen de nuestro primer bloque.

Definición de los parámetros del fichero genesis.json.

Estos son los parámetros que tenemos que configurar para crear un fichero genesis.json:

  • mixhash: hash de 256 bits que, combinado con el valor nonce, identifica que se ha llevado a cabo una cantidad suficiente de cálculos en este bloque.
  • nonce (number used once/número usado una sola vez): hash de 64 bits que, combinado con el valor mixhash, añade una seguridad extra a nuestra transacción evitando particularmente ataques de replay o de reinyección.
  • difficulty: valor que determina el nivel de dificultad aplicado durante el descubrimiento del bloque. Define el objetivo de minería, que se puede calcular a partir del nivel de dificultad del bloque anterior y la marca de tiempo. Cuanto mayor sea la dificultad, estadísticamente, más cálculos debe realizar un minero para descubrir un bloque válido.
  • Alloc: para gestionar la posesión y transferencia de los tokens o criptomonedas de Ethereum, es necesario contar con un wallet o monedero virtual. El valor alloc define una lista monederos precargados, que se usan para gestionar los tokens existentes en el momento inicial de la cadena. Como usaremos minería para extraer ether local rápidamente, no usaremos esta opción.
  • Coinbase: dirección de 160 bits a la que se han transferido todas las recompensas (en Ether) recopiladas de la extracción exitosa de un bloque.
  • Timestamp: valor del tiempo de minado del bloque. Este valor se usa para conseguir un equilibrio en términos del tiempo entre bloques. Un período pequeño entre dos bloques resulta en un aumento de la dificultad de cálculo. En cambio, un tiempo demasiado grande daría como resultado una reducción de la dificultad y el tiempo esperado para el siguiente bloque.
  • ParentHash: puntero hash al bloque padre. Este valor es el que posibilita la estructura de cadena enlazada de la Blockchain Ethereum. En el caso del bloque génesis este valor es 0.
  • ExtraData: campo para almacenar información extra correspondiente a la transacción. El tamaño de este campo es 32 bytes y es un campo opcional.
  • GasLimit: define el límite de gasto de ‘Ether’ al ejecutar transacciones.

Mecanismos de consenso: PoW y PoS

Las dos modalidades más usadas dentro de una red blockchain son Proof of Stake (PoS) y Proof of Work (PoW). Ambas tienen ventajas e inconvenientes, pero la gran diferencia es que en PoW los mineros compiten por la recompensa ofrecida por la red a quien encuentre la solución a un problema complejo que exige un gran esfuerzo de computación.

Este esfuerzo supone enormes costes en equipo de minería y electricidad, que no siempre se ven recompensados ya que todos los mineros realizan el cálculo y sólo uno gana la recompensa, lo que implica que el resto ha hecho un gasto eléctrico en vano.

Con el mecanismo PoS los mineros reservan en la red una cantidad mínima de monedas para poder participar en esta modalidad. No minan, por lo que no reciben una recompensa y no compiten entre ellos,sino que a cambio cobran una comisión por transacción aprobada.

Energéticamente es más eficiente al reducir el esfuerzo de cálculo. Quien más monedas aporte a la red (como fondo de garantía) y más tiempo permanezca en ese fondo, más probabilidad tendrá de recibir peticiones de validación de transacciones.

Si algún participante de PoS intentara injectar una transacción fraudulenta, este perdería sus fondos y serían repartidos por el restos de participantes.

Actualmente Ethereum solo trabaja en PoW, aunque existe una Ethereum Improvement Proposals (EIP-1011) para migrar de forma de trabajo actual PoW a PoS, el proyecto Casper.

Escribiendo el génesis

Una vez decididas las características de nuestra red, pasaremos a crear el fichero json de génesis. Para ello utilizaremos la herramienta ‘puppeth’ que habremos instalado previamente al instalar Ethereum. Este fichero deberá “ejecutarse” en todos los mineros que componen nuestra red.

Para arrancarlo ejecutaremos el siguiente comando:

puppeth
+-----------------------------------------------------------+
| Welcome to puppeth, your Ethereum private network manager |
|

A continuación mostramos el fichero genesis que hemos usado para nuestra red privada:

genesis.json

{
  "config": {
    	"chainId": 0,
    	"homesteadBlock": 0,
    	"eip155Block": 0,
    	"eip158Block": 0
	},
  "alloc"  	: {},
  "coinbase"   : "0x0000000000000000000000000000000000000000",
  "difficulty" : "0x20000",
  "extraData"  : "",
  "gasLimit"   : "0x2fefd8",
  "nonce"  	: "0x0000000000000042",
  "mixhash"	: "0x0000000000000000000000000000000000000000000000000000000000000000",
  "parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
  "timestamp"  : "0x00"
}

Creando un minero

Ejecutamos el comando geth con el fichero genesis.json para crear la estructura de directorios (geth, keystore) que componen el minero en la ruta /opt/minero:

Ejecutamos desde el terminal de Linux:

geth --datadir /opt/minero init genesis.json

Obtendremos la siguiente salida por consola, indicando el estado de creación del bloque inicial de la cadena.

INFO [05-03|07:37:50] Writing custom genesis block
INFO [05-03|07:37:50] Persisted trie from memory database  	nodes=0 size=0.00B 
time=13.822µs gcnodes=0 gcsize=0.00B gctime=0s livenodes=1 livesize=0.00B
INFO [05-03|07:37:50] Successfully wrote genesis state     	database=chaindata            	
hash=e68fe0…52eeca

Esta acción creará un ‘nodo’ de ethereum con las características definidas dentro del fichero genesis.json. Para tener una red, deberemos ejecutar este comando en varios nodos. Como resultado tendremos una serie de nodos que formarán parte de la misma red Ethereum.

Otro paso necesario para nuestro ejemplo es crear un monedero, ya que como minero debe poder almacenar la recompensa por los bloques minados. En las redes privadas de Ethereum, los mineros están constantemente validando bloques, tengan o no transacciones.

Al minar bloques, el minero que ha resuelto el bloque recibe una recompensa en ether y este lo almacena en el monedero.

Seguro que al leer “Recompensa + Ether” a más de uno le ha venido a la cabeza la gallina de los huevos de oro. Lamentablemente, no vamos a hacernos ricos con este Ether.

El Ether que obtendremos con nuestros mineros solamente es válido para uso interno en nuestra red, de forma que no podremos usarlo en otras redes, ya sean privadas (cualquier red privada) o públicas (como puede ser la MainNet de Ethereum, que es la red principal en la que se ejecutan contratos y en la cual se realiza la compra de Ether).

Como ya comentamos en este post cualquier transacción ejecutada en una red Ethereum implica un gasto de Gas.  Para poder ejecutar nuestros smart contracts necesitaremos tener un poco de Ether para pagar este Gas.

En las redes de prueba de Ethereum y en las redes privadas suele desplegarse un componente llamado Faucet, que no deja de ser una aplicación donde, proporcionando la dirección de nuestro contrato, podremos pedir un poco de Ether suficiente para realizar nuestros contratos. La idea sería desplegar un faucet para nuestra propia red privada.

Una vez que tengamos la red funcionando, lo siguiente que tendremos que hacer es crear un wallet o monedero. Para ello, ejecutaremos el siguiente comando:

‘geth --datadir . account new’ 

Se nos solicitará un password (que no deberemos perder bajo ningún concepto) y nos devolverá la dirección de nuestro wallet.

INFO [05-03|07:38:24] Maximum peer count                   	ETH=25 LES=0 total=25
Your new account is locked with a password. Please give a password. Do not forget this password.
Passphrase:
Repeat passphrase:
Address: {7575fa7b9c5435860f7b24ad784b68ab267bb6f3}

Este proceso lo repetimos tantas veces como mineros deseemos tener en nuestra red privada. En este momento hay dos puntos importantes a tener en cuenta que son:

  • La red a la que pertenecen viene indicado por el valor “chainId”, descrito en el génesis.json. Todos los nodos deben indicar este valor al arrancar.
  • Las redes privadas de Ethereum no son autodescubribles. Esto significa que debemos proporcionar el listado de nodos que van a formar parte de la red. De esta manera nuestra red está protegida contra posibles nodos fraudulentos que quieran unirse a la red.

En nuestro ejemplo vamos a tener tres nodos mineros. Una vez que tenemos tres nodos instalados en tres máquinas, con una IP diferente, vamos a configurar el acceso entre ellas para que puedan comunicarse y así tener una red Ethereum.

El listado de nodos que formarán parte de la red se especifica por medio del fichero static-nodes.json. Debe haber uno en cada nodo dentro del directorio en el que se ha generado la génesis de Ethereum.

Este fichero contiene los ‘enodes’, que son el identificador único de cada minero dentro de la red. Este valor lo podemos copiar de la consola con la siguiente ejecución:

geth --identity "miner1" --networkid 50 --datadir "/opt/test" --nodiscover --rpc --rpcport "8041" 
--port "30301"  --unlock 0 --password "/opt/miner/password.sec" --rpcapi 
"txpool,admin,db,eth,net,web3,miner,personal" --rpcaddr 0.0.0.0
--rpccorsdomain "*" --cache=4096 --syncmode "fast" --rpcvhosts "*"

En consola nos muestra el valor enode, que debemos copiar y pegar en el fichero static-nodes.json, sustituyendo el valor “[::]” por la IP correspondiente a la instancia de Ubuntu:

INFO [05-03|07:46:18] RLPx listener up self="enode://39f84c6f8ca535a7cecbe6892674a54
e9d2f523322504fccd7c6bddb73a1f2e6b7
f2a967fe14dad047ab8f18db42ce78c87ca8a5f7dd82abedf6ec677938304b@[::]:30302?discport=0"

A continuación mostramos un ejemplo para un grupo de tres mineros:

[	
"enode://c5644bfaf6d0bb8b5d04f93067028c41a8a22037a434e58fe88d3bfc3c700b3a5847c
4f68fcc89978ed38b08eed96dfc2ecb20ee14eb8ee8d5a3ca933c98e45d@[192.168.44.11]:30301",	
"enode://ef223de02252427137923b0b919924b320720a6c2bfd9bfbe1cbf3e826059f48c2ed6
28e2e97f9afac940a3bae1c1f420025a3b763b916bdf24777c37483a870@[192.168.44.12]:30301",	
"enode://57e3a5e0081b631171f765bbca4ad35b579ff37ce900d514e2e6bc1ff92e002882d5c6444b7
e92c1fb79e1ca16393088b35a62f165142229188ef0ae2a97a3e7@[192.168.44.13]:30301"
]

Si repetimos este paso en todos los nodos, conseguiremos que al iniciarlos se enlacen entre ellos y transmitiendo las transacciones en todas las bases de datos.

¿Y cómo sabemos que el fichero está generado correctamente y que los nodos están comunicándose? Para esto tenemos la consola del minero que ejecutaremos desde un terminal:

geth attach http://192.168.42.89:8041

Welcome to the Geth JavaScript console!

instance: Geth/miner1/v1.8.2-stable-b8b9f7f4/linux-amd64/go1.9.4
coinbase: 0x02186619ca90ec480006f4324ceb3214f8ffeb09
at block: 93242 (Thu, 03 May 2018 08:05:19 UTC)
 datadir: /opt/miner
 modules: admin:1.0 eth:1.0 miner:1.0 net:1.0 personal:1.0 rpc:1.0 txpool:1.0 web3:1.0

Al ejecutar admin.peers dentro de la consola obtenemos:

> admin.peers
[{
	caps: ["eth/62", "eth/63"],
	id: "57e3a5e0081b631171f765bbca4ad35b579ff37ce900d514e2e6bc1ff9
        2e002882d5c6444b7e92c1fb79e1ca16393088b35a62f165142229188ef0ae2a97a3e7",
	name: "Geth/miner2/v1.8.2-stable-b8b9f7f4/linux-amd64/go1.9.4",
	network: {
  	inbound: false,
  	localAddress: "192.168.44.11:54076",
  	remoteAddress: "192.168.44.12:30301",
  	static: true,
  	trusted: false
	},
	protocols: {
  	eth: {
    	difficulty: 399968839658,
    	head: "0xee8afe380b8b0c515e4c3fbe563e38ef45b22957f2b04d09e549
        c54b6a138601",
    	version: 63
  	}
	}
}, {
	caps: ["eth/62", "eth/63"],
	id: "ef223de02252427137923b0b919924b320720a6c2bfd9bfbe1cbf3e826059f48c2
        ed628e2e97f9afac940a3bae1c1f420025a3b763b916bdf24777c37483a870",
	name: "Geth/miner3/v1.8.2-stable-b8b9f7f4/linux-amd64/go1.9.4",
	network: {
  	inbound: false,
  	localAddress: "192.168.44.11:50822",
  	remoteAddress: "192.168.44.13:30301",
  	static: true,
  	trusted: false
	},
	protocols: {
  	eth: {
    	difficulty: 400011574956,
    	head: "0x81b620c7176e82ac6793f852c3ff10d4c2aeed2cbd5949ee551bbce3a375a2f5",
    	version: 63
  	}
	}
}]
>

Este mensaje solo se corresponde con los enlaces de un minero. Si esto se ejecutara en el resto de consolas de los mineros, comprobaremos si están bien enlazados. De no ser así, habría que revisar el formato del fichero static-nodes, los enode e IP de cada minero.

Una vez que hemos llegado hasta aquí, tenemos una red de mineros arrancados y conectados, pero aún no han comenzado a validar transacciones.

En este momento tendremos que acceder a la consola de cada minero para ejecutar la instrucción de minado. Para ello vamos a acceder a la consola del minero con la instrucción ‘geth attach IP’ en la que pondremos la dirección IP de cada minero.

geth attach http://192.168.42.89:8041

Welcome to the Geth JavaScript console!

instance: Geth/miner1/v1.8.2-stable-b8b9f7f4/linux-amd64/go1.9.4
coinbase: 0x02186619ca90ec480006f4324ceb3214f8ffeb09
at block: 93242 (Thu, 03 May 2018 08:05:19 UTC)
 datadir: /opt/miner
 modules: admin:1.0 eth:1.0 miner:1.0 net:1.0 personal:1.0 rpc:1.0 txpool:1.0 web3:1.0

Ejecutaremos miner.start(x) donde X será el número de hilos que se ejecutarán en el proceso de minado. Nosotros seleccionaremos el número de núcleos que tiene nuestra CPU.

> miner.start(4)

Para parar la instancia usaremos el comando miner.stop(). Es recomendable utilizar este comando siempre antes de parar la máquina donde la ejecutamos, ya que sino corremos el peligro de crear inconsistencias en la cadena y volverla corrupta.

Estando en este punto, tenemos los mineros ejecutándose y minando bloques. ¿Seguro? Lo mejor es que veamos cómo podemos comprobar que están minando.

Monitoreando nuestra red privada

Para monitorear nuestra red privada necesitamos tener una instancia dedicada con Ubuntu server e instalar en ella:

Y el software de los siguiente repositorios, que harán de backend y frontend:

Para realizar la instalación de de ambos repositorios seguimos los pasos indicados en cada uno de ellos, con las siguientes peculiaridades que veremos a continuación.

Hay que configurar tantos ficheros app.json como mineros tenemos. Es decir, el backend escucha las transacciones que ocurren en cada minero para exponerlos en el frontend.

Cada fichero tiene unas propiedades que comentamos a continuación:

  • El valor “name”: Texto que aparecerá en el front identificando las transacciones del minero configurado.
  • El valor “rpc_host”: IP del minero del que deseamos escuchar las tx.
  • El valor “rpc_port”: Puerto del minero por el que se accede a su consola.
  • El valor “listening_port”: Puerto para comunicarse entre nodos.

Los cuatro valores anteriores son los mismos valores que se encuentran en la ejecución del minero:

geth --identity "miner1" --networkid 50 --datadir "/opt/test" --nodiscover --rpc --rpcport "8041" 
--port "30301"  --unlock 0 --password "/opt/miner/password.sec" --rpcapi 
"txpool,admin,db,eth,net,web3,miner,personal" --rpcaddr 0.0.0.0 --rpccorsdomain "*" 
--cache=4096 --syncmode "fast" --rpcvhosts "*"

El valor “ws_server”: URL del servidor frontend que va a mostrar los valores (Tx) del minero, mientras que el valor “ws_secret” .- Pass da acceso al servidor frontend. A modo de ejemplo:

[
  {
	"name"          	: "MINER1",
	"script"        	: "app.js",
	"log_date_format"   : "YYYY-MM-DD HH:mm Z",
	"merge_logs"    	: false,
	"watch"         	: false,
	"max_restarts"  	: 10,
	"exec_interpreter"  : "node",
	"exec_mode"     	: "fork_mode",
	"env":
	{
  	"NODE_ENV"    	: "production",
  	"RPC_HOST"    	: "192.168.42.137",
  	"RPC_PORT"    	: "8041",
  	"LISTENING_PORT"  : "30301",
  	"INSTANCE_NAME"   : "NODO1",
  	"CONTACT_DETAILS" : "",
  	"WS_SERVER"   	: "http://192.168.42.93:3000",
  	"WS_SECRET"   	: "1234",
  	"VERBOSITY"   	: 3
	}
  }
]

Una vez configurados todos los ficheros con su correspondiente minero, arrancamos los backend:

pm2 start app1.json

pm2 start app2.json
pm2 start app3.json

Una vez arrancados los backend, arrancamos el frontend, estableciendo la variable WS_SECRET configurada en los ficheros backend.

WS_SECRET=1234

npm start

Si todo es correcto podremos ver en la URL http://localhost:3000 los valores de minado.- bloque actual, dificultad, gas estimado, etc.

Dependiendo de la rapidez de nuestro sistema, debemos esperar uno o dos minutos a la recolección de datos y que estos sean mostrados en la aplicación.

Es importante mencionar que la escala de las distintas gráficas se autoajusta en función de los valores a mostrar, por lo que para ver el valor concreto deberemos pasar el cursor por encima y ver los valores reales.

Con esto, ya tendremos una red blockchain basada en Ethereum en nuestra red local, con lo que podremos programar contratos inteligentes y desplegarlos. Esto lo veremos en el siguiente post.

Happy coding!

Referencias

Desde ya bien pequeñito empezó mi curiosidad de cómo Donkey Kong era capaz de arrojar barriles por las escaleras en aquella Nintendo Game & Watch de pantallas LCD. Hoy en Paradigma sigo manteniendo la misma curiosidad por la tecnología e ilusión que el día de Reyes del 82.

Ver toda la actividad de David Blanco