如何启动geth节点对大家来说已经不是什么难事,今天博主就带大家学习一下如何搭建两个节点的联盟链。

私有链的创建

在之前的文章中我们已经讲到过私有链的创建,本篇文章我们会有道私有链创建的知识,就重新温故一下。创建私有链首先需要指定创始块的配置,也就是genesis.json的配置。此文件就是一个内容格式为json的文本文件。

配置文件的内容格式基本如下:

{
 "alloc": {},
 "config": {
   "chainID": 72,
   "homesteadBlock": 0,
   "eip155Block": 0,
   "eip158Block": 0
 },
 "nonce": "0x0000000000000000",
 "difficulty": "0x4000",
 "mixhash": "0x0000000000000000000000000000000000000000000000000000000000000000",
 "coinbase": "0x0000000000000000000000000000000000000000",
 "timestamp": "0x00",
 "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
 "extraData": "0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa",
 "gasLimit": "0xffffffff"
}

配置项简介

我们对配置项的内容进行一下简单的介绍。

alloc: 用来预置账号以及账号的以太币数量,因为私有链挖矿比较容易,所以不需要预置有币的账号,需要的时候自己创建即可以。实例代码如下:

"alloc": {
    "de1e758511a7c67e7db93d1c23c1060a21db4615": {
      "balance": "1000"
    },
    "27dc8de9e9a1cb673543bd5fce89e83af09e228f": {
      "balance": "1100"
    },
    "d64a66c28a6ae5150af5e7c34696502793b91ae7": {
      "balance": "900"
    }

nonce:一个64位随机数,用于挖矿,和mixhash的设置需要满足以太坊的Yellow paper, 4.3.4.Block Header Validity (44)章节所描述的条件。

difficulty: 设置计算区块的难度,如果数值过大,挖矿时间较长,在测试环境为节省算力和等带时间可设置较小值。

mixhash:与nonce配合用于挖矿,由上一个区块的一部分生成的hash。和nonce的设置需要满足以太坊的Yellow paper, 4.3.4. Block Header Validity, (44)章节所描述的条件。

coinbase: 矿工账号,随便填写。

timestamp: 设置创世块的时间戳。

parentHash: 上一个区块的hash值,因为是创世块,所以这个值是0。

extraData: 附加信息,随便填,可以填你的个性信息,必须为十六进制的字符串。

gasLimit: 该值设置对GAS的消耗总量限制,用来限制区块能包含的交易信息总和,因为是私有链,所以填最大。

创世块初始化

本教程以mac操作系统为例,其他操作系统可对照执行。
执行以下命令来启动初始化创世块的命令,最简单的一组操作就是制定dir文件路径,和初始化文件目录。前面我们已经写好了genesis.json的配置文件,下面就执行一下初始化操作,涉及到操作参数为init。

ershixiongdeMacBook-Pro:geth zzs$ ./geth --datadir ./data-init1/ init genesis.json

本教程将初始化json文件放在geth同级目录下,如果放在其他目录下,指定具体的路径即可。同时创建了一个data-init1目录专门存储节点数据,执行完成会发现在该目录下多出两个目录,一个为geth一个为keystore。其中geth里面放数据相关信息,keystore里面放加密过的私钥文件。

执行时打印日志如下:

WARN [12-28|19:12:03] No etherbase set and no accounts found as default
INFO [12-28|19:12:03] Allocated cache and file handles         database=/Users/zzs/develop/eth/geth/data-init1/geth/chaindata cache=16 handles=16
INFO [12-28|19:12:03] Writing custom genesis block
INFO [12-28|19:12:03] Successfully wrote genesis state         database=chaindata                                             hash=942f59…a2588a
INFO [12-28|19:12:03] Allocated cache and file handles         database=/Users/zzs/develop/eth/geth/data-init1/geth/lightchaindata cache=16 handles=16
INFO [12-28|19:12:03] Writing custom genesis block
INFO [12-28|19:12:03] Successfully wrote genesis state         database=lightchaindata                                             hash=942f59…a2588a

经过以上命令,我们已经完成了私有连的初始化工作。因为我们要建立联盟链,因此需要再创建执行一遍同样的命令,json文件必须相同,datadir目录必须不同。博主使用data-init2目录来存储第二个节点的数据。

ershixiongdeMacBook-Pro:geth zzs$ ./geth --datadir ./data-init2/ init genesis.json

启动并进入控制台

根据具体的操作系统,开两个窗口来启动两个节点。这里有一点需要注意的是,虽然是两个节点,但他们的启动程序都是geth,只不过datadir目录不同而已。
在第一个窗口执行以下命令启动一个节点,注意启动之后不要关闭窗口。

./geth --datadir ./data-init1/ --networkid 88 --nodiscover console

参数简介:

networkid 指定网路ID,确保不适用1-4。

nodiscover 此参数确保geth不去寻找peers,主要是为了严格控制联盟链连入的节点。

这里我们需要注意的是在启动第一个节点时并没有指定port参数,因此此处采用了默认的port,也就是30303。
以下为执行时打印的日志,并进入控制台。通过日志我们也可以发现端口为30303。

WARN [12-28|19:23:16] No etherbase set and no accounts found as default
INFO [12-28|19:23:16] Starting peer-to-peer node               instance=Geth/v1.7.3-stable-4bb3c89d/darwin-amd64/go1.9.2
INFO [12-28|19:23:16] Allocated cache and file handles         database=/Users/zzs/develop/eth/geth/data-init2/geth/chaindata cache=128 handles=1024
WARN [12-28|19:23:16] Upgrading database to use lookup entries
INFO [12-28|19:23:16] Database deduplication successful        deduped=0
INFO [12-28|19:23:16] Initialised chain configuration          config="{ChainID: 72 Homestead: 0 DAO: <nil> DAOSupport: false EIP150: <nil> EIP155: 0 EIP158: 0 Byzantium: <nil> Engine: unknown}"
INFO [12-28|19:23:16] Disk storage enabled for ethash caches   dir=/Users/zzs/develop/eth/geth/data-init2/geth/ethash count=3
INFO [12-28|19:23:16] Disk storage enabled for ethash DAGs     dir=/Users/zzs/.ethash                                 count=2
INFO [12-28|19:23:16] Initialising Ethereum protocol           versions="[63 62]" network=88
INFO [12-28|19:23:16] Loaded most recent local header          number=0 hash=942f59…a2588a td=16384
INFO [12-28|19:23:16] Loaded most recent local full block      number=0 hash=942f59…a2588a td=16384
INFO [12-28|19:23:16] Loaded most recent local fast block      number=0 hash=942f59…a2588a td=16384
INFO [12-28|19:23:16] Regenerated local transaction journal    transactions=0 accounts=0
INFO [12-28|19:23:16] Starting P2P networking
INFO [12-28|19:23:16] RLPx listener up                         self="enode://aa621c010c685665ef217044dac4d57f4d1d682c682a5b3f92ca23b40982383240a05b680060ce8b0ce020a96c49c9c2c3628c4ea3281845211bd4cf4f03b35c@[::]:30303?discport=0"
INFO [12-28|19:23:16] IPC endpoint opened: /Users/zzs/develop/eth/geth/data-init2/geth.ipc
Welcome to the Geth JavaScript console!

instance: Geth/v1.7.3-stable-4bb3c89d/darwin-amd64/go1.9.2
 modules: admin:1.0 debug:1.0 eth:1.0 miner:1.0 net:1.0 personal:1.0 rpc:1.0 txpool:1.0 web3:1.0

> INFO [12-28|19:23:18] Mapped network port                      proto=tcp extport=30303 intport=30303 interface="UPNP IGDv1-IP1"

下面在另外一个窗口,换一个端口,比如30306,再换一下datadir,来启动第二个节点。

./geth --datadir ./data-init2/ --port 30306 --networkid 88 --nodiscover console

执行上面命令,完成节点2的启动,并进入控制台。

添加coinbase账户

上面的日志我们也看到警告信息,提示没有账户存在,那么现在我们就在第一个节点上创建一个账户,具体在控制台操作命令如下:

> personal.listAccounts
[]
> personal.newAccount("123456")
"0x60c8abe58c9dbc52a4ee9f8510f1799c432c0f3e"
>

上面的命令先是查看了节点下的地址,结果为空,然后创建了一个秘密为123456的账号。

同样的,在另外一个窗口我们执行同样的命令:

> personal.listAccounts
[]
> personal.newAccount("123456")
"0x02b7344004c45465796f779b7b95d7912c2ef572"
>

这样,两个节点就拥有了两个地址。同时,在它们的keystore目录下对应生成了加密的私钥文件。

我们也可以再次执行list命令查看添加账户之后的情况。同时可以执行以下命令查看coinbase账号:

> eth.coinbase
"0x02b7344004c45465796f779b7b95d7912c2ef572"
>

由于只有一个地址,因此该地址就作为coinbase地址。如果想查看更多的信息可以执行以下命令:

> personal.listWallets
[{
    accounts: [{
        address: "0x02b7344004c45465796f779b7b95d7912c2ef572",
        url: "keystore:///Users/zzs/develop/eth/geth/data-init2/keystore/UTC--2017-12-28T11-36-18.185974427Z--02b7344004c45465796f779b7b95d7912c2ef572"
    }],
    status: "Locked",
    url: "keystore:///Users/zzs/develop/eth/geth/data-init2/keystore/UTC--2017-12-28T11-36-18.185974427Z--02b7344004c45465796f779b7b95d7912c2ef572"
}]

这里不仅打印了账户信息,还打印出了私钥存储的位置和账户状态等信息。

联盟链互通

上面分别是在两个节点上进行的操作,下面我们需要把两个节点之间建立起链接。首先,我们执行以下命令查看以下节点的peers的情况。

> admin.peers
[]

发现节点并没有链接上任何其他节点,这也是我们的nodiscover参数发挥了效果。

下面就通过分享enode地址的方式来让两个节点建立链接。

> admin.nodeInfo.enode
"enode://aa621c010c685665ef217044dac4d57f4d1d682c682a5b3f92ca23b40982383240a05b680060ce8b0ce020a96c49c9c2c3628c4ea3281845211bd4cf4f03b35c@[::]:30306?discport=0"
>

通过上面命令,我们获得了节点2的encode信息。这是geth用来连接到不同节点的enode信息,在这些不同的节点它们能够分享交易和成功挖掘信息。

其实这个信息如果留心的话,在启动节点的打印日志中已经打印出每个节点的encode信息。比如:

INFO [12-28|19:23:16] RLPx listener up                         self="enode://aa621c010c685665ef217044dac4d57f4d1d682c682a5b3f92ca23b40982383240a05b680060ce8b0ce020a96c49c9c2c3628c4ea3281845211bd4cf4f03b35c@[::]:30303?discport=0"

现在,我们要告知一个节点,另外一个节点的encode信息。首先复制节点2的日志中self等号后面的信息,在节点1的控制台执行以下命令:

> admin.addPeer("enode://aa621c010c685665ef217044dac4d57f4d1d682c682a5b3f92ca23b40982383240a05b680060ce8b0ce020a96c49c9c2c3628c4ea3281845211bd4cf4f03b35c@[::]:30306?discport=0")
true

返回true,说明执行成功。再次验证一下:

> admin.peers
[{
    caps: ["eth/63"],
    id: "aa621c010c685665ef217044dac4d57f4d1d682c682a5b3f92ca23b40982383240a05b680060ce8b0ce020a96c49c9c2c3628c4ea3281845211bd4cf4f03b35c",
    name: "Geth/v1.7.3-stable-4bb3c89d/darwin-amd64/go1.9.2",
    network: {
      localAddress: "[::1]:49426",
      remoteAddress: "[::1]:30306"
    },
    protocols: {
      eth: {
        difficulty: 16384,
        head: "0x942f596f99dc8879b426b59080824662e1f97587353d087487fea0a0e2a2588a",
        version: 63
      }
    }
}]
>

发现节点1已经有一个peer了,同时我们可以看到remoteAddress: “[::1]:30306″,正是我们节点2的端口信息。在节点2执行admin.peers会发现有类似的信息,指向的peer正是节点1的。

查询余额并挖矿

执行查看余额命令:

> eth.getBalance(eth.coinbase)
0
>

发现两个节点的账号余额都为0。

在节点1执行miner.start()进行挖矿,执行miner.stop()停止挖矿。停止挖矿的时候开业忽略控制台输出,只要正确拼写命令回车即可。

当我们在节点1执行挖矿时,我们会发现节点2的控制台出现了这样的日志信息:

> INFO [12-28|20:05:32] Block synchronisation started
INFO [12-28|20:05:33] Imported new state entries               count=1 elapsed=47.661µs processed=1 pending=0 retry=0 duplicate=0 unexpected=0
WARN [12-28|20:05:33] Discarded bad propagated block           number=1 hash=ab49ba…1cf32f
INFO [12-28|20:05:33] Imported new block headers               count=2 elapsed=9.208ms  number=2 hash=738225…000e3b ignored=0
INFO [12-28|20:05:33] Imported new chain segment               blocks=2 txs=0 mgas=0.000 elapsed=1.724ms  mgasps=0.000 number=2 hash=738225…000e3b
INFO [12-28|20:05:33] Fast sync complete, auto disabling
INFO [12-28|20:05:34] Imported new chain segment               blocks=1 txs=0 mgas=0.000 elapsed=5.978ms  mgasps=0.000 number=3 hash=b069a9…426060
INFO [12-28|20:05:38] Imported new chain segment               blocks=1 txs=0 mgas=0.000 elapsed=6.930ms  mgasps=0.000 number=4 hash=21217e…526253
INFO [12-28|20:05:41] Imported new chain segment               blocks=1 txs=0 mgas=0.000 elapsed=6.419ms  mgasps=0.000 number=5 hash=3fa6ff…cf2794
INFO [12-28|20:05:43] Imported new chain segment               blocks=1 txs=0 mgas=0.000 elapsed=6.557ms  mgasps=0.000 number=6 hash=4c35b9…78b3ec
INFO [12-28|20:05:45] Imported new chain segment               blocks=1 txs=0 mgas=0.000 elapsed=6.514ms  mgasps=0.000 number=7 hash=328e62…1bd3d3
INFO [12-28|20:05:46] Imported new chain segment               blocks=1 txs=0 mgas=0.000 elapsed=6.513ms  mgasps=0.000 number=8 hash=12287e…0465b5
INFO [12-28|20:06:19] Imported new chain segment               blocks=1 txs=0 mgas=0.000 elapsed=7.048ms  mgasps=0.000 number=9 hash=8e844b…b99d6c
INFO [12-28|20:06:22] Imported new chain segment               blocks=1 txs=0 mgas=0.000 elapsed=8.156ms  mgasps=0.000 number=10 hash=159b36…d4dde5
INFO [12-28|20:06:24] Imported new chain segment               blocks=1 txs=0 mgas=0.000 elapsed=6.549ms  mgasps=0.000 number=11 hash=969100…5658a5

也就是说,节点1挖矿,节点2在同步数据信息。

停止节点1的挖矿,并查看coinbase地址金额:

> miner.stop()
true
> eth.getBalance(eth.coinbase)
140000000000000000000
> eth.coinbase
"0x60c8abe58c9dbc52a4ee9f8510f1799c432c0f3e"

这里我们知道了节点一种的地址信息和余额信息,那我们拿节点1的这个地址在节点2的控制台查询一下信息:

> eth.getBalance("0x60c8abe58c9dbc52a4ee9f8510f1799c432c0f3e")
140000000000000000000
>

很显然,节点2中也能查询到节点1中地址的余额。以上信息说明,节点1和节点2的数据是完全同步的。

交易转账

现在我们从节点1的coinbase账户发一笔交易到节点2的coinbase账户。

> personal.unlockAccount("0x60c8abe58c9dbc52a4ee9f8510f1799c432c0f3e")
Unlock account 0x60c8abe58c9dbc52a4ee9f8510f1799c432c0f3e
Passphrase:
true
> eth.sendTransaction({from: eth.coinbase, to: '0x02b7344004c45465796f779b7b95d7912c2ef572', value: 100000000})
INFO [12-28|20:13:21] Submitted transaction                    fullhash=0xc32e40f0f606a6368aa2c4c6a27db20f5a1d728fb9ef1f50a0410f4889e095a0 recipient=0x02b7344004c45465796F779B7b95d7912C2eF572
"0xc32e40f0f606a6368aa2c4c6a27db20f5a1d728fb9ef1f50a0410f4889e095a0"
>

解锁账户并发送1币交易信息。现在查看一下节点2的地址内是否有余额:

> eth.getBalance("0x02b7344004c45465796f779b7b95d7912c2ef572")
0
>

发现余额是0,为什么呢?因为虽然我们发起了交易,单并没有矿工挖矿打包交易。再次执行miner.start()。再次查询,就会发现节点2的coinbase地址已经有金额了。

> eth.getBalance("0x02b7344004c45465796f779b7b95d7912c2ef572")
100000000

通过以上的操作我们已经建立了一个拥有两个节点的联盟链,如果想建立更多节点的联盟链,可以此添加新的节点。

注意事项

需要注意的是,如果一个节点重启,则需要重新添加peer。另外,在实际生产中可去掉console命令,通过之前介绍的attach命令进入控制台。

更多资讯

获取更多相关资讯可关注微信公众号:程序新视界和知识星球(小密圈),为你解疑答惑。



以太坊如何搭建私有连联盟链插图

关注公众号:程序新视界,一个让你软实力、硬技术同步提升的平台

除非注明,否则均为程序新视界原创文章,转载必须以链接形式标明本文链接

本文链接:http://choupangxia.com/2019/07/06/%e4%bb%a5%e5%a4%aa%e5%9d%8a%e5%a6%82%e4%bd%95%e6%90%ad%e5%bb%ba%e7%a7%81%e6%9c%89%e8%bf%9e%e8%81%94%e7%9b%9f%e9%93%be/