Author: freewind
Compared to the original project warehouse:
GitHub Address: Https://github.com/Bytom/bytom
Gitee Address: Https://gitee.com/BytomBlockc ...
At first I had a problem with this question: Blockchain is a distributed network, then when a node starts, how does it know where to find other nodes to join the network?
After seeing the code, I realized that it was hard to encode some seed addresses in the code so that you could start by adding a seed address to the network. Although the entire network is distributed, it needs to be centralized at first.
Pre-coded content
For configuration files config.toml
, the configuration file contents are hardcoded in the original code:
Config/toml.go#l22-l45
var defaultConfigTmpl = `# This is a TOML config file.# For more information, see https://github.com/toml-lang/tomlfast_sync = truedb_backend = "leveldb"api_addr = "0.0.0.0:9888"`var mainNetConfigTmpl = `chain_id = "mainnet"[p2p]laddr = "tcp://0.0.0.0:46657"seeds = "45.79.213.28:46657,198.74.61.131:46657,212.111.41.245:46657,47.100.214.154:46657,47.100.109.199:46657,47.100.105.165:46657"`var testNetConfigTmpl = `chain_id = "testnet"[p2p]laddr = "tcp://0.0.0.0:46656"seeds = "47.96.42.1:46656,172.104.224.219:46656,45.118.132.164:46656"`var soloNetConfigTmpl = `chain_id = "solonet"[p2p]laddr = "tcp://0.0.0.0:46658"seeds = ""`
As can be seen, for different chain_id
, the preset seeds are different.
Of course, if we know the address of some nodes ourselves, we can also config.toml
manually modify the file to add it after the initialization is generated.
StartsyncManager
So, what about using these seed addresses and connecting them in the code? The point is that the connected code is SyncManager
in, so we're going to find syncManager
the place to start.
First, when we use bytomd node
startup, the following function will be called:
Cmd/bytomd/commands/run_node.go#l41
func runNode(cmd *cobra.Command, args []string) error { // Create & start node n := node.NewNode(config) if _, err := n.Start(); err != nil { // ... } // ...}
Called here n.Start
, where the Start
method is derived from the Node
embedded cmn.BaseService
:
Node/node.go#l39
type Node struct { cmn.BaseService // ...}
So the n.Start
corresponding method is the following:
Vendor/github.com/tendermint/tmlibs/common/service.go#l97
func (bs *BaseService) Start() (bool, error) { // ... err := bs.impl.OnStart() // ...}
Here, the bs.impl
call will continue because it corresponds to Node
Node.OnStart()
:
node/node.go#l169
func (n *Node) OnStart() error { // ... n.syncManager.Start() // ...}
As you can see, we finally get to the place where the call was made syncManager.Start()
.
syncManager
The processing in
Then there is syncManager
some processing in the interior.
It is mainly in addition to the seed node from the, also need to connect to the config.toml
previous and saved in the local AddressBook.json
node also take out the connection, so that even if the default seed node failed, it is still possible to connect to the network (partially resolved the previously mentioned centrality of concern).
syncManager.Start()
Corresponds to:
netsync/handle.go#l141
func (sm *SyncManager) Start() { go sm.netStart() // ...}
Where sm.netStart()
, corresponds to:
netsync/handle.go#l121
func (sm *SyncManager) netStart() error { // ... // If seeds exist, add them to the address book and dial out if sm.config.P2P.Seeds != "" { // dial out seeds := strings.Split(sm.config.P2P.Seeds, ",") if err := sm.DialSeeds(seeds); err != nil { return err } } // ...}
One of them sm.config.P2P.Seeds
corresponds to the config.toml
middle seeds
. about how the two correspond together, will be explained in the following article.
Then, by sm.DialSeeds(seeds)
connecting the seed, the code for this method is located at:
netsync/handle.go#l229
func (sm *SyncManager) DialSeeds(seeds []string) error { return sm.sw.DialSeeds(sm.addrBook, seeds)}
In fact, it is called sm.sw.DialSeeds
, and sm.sw
it refers to Switch
. At this point, we can see that there is a thing called to addrBook
participate in, it saved the node before the link successfully connected to the address, we do not have much discussion here.
Switch.DialSeeds
Corresponds to:
p2p/switch.go#l311
func (sw *Switch) DialSeeds(addrBook *AddrBook, seeds []string) error { // ... perm := rand.Perm(len(netAddrs)) for i := 0; i < len(perm)/2; i++ { j := perm[i] sw.dialSeed(netAddrs[j]) } // ...}
The random numbers are introduced here to disrupt the order in which the connections are initiated, so that each seed gets a fair chance to connect.
sw.dialSeed(netAddrs[j])
Corresponds to:
p2p/switch.go#l342
func (sw *Switch) dialSeed(addr *NetAddress) { peer, err := sw.DialPeerWithAddress(addr, false) // ...}
sw.DialPeerWithAddress(addr, false)
Also corresponds to:
p2p/switch.go#l351
func (sw *Switch) DialPeerWithAddress(addr *NetAddress, persistent bool) (*Peer, error) { // ... log.WithField("address", addr).Info("Dialing peer") peer, err := newOutboundPeerWithConfig(addr, sw.reactorsByCh, sw.chDescs, sw.StopPeerForError, sw.nodePrivKey, sw.peerConfig) // ...}
The persistent
argument, if it is true
, indicates that the peer is more important and, in some cases, attempts to reconnect if it is disconnected. If it persistent
is false
, there is no such treatment.
newOutboundPeerWithConfig
Corresponds to:
P2p/peer.go#l69
func newOutboundPeerWithConfig(addr *NetAddress, reactorsByCh map[byte]Reactor, chDescs []*ChannelDescriptor, onPeerError func(*Peer, interface{}), ourNodePrivKey crypto.PrivKeyEd25519, config *PeerConfig) (*Peer, error) { conn, err := dial(addr, config) // ...}
Continue dial
, add a timeout:
p2p/peer.go#l284
func dial(addr *NetAddress, config *PeerConfig) (net.Conn, error) { conn, err := addr.DialTimeout(config.DialTimeout * time.Second) if err != nil { return nil, err } return conn, nil}
addr.DialTimeout
Corresponds to:
p2p/netaddress.go#l141
func (na *NetAddress) DialTimeout(timeout time.Duration) (net.Conn, error) { conn, err := net.DialTimeout("tcp", na.String(), timeout) if err != nil { return nil, err } return conn, nil}
Finally to the net
call of the package, began to really go to connect this seed node, and here we can think of this problem solved.