
如果您需要基于Geth部署专用的以太坊网络,则可以例如通过在物理服务器或虚拟机上创建网络节点来完成。 但是,使用Docker集线器存储库中的Geth容器要容易得多。 在这种情况下,您甚至可以在一台虚拟机或一台物理服务器上安装整个节点网络。
在本文中,我们将告诉您如何在运行Debian 9(虚拟或物理)的服务器上安装Docker,如何在运行Geth的情况下创建多个容器并将它们组合到专用网络中。 我们将为Node.js提供一个示例脚本,该脚本访问在容器中工作的Geth节点。
安装Docker
Docker安装在官方网站上有描述。 对于Debian 9和10,您可以在
https://docs.docker.com/install/linux/docker-ce/debian/中找到详细的说明。
安装Docker首先,更新软件包:
接下来,使用以下命令安装必要的软件包:
添加官方GPG密钥:
确保您获得的指纹为9DC8 5822 9FC7 DD38 854A E2D8 8D81 803C 0EBF CD88
添加一个稳定的Docker仓库:
更新软件包并安装Docker:
现在仅需验证所有操作是否正确。 为此,请运行(Image)hello-world图像:
此命令将下载所需的图像,并运行它以执行。 如您所料,您将在控制台上看到消息“ Hello from Docker!”。
如您所见,没有什么复杂的! 现在,您可以开始创建一个私有的Geth网络,其中的节点在容器中运行。
创建用户,目录和文件
在用户的服务器上以及其主目录中的以下子目录中创建一本书:
/home/book/dock-test /home/book/dock-test/distr
接下来,在/ home / book / dock-test目录中,创建一个Dockerfile:
清单1.文件/ 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
创建Docker容器时将使用此文件。
您还需要创建/home/book/dock-test/distr/genesis.json文件,这是初始化以太坊网络节点所必需的:
清单2.文件/home/book/dock-test/distr/genesis.json { "config": { "chainId": 98760, "homesteadBlock": 0, "eip150Block": 0, "eip155Block": 0, "eip158Block": 0 }, "difficulty": "10", "gasLimit": "5100000", "alloc": {} }
在这里,我们将专用网络的标识符设置为98760,为便于挖掘,我们在难度参数中指定了10,这将使我们能够在内存量相对较小(例如4 GB)的虚拟机上工作。
创建一个网络和容器
为了使我们的节点相互交换数据,我们将在容器之间创建一个网络:
接下来,您需要创建Dockerfile所在的当前目录/ home / book / dock-test。 然后创建所需数量的容器,例如三个:
创建第一个容器需要相对较长的时间,因为 您需要下载必要的图像。 但是,以下容器几乎是立即创建的-与创建虚拟机的速度相比,即使通过克隆也是如此。
现在打开四个控制台窗口。 在其中三个中,我们将使用容器,在第四个中,我们将接收有关容器和网络所需的数据。
在第一个控制台窗口中,发出以下命令:
您将看到如下提示:
eth_book@304bf4f09063:~$
在第二个和第三个控制台窗口中,分别输入以下命令:
创建以太坊专用网络节点和帐户
目前,我们有3个与Geth在一起的集装箱。 让我们在其中的每一个中创建我们以太坊专用网络和帐户的节点。
在前三个控制台窗口中输入命令:
$ geth account new
您将需要输入密码。 为简单起见,请在每个节点上使用相同的密码进行测试,但将其写下来。 如果您忘记了密码,那么它将无法以任何方式恢复。
在控制台上创建帐户后,将显示类似于以下内容的公钥地址:
Public address of the key: 0xc5Df10a76Bb559332c385F8cA789C0F37dD77A54 Public address of the key: 0x0C976006a5762779bA36AC590D1D8Ebac1Ca2981 Public address of the key: 0xaB627feab4e962222a3333F3b09182dF68bB9422
将地址(您将拥有其他地址)另存为 我们需要它们来运行节点。
现在,您需要开始节点的初始化。 使用以下命令完成此操作:
$ geth --nousb init distr/genesis.json
在我们容器的所有控制台窗口中输入此命令。 --nousb选项禁用与USB设备的所有通信。
运行Geth节点
现在依次打开每个控制台窗口,并使用以下命令在其中运行Geth。
第一个窗口:
$ 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
第二个窗口:
$ 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
第三个窗口:
$ 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
在每个打开的窗口中,将显示有关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 …
等到生成完成。 之后,eth.hashrate和eth.blockNumber将不同于0:
> eth.hashrate 4 > eth.blockNumber 2
您可以按以下方式检查当前帐户余额:
> web3.fromWei( eth.getBalance(eth.coinbase) )
为了避免被控制台窗口中有关创建新块的消息和其他消息所困扰,请使用exit命令退出Geth,然后将verbosity参数设置为1重新启动。
网络节点
首先,我们将查看正在运行的容器的列表。 从第四个控制台窗口发出以下命令:
在这里,对于每个容器,将显示其标识符,图像名称,容器名称和其他信息。
要将容器加入网络,您将需要我们专用PRIVATENET网络中容器的IP地址。 您可以通过容器ID获取这些地址,例如,如下所示:
您还可以使用docker network inspect PRIVATENET命令:
码头工人网络检查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": {} } ]
比较这些命令接收的数据,我们将列出容器的IP地址列表:
node01 - 172.21.0.2 node02 - 172.21.0.3 node03 - 172.21.0.4
当然,您将有一个不同的列表,并且当您重新启动容器时,这些地址可能会更改。
收到地址列表后,在所有容器中重新启动geth,并在--rpcaddr参数中指定容器的地址。 当然,您可以设置地址0.0.0.0,但是从安全角度来看,这很不好-任何人或任何事物都可以连接到该节点。 例如,可能是僵尸程序试图从这些节点“提取”所有资金,等待该节点被解锁。
要合并节点,您将需要使用admin.addPeer命令。 作为此命令的参数,您需要以enode格式传递URL。 使用admin.nodeInfo.enode命令为每个容器获取此URL:
> 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"
收到的地址保存。
为了将一个节点连接到另一个节点,请打开第一个节点的控制台窗口,然后在其中输入以下命令:
> admin.addPeer( "enode://0a84e562c9b22e43269b7dca215cf2ed8c20bbf35da67bae8d5ee81b36d8bbb69e3ec704b9b6f7501059fe861843a836b2fbab641f36616cdd77365b1a522d5b@172.21.0.3:30303")
在这里,我们传递了URL地址,其中指出了所连接主机的IP地址。 在其他网络节点上遵循相同的步骤。
要验证已建立连接,请使用admin.peers命令。 如果没有连接,该命令将返回空结果:
> admin.peers []
在我们的示例中,该命令显示地址为172.21.0.2的第一个节点已连接到地址为172.21.0.3和172.21.0.4的节点:
有连接时查看结果 > 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 } } }]
请注意,在成功合并并同步了节点之后,当在我们的三个容器中的任何一个中启动eth.blockNumber命令时,它将返回相同的值。
相反,web3.fromWei命令(eth.getBalance(eth.coinbase))将在不同节点上显示不同的平衡,因为 每个节点都有自己的帐户。
使用Node.js处理主机
在清单3中,我们提供了一个运行Node.js的简单脚本,该脚本在控制台上显示了指定节点的帐户列表以及每个帐户的余额:
清单3. File / 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); });
您可以在另一个单独的窗口中运行此脚本,例如,在第四个控制台窗口中:
接下来是什么
一般而言,加密货币,特别是以太坊的软件开发主题非常引人入胜。 如果在阅读本文后有疑问并想了解更多,请阅读我的书
“为以太坊区块链创建Solidity智能合约”。 实用指南” ,由
Liters出版社出版。
您可能还需要一个好的
Docker备忘单 。
对于本文和本书的任何评论和补充,我将不胜感激!