
Se você precisar implantar uma rede Ethereum privada baseada em Geth, isso poderá ser feito, por exemplo, criando nós de rede em servidores físicos ou máquinas virtuais. No entanto, é muito mais fácil usar contêineres Geth no repositório do hub do Docker. Nesse caso, você pode instalar uma rede inteira de nós, mesmo em uma máquina virtual ou em um servidor físico.
Neste artigo, mostraremos como instalar o Docker em um servidor executando o Debian 9 (virtual ou físico), como criar vários contêineres com o Geth em execução e combiná-los em uma rede privada. Daremos um exemplo de script para o Node.js que acessa os nós Geth que funcionam em contêineres.
Instalar o Docker
A instalação do Docker é descrita no site oficial. Para o Debian 9 e 10, você encontrará instruções detalhadas em
https://docs.docker.com/install/linux/docker-ce/debian/ .
Instalar o DockerPrimeiro de tudo, atualize os pacotes:
Em seguida, instale os pacotes necessários com o seguinte comando:
Adicione a chave oficial do GPG:
Certifique-se de obter a chave com a impressão digital 9DC8 5822 9FC7 DD38 854A E2D8 8D81 803C 0EBF CD88:
Adicione um repositório Docker estável:
Atualize pacotes e instale o Docker:
Agora resta apenas verificar se tudo foi feito corretamente. Para fazer isso, execute a imagem hello-world (Imagem):
Este comando fará o download da imagem desejada e a executará para execução. Como seria de esperar, você verá a mensagem "Olá do Docker!" No console.
Como você pode ver, nada complicado! Agora você pode começar a criar uma rede Geth privada com nós em execução em contêineres.
Crie usuário, diretórios e arquivos
Crie um livro no servidor do usuário e os seguintes subdiretórios em seu diretório pessoal:
/home/book/dock-test /home/book/dock-test/distr
Em seguida, no diretório / home / book / dock-test, crie um Dockerfile:
Listagem 1. Arquivo / home / livro / 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 arquivo será usado ao criar contêineres do Docker.
Você também precisará criar o arquivo /home/book/dock-test/distr/genesis.json, necessário para inicializar os nós da rede Ethereum:
Listagem 2. Arquivo /home/book/dock-test/distr/genesis.json { "config": { "chainId": 98760, "homesteadBlock": 0, "eip150Block": 0, "eip155Block": 0, "eip158Block": 0 }, "difficulty": "10", "gasLimit": "5100000", "alloc": {} }
Aqui, definimos o identificador de nossa rede privada como 98760. Para facilitar a mineração, especificamos 10 no parâmetro de dificuldade, o que nos permitirá trabalhar em máquinas virtuais com uma quantidade relativamente pequena de memória (por exemplo, 4 GB).
Crie uma rede e contêineres
Para que nossos nós troquem dados entre si, criaremos uma rede entre contêineres:
Em seguida, você precisa criar o diretório atual / home / book / dock-test, no qual o Dockerfile está localizado. Em seguida, crie o número necessário de contêineres, por exemplo, três:
A criação do primeiro contêiner leva um tempo relativamente longo, porque Você precisa baixar as imagens necessárias. Mas os seguintes contêineres são criados quase instantaneamente - compare com a velocidade de criação de máquinas virtuais, mesmo através da clonagem.
Agora abra as quatro janelas do console. Em três deles, trabalharemos com contêineres e, no quarto, receberemos os dados necessários sobre contêineres e rede.
Na primeira janela do console, emita o seguinte comando:
Você verá um prompt como:
eth_book@304bf4f09063:~$
Na segunda e terceira janelas do console, digite os seguintes comandos, respectivamente:
Criando nós e contas de rede privada Ethereum
No momento, temos três contêineres com Geth. Vamos criar em cada um deles o nó da nossa rede e conta privadas do Ethereum.
Digite o comando nas três primeiras janelas do console:
$ geth account new
Você precisará digitar uma senha. Para simplificar, use a mesma senha em cada nó para testes, mas anote-a em algum lugar. Se você esquecer a senha, ela não funcionará de forma alguma para se recuperar.
Depois de criar as contas no console, endereços de chave pública semelhantes a estes aparecerão:
Public address of the key: 0xc5Df10a76Bb559332c385F8cA789C0F37dD77A54 Public address of the key: 0x0C976006a5762779bA36AC590D1D8Ebac1Ca2981 Public address of the key: 0xaB627feab4e962222a3333F3b09182dF68bB9422
Salve os endereços (você terá outros), como precisamos deles para executar os nós.
Agora você precisa iniciar a inicialização dos nós. Isso é feito usando o comando:
$ geth --nousb init distr/genesis.json
Digite este comando em todas as janelas do console de nossos contêineres. A opção --nousb desativa todas as comunicações com dispositivos USB.
Executando Geth Nodes
Agora abra cada uma das janelas do console e execute Geth lá usando os comandos abaixo.
Primeira janela:
$ 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 janela:
$ 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
Terceira janela:
$ 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
Em cada uma das janelas abertas, serão exibidas mensagens sobre a geração do 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 …
Aguarde até a geração estar concluída. Depois disso, eth.hashrate e eth.blockNumber serão diferentes de 0:
> eth.hashrate 4 > eth.blockNumber 2
Você pode verificar o saldo da conta atual da seguinte maneira:
> web3.fromWei( eth.getBalance(eth.coinbase) )
Para que você não seja incomodado por mensagens sobre a criação de novos blocos e outras mensagens nas janelas do console, saia do Geth com o comando exit e reinicie novamente com o parâmetro de verbosidade definido como 1.
Nós de rede
Para começar, veremos a lista de contêineres em execução. Emita o seguinte comando na quarta janela do console:
Aqui, para cada contêiner, seu identificador, nome da imagem, nome do contêiner e outras informações são exibidos.
Para associar contêineres a uma rede, você precisará dos endereços IP dos contêineres em nossa rede privada PRIVATENET. Você pode obter esses endereços pelo ID do contêiner, por exemplo, da seguinte maneira:
Você também pode usar o comando PRIVATENET inspecionar rede do docker:
rede docker inspecionar 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 os dados recebidos por esses comandos, faremos uma lista de endereços IP para nossos contêineres:
node01 - 172.21.0.2 node02 - 172.21.0.3 node03 - 172.21.0.4
Obviamente, você terá uma lista diferente e, quando reiniciar os contêineres, esses endereços poderão mudar.
Após receber a lista de endereços, reinicie o geth em todos os contêineres, especificando o endereço do seu contêiner no parâmetro --rpcaddr. Obviamente, você pode definir o endereço 0.0.0.0, mas isso é ruim do ponto de vista da segurança - qualquer pessoa ou qualquer coisa pode se conectar ao nó. Por exemplo, podem ser bots que tentam "retirar" todos os fundos desses nós, aguardando o desbloqueio do nó.
Para mesclar os nós, você precisará usar o comando admin.addPeer. Como parâmetro para este comando, você precisa passar a URL no formato enode. Obtenha esse URL para cada contêiner usando o 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"
Os endereços recebidos são salvos.
Para conectar um nó ao outro, abra a janela do console do primeiro nó e digite o seguinte comando:
> admin.addPeer( "enode://0a84e562c9b22e43269b7dca215cf2ed8c20bbf35da67bae8d5ee81b36d8bbb69e3ec704b9b6f7501059fe861843a836b2fbab641f36616cdd77365b1a522d5b@172.21.0.3:30303")
Aqui passamos o endereço da URL, indicando nele o endereço IP do host conectado. Siga o mesmo procedimento em outros nós da rede.
Para verificar se a conexão foi estabelecida, use o comando admin.peers. Se não houver conexões, o comando retornará um resultado vazio:
> admin.peers []
No nosso caso, o comando mostra que o primeiro nó com o endereço 172.21.0.2 está conectado aos nós com os endereços 172.21.0.3 e 172.21.0.4:
Veja o resultado quando houver conexões > 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 } } }]
Observe que, depois que os nós forem combinados e sincronizados com êxito, o comando eth.blockNumber retornará o mesmo valor quando iniciado em qualquer um de nossos três contêineres.
O comando web3.fromWei (eth.getBalance (eth.coinbase)), pelo contrário, mostrará um equilíbrio diferente em nós diferentes, porque cada nó tem sua própria conta.
Trabalhar com hosts usando o Node.js
Na Listagem 3, apresentamos um script simples executando o Node.js que exibe no console uma lista de contas para o nó especificado e o saldo de cada uma delas:
Listagem 3. Arquivo / 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); });
Você pode executar este script em um separado, por exemplo, na quarta janela do console:
O que vem a seguir
O tópico do desenvolvimento de software para criptomoedas em geral e para o Ethereum em particular é bastante fascinante. Se, depois de ler este artigo, você tiver perguntas e quiser saber mais, leia meu livro
“Criando contratos inteligentes Solidity para a blockchain Ethereum. Guia Prático ” , publicado pela
Liters Publishing House.
Você também pode precisar de uma boa
folha de dicas do Docker .
Ficaria grato por quaisquer comentários e acréscimos ao artigo e ao livro!