bootnode解析及操作 - Agzs/geth-pbft-study GitHub Wiki
在server运行之前,需要进行配置,配置流程如下:
geth(ctx) >> makeFullNode(ctx) >> makeConfigNode(ctx) >> utils.SetNodeConfig(ctx, &cfg.Node) >> SetP2PConfig(ctx, &cfg.Node.P2P)
p2p的配置调用SetP2PConfig()实现,该函数中牵扯一些node discover的初始配置,如下
func SetP2PConfig(ctx *cli.Context, cfg *p2p.Config) {
setNodeKey(ctx, cfg)
setNAT(ctx, cfg)
setListenAddress(ctx, cfg) // setListenAddress creates a TCP listening address string from set command line flags.
setDiscoveryV5Address(ctx, cfg) // setDiscoveryV5Address creates a UDP listening address string from set command line flags for the V5 discovery protocol.
setBootstrapNodes(ctx, cfg) // setBootstrapNodes creates a list of bootstrap nodes from the command line flags, reverting to pre-configured ones if none have been specified.
setBootstrapNodesV5(ctx, cfg) // // setBootstrapNodesV5 creates a list of bootstrap nodes from the command line flags, reverting to pre-configured ones if none have been specified.
...
if ctx.GlobalIsSet(NoDiscoverFlag.Name) || ctx.GlobalBool(LightModeFlag.Name) {
cfg.NoDiscovery = true
}
...
if ctx.GlobalIsSet(DiscoveryV5Flag.Name) {
cfg.DiscoveryV5 = ctx.GlobalBool(DiscoveryV5Flag.Name)
} else if forceV5Discovery {
cfg.DiscoveryV5 = true
}
...
}
setBootstrapNodes()中含有BootstrapNodes的操作,其中,BootstrapNodes存在默认值,保存在go-ethereum/params/bootnodes.go的MainnetBootnodes变量中。如下:
func setBootstrapNodes(ctx *cli.Context, cfg *p2p.Config) {
urls := params.MainnetBootnodes
switch {
case ctx.GlobalIsSet(BootnodesFlag.Name) || ctx.GlobalIsSet(BootnodesV4Flag.Name):
if ctx.GlobalIsSet(BootnodesV4Flag.Name) {
urls = strings.Split(ctx.GlobalString(BootnodesV4Flag.Name), ",")
} else {
urls = strings.Split(ctx.GlobalString(BootnodesFlag.Name), ",")
}
case ctx.GlobalBool(TestnetFlag.Name):
urls = params.TestnetBootnodes
case ctx.GlobalBool(RinkebyFlag.Name):
urls = params.RinkebyBootnodes
}
cfg.BootstrapNodes = make([]*discover.Node, 0, len(urls))
for _, url := range urls {
node, err := discover.ParseNode(url)
if err != nil {
log.Error("Bootstrap URL invalid", "enode", url, "err", err)
continue
}
cfg.BootstrapNodes = append(cfg.BootstrapNodes, node)
}
}
后期在server.Start()函数中,使用newDialState(srv.StaticNodes, srv.BootstrapNodes, srv.ntab, dynPeers, srv.NetRestrict)初始化dialstate,作为其成员变量bootnodes,在dialstate的方法newTasks()中,有如下使用:
// If we don't have any peers whatsoever, try to dial a random bootnode. This
// scenario is useful for the testnet (and private networks) where the discovery
// table might be full of mostly bad peers, making it hard to find good ones.
if len(peers) == 0 && len(s.bootnodes) > 0 && needDynDials > 0 && now.Sub(s.start) > fallbackInterval {
bootnode := s.bootnodes[0]
s.bootnodes = append(s.bootnodes[:0], s.bootnodes[1:]...)
s.bootnodes = append(s.bootnodes, bootnode)
if addDial(dynDialedConn, bootnode) {
needDynDials--
}
}
其中addDial为newTasks()中的函数类型变量,具体函数如下如下:
func(flag connFlag, n *discover.Node) bool {
if err := s.checkDial(n, peers); err != nil {
log.Trace("Skipping dial candidate", "id", n.ID, "addr", &net.TCPAddr{IP: n.IP, Port: int(n.TCP)}, "err", err)
return false
}
s.dialing[n.ID] = flag
newtasks = append(newtasks, &dialTask{flags: flag, dest: n})
return true
}
该函数中,peers为newTasks()的参数,s为dialstate,newtasks为newTasks()的函数返回值。
===============================================================
配置之后,启动服务流程
geth() >> startNode() >> StartNode() >> node.Start() >> server.Start() >> server.run() >> for, scheduleTasks()
>> startTasks() >> go, t.Do(srv); >> dialTask.resolve(srv) >> srv.ntab.Resolve(t.dest.ID) >> Lookup(targetID)
>> lookup(targetID, true) >> findnode()
===============================================================
实际操作
在终端1中运行以下指令,本次测试采用普通版本(geth-client)进行测试:
ethtest@ethtest:~$ bootnode-client
Fatal: Use -nodekey or -nodekeyhex to specify a private key
*********************************************************************** 16:13:03
ethtest@ethtest:~$ bootnode-client -genkey bootnode.key
*********************************************************************** 16:13:23
ethtest@ethtest:~$ cat bootnode.key
5f033a38c38b68983c414b20f876128635791db93b5329e81cae4941754e5637
*********************************************************************** 16:13:29
执行完上述指令后,会在home目录下生成一个bootnode.key的文件,内容为5f03...e5637
随后使bootnode在线,运行如下指令:
ethtest@ethtest:~$ bootnode-client -nodekey bootnode.key
INFO [11-30|23:06:01] UDP listener up self=enode://aec154c6f9f2112eb6d8d0912a775f3c7c2865171d16c759e156fd60663a8de54791e94c64a9da330ee305a762baad4bfb3f75bba9da49d006b93c4a7154fcb3@[::]:30301
另外打开三个终端,分别运行geth-client ... --bootnodes ... 指令,三个终端的运行状态如下:
终端2
ethtest@ethtest:~$ geth-client --datadir pbft-nodes/node1/data --networkid 15 --bootnodes "enode://aec154c6f9f2112eb6d8d0912a775f3c7c2865171d16c759e156fd60663a8de54791e94c64a9da330ee305a762baad4bfb3f75bba9da49d006b93c4a7154fcb3@127.0.0.1:30301" console --port 2000
INFO [11-30|23:08:20] Starting peer-to-peer node instance=Geth/v1.6.7-stable/linux-amd64/go1.9.2
INFO [11-30|23:08:20] Allocated cache and file handles database=/home/zhiguo/pbft-nodes/node1/data/geth/chaindata cache=128 handles=1024
...
INFO [11-30|23:08:20] Starting P2P networking
INFO [11-30|23:08:22] UDP listener up self=enode://b94c919dec7dcc66ad5490bf2c6f63ba3d73cad330c756913da6dd2039202f0fd5c2d4c55a4890348b55af9907db800c526e804d0eca6513b0c2b69144d05bb5@[::]:2000
INFO [11-30|23:08:22] IPC endpoint opened: /home/zhiguo/pbft-nodes/node1/data/geth.ipc
INFO [11-30|23:08:22] RLPx listener up self=enode://b94c919dec7dcc66ad5490bf2c6f63ba3d73cad330c756913da6dd2039202f0fd5c2d4c55a4890348b55af9907db800c526e804d0eca6513b0c2b69144d05bb5@101.76.211.29:2000
Welcome to the Geth JavaScript console!
instance: Geth/v1.6.7-stable/linux-amd64/go1.9.2
coinbase: 0x24ff90ae9012f1b607a00aedd866403ef16f32ca
at block: 0 (Thu, 02 Nov 2017 18:29:23 CST)
datadir: /home/zhiguo/pbft-nodes/node1/data
modules: admin:1.0 debug:1.0 eth:1.0 miner:1.0 net:1.0 pbft:1.0 personal:1.0 rpc:1.0 txpool:1.0 web3:1.0
> INFO [11-30|23:09:38] pm.handle(peer)-----------add peer done-------------
INFO [11-30|23:10:33] pm.handle(peer)-----------add peer done-------------
终端3
geth-client --datadir pbft-nodes/node2/data --networkid 15 --bootnodes "enode://aec154c6f9f2112eb6d8d0912a775f3c7c2865171d16c759e156fd60663a8de54791e94c64a9da330ee305a762baad4bfb3f75bba9da49d006b93c4a7154fcb3@[127.0.0.1]:30301" console --port 2001
INFO [11-30|23:08:18] Starting peer-to-peer node instance=Geth/v1.6.7-stable/linux-amd64/go1.9.2
INFO [11-30|23:08:18] Allocated cache and file handles database=/home/zhiguo/pbft-nodes/node2/data/geth/chaindata cache=128 handles=1024
...
INFO [11-30|23:08:18] Starting P2P networking
INFO [11-30|23:08:21] UDP listener up self=enode://580e2b6914cb9657496efc817d95581cd2ba3bff1de5536346fc147a406649612848f0505aba99214c8e889d53c3e9974230d86dabfe8066e1049a8a0fc8eb6a@[::]:2001
INFO [11-30|23:08:21] IPC endpoint opened: /home/zhiguo/pbft-nodes/node2/data/geth.ipc
INFO [11-30|23:08:21] RLPx listener up self=enode://580e2b6914cb9657496efc817d95581cd2ba3bff1de5536346fc147a406649612848f0505aba99214c8e889d53c3e9974230d86dabfe8066e1049a8a0fc8eb6a@101.76.211.29:2001
Welcome to the Geth JavaScript console!
instance: Geth/v1.6.7-stable/linux-amd64/go1.9.2
coinbase: 0x079aafa464a201cf265850d852457812766bf549
at block: 0 (Thu, 02 Nov 2017 18:29:23 CST)
datadir: /home/zhiguo/pbft-nodes/node2/data
modules: admin:1.0 debug:1.0 eth:1.0 miner:1.0 net:1.0 pbft:1.0 personal:1.0 rpc:1.0 txpool:1.0 web3:1.0
> INFO [11-30|23:09:38] pm.handle(peer)-----------add peer done-------------
INFO [11-30|23:32:10] pm.handle(peer)-----------add peer done-------------
终端4
geth-client --datadir pbft-nodes/node3/data --networkid 15 --bootnodes "enode://aec154c6f9f2112eb6d8d0912a775f3c7c2865171d16c759e156fd60663a8de54791e94c64a9da330ee305a762baad4bfb3f75bba9da49d006b93c4a7154fcb3@[127.0.0.1]:30301" console --port 2002
INFO [11-30|23:08:16] Starting peer-to-peer node instance=Geth/v1.6.7-stable/linux-amd64/go1.9.2
INFO [11-30|23:08:16] Allocated cache and file handles database=/home/zhiguo/pbft-nodes/node3/data/geth/chaindata cache=128 handles=1024
...
INFO [11-30|23:08:16] Starting P2P networking
INFO [11-30|23:08:18] UDP listener up self=enode://1ba5928b324553b97449f2ac4c0d342ea125ab876003170dd02cbb5d71650a4e280e147f712d885ff73072ae4e7ae131d7580cee5f4a7fe7770d8c31df1d256f@[::]:2002
INFO [11-30|23:08:18] IPC endpoint opened: /home/zhiguo/pbft-nodes/node3/data/geth.ipc
INFO [11-30|23:08:18] RLPx listener up self=enode://1ba5928b324553b97449f2ac4c0d342ea125ab876003170dd02cbb5d71650a4e280e147f712d885ff73072ae4e7ae131d7580cee5f4a7fe7770d8c31df1d256f@101.76.211.29:2002
Welcome to the Geth JavaScript console!
instance: Geth/v1.6.7-stable/linux-amd64/go1.9.2
coinbase: 0xf99ea52d0b88870212bc3a3d9abdc6223ced643d
at block: 0 (Thu, 02 Nov 2017 18:29:23 CST)
datadir: /home/zhiguo/pbft-nodes/node3/data
modules: admin:1.0 debug:1.0 eth:1.0 miner:1.0 net:1.0 pbft:1.0 personal:1.0 rpc:1.0 txpool:1.0 web3:1.0
> INFO [11-30|23:10:33] pm.handle(peer)-----------add peer done-------------
INFO [11-30|23:32:10] pm.handle(peer)-----------add peer done-------------
从上述实验结果来看,在同一networkid,并且指定同一个bootnode(在线)的情形下,假设geth-client启动时的时间为0,经过76s后node1和node2建立连接,经过2m11s后node1和node3建立连接,经过23m52s后node2和node3建立连接,三者之间最终实现两两互联。