Peel the original look at code 02: Where to connect the other nodes after the original boot

Source: Internet
Author: User
Tags netsync

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() .

syncManagerThe 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.DialSeedsCorresponds 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.

newOutboundPeerWithConfigCorresponds 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.DialTimeoutCorresponds 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.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.