Crear una red privada Ethereum Geth en contenedores Docker



Si necesita implementar una red privada de Ethereum basada en Geth, esto puede hacerse, por ejemplo, creando nodos de red en servidores físicos o máquinas virtuales. Sin embargo, es mucho más fácil usar contenedores Geth desde el repositorio de Docker Hub. En este caso, puede instalar una red completa de nodos, incluso en una máquina virtual o en un servidor físico.

En este artículo le diremos cómo instalar Docker en un servidor que ejecuta Debian 9 (virtual o físico), cómo crear varios contenedores con Geth en ejecución y combinarlos en una red privada. Le daremos un script de ejemplo para Node.js que accede a los nodos Geth que funcionan en contenedores.

Instalar Docker


La instalación de Docker se describe en el sitio web oficial. Para Debian 9 y 10, encontrará instrucciones detalladas en https://docs.docker.com/install/linux/docker-ce/debian/ .

Instalar Docker
En primer lugar, actualice los paquetes:

# apt-get update 

Luego, instale los paquetes necesarios con el siguiente comando:

 # apt-get install apt-transport-https ca-certificates curl gnupg2 software-properties-common 

Agregue la clave GPG oficial:

 # curl -fsSL https://download.docker.com/linux/debian/gpg | sudo apt-key add - 

Asegúrese de obtener la clave con la huella digital 9DC8 5822 9FC7 DD38 854A E2D8 8D81 803C 0EBF CD88:

 # apt-key fingerprint 0EBFCD88 pub 4096R/0EBFCD88 2017-02-22 Key fingerprint = 9DC8 5822 9FC7 DD38 854A E2D8 8D81 803C 0EBF CD88 uid Docker Release (CE deb) docker@docker.com sub 4096R/F273FCD8 2017-02-22 

Agregue un repositorio estable de Docker:

 # add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/debian $(lsb_release -cs) stable" 

Actualice los paquetes e instale Docker:

 # apt-get update # apt-get install docker-ce docker-ce-cli containerd.io 

Ahora solo queda verificar que todo esté hecho correctamente. Para hacer esto, ejecute la imagen (Imagen) hello-world:

 # docker run hello-world 

Este comando descargará la imagen deseada y la ejecutará para su ejecución. Como era de esperar, verá el mensaje "¡Hola desde Docker!" En la consola.

Como puedes ver, ¡nada complicado! Ahora puede comenzar a crear una red privada Geth con nodos que se ejecutan en contenedores.

Crea usuarios, directorios y archivos


Cree un libro en el servidor del usuario y los siguientes subdirectorios en su directorio de inicio:

 /home/book/dock-test /home/book/dock-test/distr 

A continuación, en el directorio / home / book / dock-test, cree un Dockerfile:

Listado 1. File / home / book / dock-test / Dockerfile
 FROM ubuntu:16.04 LABEL version="1.0" LABEL maintainer="alexandre@frolov.pp.ru" ENV DEBIAN_FRONTEND=noninteractive RUN apt-get update && apt-get install --yes software-properties-common RUN add-apt-repository ppa:ethereum/ethereum RUN apt-get update && apt-get install --yes geth RUN adduser --disabled-login --gecos "" eth_book COPY distr /home/eth_book/distr RUN chown -R eth_book:eth_book /home/eth_book/distr USER eth_book WORKDIR /home/eth_book RUN geth --nousb init distr/genesis.json ENTRYPOINT bash 


Este archivo se usará al crear contenedores Docker.

También deberá crear el archivo /home/book/dock-test/distr/genesis.json, necesario para inicializar los nodos de red Ethereum:

Listado 2. Archivo /home/book/dock-test/distr/genesis.json
 { "config": { "chainId": 98760, "homesteadBlock": 0, "eip150Block": 0, "eip155Block": 0, "eip158Block": 0 }, "difficulty": "10", "gasLimit": "5100000", "alloc": {} } 


Aquí configuramos el identificador de nuestra red privada en 98760. Para facilitar la extracción, especificamos 10 en el parámetro de dificultad, lo que nos permitirá trabajar en máquinas virtuales con una cantidad relativamente pequeña de memoria (por ejemplo, 4 GB).

Crear una red y contenedores


Para que nuestros nodos intercambien datos entre sí, crearemos una red entre contenedores:

 # docker network create PRIVATENET 

A continuación, debe crear el directorio actual / home / book / dock-test, en el que se encuentra el Dockerfile. Luego cree el número requerido de contenedores, por ejemplo, tres:

 # docker build -t node01 . # docker build -t node02 . # docker build -t node03 . 

Crear el primer contenedor lleva un tiempo relativamente largo, porque Necesitas descargar las imágenes necesarias. Pero los siguientes contenedores se crean casi instantáneamente: compárelos con la velocidad de crear máquinas virtuales, incluso a través de la clonación.

Ahora abra las cuatro ventanas de la consola. En tres de ellos, trabajaremos con contenedores, y en el cuarto, recibiremos los datos que necesitamos sobre contenedores y la red.

En la primera ventana de consola, emita el siguiente comando:

 # docker run --rm -it -p 8545:8545 --net=PRIVATENET node01 

Verá un mensaje como:

 eth_book@304bf4f09063:~$ 

En las ventanas de la segunda y tercera consola, ingrese los siguientes comandos, respectivamente:

 # docker run --rm -it -p 8546:8546 --net=PRIVATENET node02 # docker run --rm -it -p 8547:8547 --net=PRIVATENET node03 

Creación de nodos y cuentas de red privada de Ethereum


Por el momento, tenemos tres contenedores con Geth. Creemos en cada uno de ellos el nodo de nuestra red y cuenta privada de Ethereum.

Ingrese el comando en las tres primeras ventanas de la consola:

 $ geth account new 

Necesitará ingresar una contraseña. Para simplificar, use la misma contraseña en cada nodo para las pruebas, pero escríbala en alguna parte. Si olvida la contraseña, no funcionará de ninguna manera para recuperarse.

Después de crear las cuentas en la consola, aparecerán direcciones de clave pública similares a estas:

 Public address of the key: 0xc5Df10a76Bb559332c385F8cA789C0F37dD77A54 Public address of the key: 0x0C976006a5762779bA36AC590D1D8Ebac1Ca2981 Public address of the key: 0xaB627feab4e962222a3333F3b09182dF68bB9422 

Guarde las direcciones (tendrá otras), como los necesitamos para ejecutar los nodos.

Ahora necesita comenzar la inicialización de los nodos. Esto se hace usando el comando:

 $ geth --nousb init distr/genesis.json 

Ingrese este comando en todas las ventanas de consola de nuestros contenedores. La opción --nousb deshabilita todas las comunicaciones con dispositivos USB.

Ejecución de nodos geth


Ahora abra cada una de las ventanas de la consola y ejecute Geth usando los comandos a continuación.

Primera ventana:

 $ geth --identity="Node01" --etherbase "0xc5Df10a76Bb559332c385F8cA789C0F37dD77A54" --mine --minerthreads 1 --verbosity 3 --networkid 98760 --rpc --rpcaddr 127.0.0.1 --nousb --rpcapi="db,eth,net,web3,personal,web3" console 

Segunda ventana:

 $ geth --identity="Node02" --etherbase "0x0C976006a5762779bA36AC590D1D8Ebac1Ca2981" --mine --minerthreads 1 --verbosity 3 --networkid 98760 --rpc --rpcaddr 127.0.0.1 --rpcport=8546 --nousb --rpcapi="db,eth,net,web3,personal,web3" console 

Tercera ventana:

 $ geth --identity="Node03" --etherbase "0xaB627feab4e962222a3333F3b09182dF68bB9422" --mine --minerthreads 1 --verbosity 3 --networkid 98760 --rpc --rpcaddr 127.0.0.1 --rpcport=8547 --nousb --rpcapi="db,eth,net,web3,personal,web3" console 

En cada una de las ventanas abiertas, aparecerán mensajes sobre la generación de DAG:

 … INFO [12-19|17:57:44.072] Generating DAG in progress epoch=0 percentage=34 elapsed=29.740s INFO [12-19|17:57:44.898] Generating DAG in progress epoch=0 percentage=35 elapsed=30.566s INFO [12-19|17:57:45.671] Generating DAG in progress epoch=0 percentage=36 elapsed=31.339s … 

Espere hasta que se complete la generación. Después de eso, eth.hashrate y eth.blockNumber serán diferentes de 0:

 > eth.hashrate 4 > eth.blockNumber 2 

Puede consultar el saldo de la cuenta corriente de la siguiente manera:

 > web3.fromWei( eth.getBalance(eth.coinbase) ) 

Para que no le molesten los mensajes sobre la creación de nuevos bloques y otros mensajes en las ventanas de la consola, salga de Geth con el comando de salida y reinicie nuevamente con el parámetro de verbosidad establecido en 1.

Nodos de red


Para comenzar, veremos la lista de contenedores en ejecución. Emita el siguiente comando desde la cuarta ventana de la consola:

 # docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES fa70a5418618 node03 "/bin/sh -c bash" 2 hours ago Up 2 hours 0.0.0.0:8547->8547/tcp gifted_curran 49a028744b4b node02 "/bin/sh -c bash" 2 hours ago Up 2 hours 0.0.0.0:8546->8546/tcp reverent_wescoff 5a9ade2947eb node01 "/bin/sh -c bash" 2 hours ago Up 2 hours 0.0.0.0:8545->8545/tcp clever_ellis 

Aquí, para cada contenedor, se muestran su identificador, nombre de imagen, nombre del contenedor y otra información.

Para unir contenedores en una red, necesitará las direcciones IP de los contenedores en nuestra red privada PRIVATENET. Puede obtener estas direcciones por ID de contenedor, por ejemplo, de la siguiente manera:

 # docker inspect 5a9ade2947eb | grep IPAddress "SecondaryIPAddresses": null, "IPAddress": "", "IPAddress": "172.21.0.2", 

También puede utilizar el comando de inspección de red de Docker PRIVATENET:

red de docker inspeccionar PRIVATENET
 [ { "Name": "PRIVATENET", "Id": "576ec7edba5b4c228740deaf7fabb5e2ba003d310086153dd7f15e2c7de0c1b2", "Created": "2019-12-20T11:52:07.90695857+03:00", "Scope": "local", "Driver": "bridge", "EnableIPv6": false, "IPAM": { "Driver": "default", "Options": {}, "Config": [ { "Subnet": "172.21.0.0/16", "Gateway": "172.21.0.1" } ] }, "Internal": false, "Attachable": false, "Ingress": false, "ConfigFrom": { "Network": "" }, "ConfigOnly": false, "Containers": { "49a028744b4b6073f6dbca23e78625bc58fc0cdacadec7cded4bb0e888c7e37b": { "Name": "reverent_wescoff", "EndpointID": "11006b596b5a46df9bf9f95a9456784795d333a3e6901b15bd2db746fd4b5513", "MacAddress": "02:42:ac:15:00:03", "IPv4Address": "172.21.0.3/16", "IPv6Address": "" }, "5a9ade2947ebd8e55594ede9763aac71f5e6529c03e762ef723adb2c592c5ccd": { "Name": "clever_ellis", "EndpointID": "41ef69a0a93b5b1de495836028bac1742c303de92ffe42a0855ed32c93c28953", "MacAddress": "02:42:ac:15:00:02", "IPv4Address": "172.21.0.2/16", "IPv6Address": "" }, "fa70a54186185de01db3647e7333bf6c71250162fafefb78dbe9998e5ac93f34": { "Name": "gifted_curran", "EndpointID": "d368c032bc0886c27ad4895d1856e4f00cf1b25ce040f3b42393dbff778c18e5", "MacAddress": "02:42:ac:15:00:04", "IPv4Address": "172.21.0.4/16", "IPv6Address": "" } }, "Options": {}, "Labels": {} } ] 

Comparando los datos recibidos por estos comandos, haremos una lista de direcciones IP para nuestros contenedores:

 node01 - 172.21.0.2 node02 - 172.21.0.3 node03 - 172.21.0.4 

Por supuesto, tendrá una lista diferente, y cuando reinicie los contenedores, estas direcciones pueden cambiar.

Después de recibir la lista de direcciones, reinicie geth en todos los contenedores, especificando la dirección de su contenedor en el parámetro --rpcaddr. Por supuesto, puede establecer la dirección 0.0.0.0, pero esto es malo desde el punto de vista de la seguridad: cualquiera o cualquier cosa puede conectarse al nodo. Por ejemplo, pueden ser bots que intentan "retirar" todos los fondos de estos nodos, esperando que el nodo se desbloquee.

Para fusionar los nodos, deberá usar el comando admin.addPeer. Como parámetro de este comando, debe pasar la URL en formato enode. Obtenga esta URL para cada contenedor utilizando el comando admin.nodeInfo.enode:

 > admin.nodeInfo.enode "enode://0a84e562c9b22e43269b7dca215cf2ed8c20bbf35da67bae8d5ee81b36d8bbb69e3ec704b9b6f7501059fe861843a836b2fbab641f36616cdd77365b1a522d5b@62.152.63.28:30303?discport=1350" "enode://ee49f69e25c068e006fec4a8d74370370b1d2be9715b86eddd99f97a3a5a9c692a265ab7d01fb36410d59c3f6e2b253a22f652ecbf1941eef0b3f1d30b19a535@62.152.63.28:30303?discport=1345" "enode://156d43648b47078439c7481e54f697bbf1c6b6e762029ba2969f1556ceb94e51ad03f8bd2bed35f466073165810600f52925d155f0fceef832ae86fc39a8c135@62.152.63.28:30303?discport=1348" 

Direcciones recibidas guardar.

Para conectar un nodo a otro, abra la ventana de consola del primer nodo e ingrese el siguiente comando allí:

 > admin.addPeer( "enode://0a84e562c9b22e43269b7dca215cf2ed8c20bbf35da67bae8d5ee81b36d8bbb69e3ec704b9b6f7501059fe861843a836b2fbab641f36616cdd77365b1a522d5b@172.21.0.3:30303") 

Aquí pasamos la dirección URL, indicando en ella la dirección IP del host conectado. Siga el mismo procedimiento en otros nodos de red.

Para verificar que la conexión esté establecida, use el comando admin.peers. Si no hay conexiones, el comando devolverá un resultado vacío:

 > admin.peers [] 

En nuestro caso, el comando muestra que el primer nodo con la dirección 172.21.0.2 está conectado a los nodos con las direcciones 172.21.0.3 y 172.21.0.4:

Ver el resultado cuando hay conexiones
 > admin.peers [{ caps: ["eth/63", "eth/64"], enode: "enode://156d43648b47078439c7481e54f697bbf1c6b6e762029ba2969f1556ceb94e51ad03f8bd2bed35f466073165810600f52925d155f0fceef832ae86fc39a8c135@172.21.0.4:30303", id: "4dac1d10cb6ae8bfc1fdebd3f5334b24ee62ec38a50bc92c89104cfc3251b5fc", name: "Geth/Node03/v1.9.9-stable-01744997/linux-amd64/go1.13.4", network: { inbound: false, localAddress: "172.21.0.2:40652", remoteAddress: "172.21.0.4:30303", static: true, trusted: false }, protocols: { eth: { difficulty: 98414119, head: "0x6b31a5bb9cde06fab5a8cc1ae9b18bada30de0d1b76cb3286c1081e76dbf5b83", version: 64 } } }, { caps: ["eth/63", "eth/64"], enode: "enode://ee49f69e25c068e006fec4a8d74370370b1d2be9715b86eddd99f97a3a5a9c692a265ab7d01fb36410d59c3f6e2b253a22f652ecbf1941eef0b3f1d30b19a535@172.21.0.3:30303", id: "b74277d278c15317fa7f7fa492daca60492ea22053bfc53281dd0071eba1c16b", name: "Geth/Node02/v1.9.9-stable-01744997/linux-amd64/go1.13.4", network: { inbound: false, localAddress: "172.21.0.2:42576", remoteAddress: "172.21.0.3:30303", static: true, trusted: false }, protocols: { eth: { difficulty: 99041423, head: "0x0ec44735bbb425cb8db96103f52300dfaae1147ba0e03aa4892d041250ce4408", version: 64 } } }] 

Tenga en cuenta que después de que los nodos se combinen y sincronicen con éxito, el comando eth.blockNumber devolverá el mismo valor cuando se inicie en cualquiera de nuestros tres contenedores.

El comando web3.fromWei (eth.getBalance (eth.coinbase)), por el contrario, mostrará un equilibrio diferente en diferentes nodos, porque cada nodo tiene su propia cuenta.

Trabajar con hosts usando Node.js


En el Listado 3, presentamos un script simple que ejecuta Node.js que muestra en la consola una lista de cuentas para el nodo especificado y el saldo de cada uno de ellos:

Listado 3. Archivo / home / book / list_accounts.js
 var Web3 = require('web3') var web3 = new Web3(new Web3.providers.HttpProvider("http://172.21.0.2:8545")); web3.eth.getAccounts() .then(accList => { return accList; }) .then(function (accounts) { var balancePromeses = []; for(let i = 0; i < accounts.length; i++) { balancePromeses[i] = web3.eth.getBalance(accounts[i]); } Promise.all(balancePromeses).then(values => { for(let i = 0; i < values.length; i++) { console.log('Account: ', accounts[i], 'balance: ', values[i], 'wei, ', web3.utils.fromWei(values[i], 'ether'), 'ether'); } }); }) .catch(function (error) { console.error(error); }); 


Puede ejecutar esta secuencia de comandos por separado, por ejemplo, en la cuarta ventana de la consola:

 # node list_accounts.js Account: 0x0C976006a5762779bA36AC590D1D8Ebac1Ca2981 balance: 3350000000000000000000 wei, 3350 ether 

Que sigue


El tema del desarrollo de software para las criptomonedas en general y para Ethereum en particular es bastante fascinante. Si después de leer este artículo tiene preguntas y desea saber más, lea mi libro "Creación de contratos inteligentes de Solidez para la cadena de bloques Ethereum. Guía práctica " , que fue publicado por Liters Publishing House.

También es posible que necesite una buena hoja de trucos de Docker .

¡Le agradecería cualquier comentario y adiciones al artículo y al libro!

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


All Articles