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.goMainnetBootnodes变量中。如下:

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--
		}
	}

其中addDialnewTasks()中的函数类型变量,具体函数如下如下:

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
}

该函数中,peersnewTasks()的参数,sdialstatenewtasksnewTasks()的函数返回值。

===============================================================

配置之后,启动服务流程

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,经过76snode1node2建立连接,经过2m11snode1node3建立连接,经过23m52snode2node3建立连接,三者之间最终实现两两互联。

相关链接

Connecting to the network

Setting up private network or local cluster

Private network

bootnodes.go